Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
KDE:Unstable:Frameworks
libkscreen2
_service:obs_scm:libkscreen-VERSIONgit.20240506...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:libkscreen-VERSIONgit.20240506T035822~1c4fd3a.obscpio of Package libkscreen2
07070100000000000081A40000000000000000000000016638555E00000038000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/.git-blame-ignore-revs# clang-format 0ae03aa6e4b2afd6f1348ff16502a6257f3580e4 07070100000001000081A40000000000000000000000016638555E00000065000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/.gitignore.kdev4 *.kdev4 /build* .clang-format cmake-build-debug* .idea /compile_commands.json .clangd .cache 07070100000002000081A40000000000000000000000016638555E000001B4000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/.gitlab-ci.yml# SPDX-FileCopyrightText: None # SPDX-License-Identifier: CC0-1.0 include: - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml 07070100000003000081A40000000000000000000000016638555E000000E0000000000000000000000000000000000000003A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/.kde-ci.yml# SPDX-FileCopyrightText: None # SPDX-License-Identifier: CC0-1.0 Dependencies: - 'on': ['@all'] 'require': 'frameworks/extra-cmake-modules': '@latest' 'frameworks/kconfig': '@latest' 'plasma/kwayland': 'kf5' 07070100000004000081A40000000000000000000000016638555E00000023000000000000000000000000000000000000003700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/.mailmapSebastian Kügler <sebas@kde.org> 07070100000005000081A40000000000000000000000016638555E000010C4000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/CMakeLists.txtcmake_minimum_required(VERSION 3.16) project(libkscreen) set(PROJECT_VERSION "5.27.11") set(QT_MIN_VERSION "5.15.2") set(KF5_MIN_VERSION "5.102.0") set(KDE_COMPILERSETTINGS_LEVEL "5.82") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMSetupVersion) include(ECMMarkAsTest) include(ECMGenerateHeaders) include(ECMQtDeclareLoggingCategory) include(ECMAddQch) include(ECMPoQmTools) include(FeatureSummary) include(CMakePackageConfigHelpers) include(GenerateExportHeader) include(KDEClangFormat) include(KDEGitCommitHooks) include(ECMDeprecationSettings) find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} CONFIG REQUIRED Core DBus Gui Test WaylandClient) if (QT_MAJOR_VERSION EQUAL "5") find_package(Qt5 CONFIG REQUIRED X11Extras) find_package(QtWaylandScanner REQUIRED) endif() find_package(WaylandScanner) # Wayland backend find_package(KF5Wayland ${KF5_MIN_VERSION} CONFIG REQUIRED) add_feature_info("KF5Wayland" KF5Wayland_FOUND "Required for building libkscreen's KWayland backend") find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(PlasmaWaylandProtocols 1.10.0 CONFIG) set_package_properties(PlasmaWaylandProtocols PROPERTIES TYPE REQUIRED) find_package(Wayland 1.15 COMPONENTS Client) set_package_properties(Wayland PROPERTIES TYPE REQUIRED ) # xrandr backend find_package(XCB COMPONENTS XCB RANDR DPMS) set_package_properties(XCB PROPERTIES TYPE OPTIONAL PURPOSE "Required for building XRandR backends" ) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") find_program(JQ NAMES "jq" DOC "jq is used in zsh completion script") if(JQ) message(STATUS "Found jq executable: ${JQ}") else() message(WARNING "jq executable not found, zsh completions won't work at their full capacity") endif() ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX KSCREEN VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kscreen_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfigVersion.cmake" SOVERSION 8) ecm_set_disabled_deprecation_versions(QT 5.15.2 KF 5.101 ) add_subdirectory(src) add_subdirectory(backends) if(BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() ecm_install_po_files_as_qm(poqm) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Screen") # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT) if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5Screen_QCH FILE KF5ScreenQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5ScreenQchTargets.cmake\")") endif() configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5ScreenConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KF5ScreenTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5ScreenTargets.cmake NAMESPACE KF5:: COMPONENT Devel ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kscreen_version.h" DESTINATION "${KDE_INSTALL_INCLUDEDIR_KF}" COMPONENT Devel ) ecm_qt_install_logging_categories(EXPORT KSCREEN KSCREEN_DPMS KSCREEN_EDID FILE libkscreen.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) 07070100000006000081A40000000000000000000000016638555E00000F4E000000000000000000000000000000000000004000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/CMakePresets.json{ "version": 2, "configurePresets": [ { "name": "dev", "displayName": "Build as debug", "generator": "Ninja", "binaryDir": "${sourceDir}/build", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { "name": "dev-disable-deprecated", "displayName": "Build as without deprecated methods", "generator": "Ninja", "binaryDir": "${sourceDir}/build-disable-deprecated", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", "CMAKE_CXX_FLAGS_INIT": "-DQT_DISABLE_DEPRECATED_BEFORE=0x060000 -DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000" } }, { "name": "asan", "displayName": "Build with Asan support.", "generator": "Ninja", "binaryDir": "${sourceDir}/build-asan", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "ECM_ENABLE_SANITIZERS" : "'address;undefined'", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { "name": "unity", "displayName": "Build with CMake unity support.", "generator": "Ninja", "binaryDir": "${sourceDir}/build-unity", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "CMAKE_UNITY_BUILD": "ON", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { "name": "release", "displayName": "Build as release mode.", "generator": "Ninja", "binaryDir": "${sourceDir}/build-release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "profile", "displayName": "profile", "generator": "Ninja", "binaryDir": "${sourceDir}/build-profile", "cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { "name": "clazy", "displayName": "clazy", "generator": "Ninja", "binaryDir": "${sourceDir}/build-clazy", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }, "environment": { "CXX": "clazy", "CCACHE_DISABLE": "ON" } } ], "buildPresets": [ { "name": "dev", "configurePreset": "dev" }, { "name": "asan", "configurePreset": "asan" }, { "name": "dev-disable-deprecated", "configurePreset": "dev-disable-deprecated" }, { "name": "unity", "configurePreset": "unity" }, { "name": "clazy", "configurePreset": "clazy", "environment": { "CLAZY_CHECKS" : "level0,level1,detaching-member,ifndef-define-typo,isempty-vs-count,qrequiredresult-candidates,reserve-candidates,signal-with-return-value,unneeded-cast,function-args-by-ref,function-args-by-value,returning-void-expression,no-ctor-missing-parent-argument,isempty-vs-count,qhash-with-char-pointer-key,raw-environment-function,qproperty-type-mismatch,old-style-connect,qstring-allocations,container-inside-loop,heap-allocated-small-trivial-type,inefficient-qlist,qstring-varargs,level2,detaching-member,heap-allocated-small-trivial-type,isempty-vs-count,qstring-varargs,qvariant-template-instantiation,raw-environment-function,reserve-candidates,signal-with-return-value,thread-with-slots,no-ctor-missing-parent-argument,no-missing-typeinfo", "CCACHE_DISABLE" : "ON" } } ] } 07070100000007000081A40000000000000000000000016638555E0000083D000000000000000000000000000000000000003E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/CONTRIBUTING.md# Contributing to libkscreen - [Code of Conduct](#code-of-conduct) - [Submission Guideline](#submission-guideline) - [Commit Message Guideline](#commit-message-guideline) - [Contact](#contact) ## Code of Conduct The [KDE Community Code of Conduct][kde-coc] is applied. You can reach out to the [Commmunity Working Group][community-working-group] if you have questions about the Code of Conduct or if you want to get help on solving an issue with another contributor or maintainer. ## Issues Please reports issues and suggestions using [KDE's bugzilla][bugzilla] ## Submission Guideline The project follows the [Frameworks Coding Style][frameworks-style]. All non-trivial patches need to go through [code review][gitlab-reviews]. Commits are applied on top of master and cherry-picked to release branches if appropriate. Use `-x` when cherry-picking. Larger changes should be split up into smaller logical commits. libkscreen is released as part of Plasma. See the [Plasma schedule][plasma-schedule] for information on the release schedule. ## Commit Message Guideline Please follow the [KDE Commit Policy][commit-policy]. In particular please make sure to use appropriate [commit keywords][commit-policy-keywords]. ## Contact Real-time communication about the project happens on the IRC channel `#plasma` on irc.libera.chat and the bridged Matrix room `#plasma:kde.org`. Emails about the project can be sent to the [plasma-devel][plasma-devel] mailing list. [kde-coc]: https://kde.org/code-of-conduct [community-working-group]: https://ev.kde.org/workinggroups/cwg.php [frameworks-style]: https://community.kde.org/Policies/Frameworks_Coding_Style [gitlab-reviews]: https://invent.kde.org/plasma/kscreen [plasma-schedule]: https://community.kde.org/Schedules/Plasma_5 [commit-policy]: https://community.kde.org/Policies/Commit_Policy [bugzilla]: https://bugs.kde.org/describecomponents.cgi?product=KScreen [commit-policy-keywords]: https://community.kde.org/Policies/Commit_Policy#Special_keywords_in_GIT_and_SVN_log_messages [plasma-devel]: https://mail.kde.org/mailman/listinfo/plasma-devel 07070100000008000081A40000000000000000000000016638555E00000101000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/KF5ScreenConfig.cmake.in@PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(Qt@QT_MAJOR_VERSION@Core @QT_MIN_VERSION@) find_dependency(Qt@QT_MAJOR_VERSION@Gui @QT_MIN_VERSION@) include("${CMAKE_CURRENT_LIST_DIR}/KF5ScreenTargets.cmake") @PACKAGE_INCLUDE_QCHTARGETS@ 07070100000009000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES0707010000000A000081A40000000000000000000000016638555E00001B88000000000000000000000000000000000000004300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/CC0-1.0.txtCreative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. 0707010000000B000081A40000000000000000000000016638555E0000437D000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/GPL-2.0-only.txtGNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice 0707010000000C000081A40000000000000000000000016638555E0000437D000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/GPL-2.0-or-later.txtGNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice 0707010000000D000081A40000000000000000000000016638555E0000870F000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/GPL-3.0-only.txtGNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. 0707010000000E000081A40000000000000000000000016638555E0000652E000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/LGPL-2.1-or-later.txtGNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! 0707010000000F000081A40000000000000000000000016638555E00000296000000000000000000000000000000000000005700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/LICENSES/LicenseRef-KDE-Accepted-GPL.txtThis library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the license or (at your option) at any later version that is accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy as defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 07070100000010000081A40000000000000000000000016638555E00000302000000000000000000000000000000000000003800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/README.md# libkscreen libkscreen is the screen management library for KDE Plasma Workspaces. Its primary consumer is the KDE screen management application KScreen. libkscreen is part of [Plasma releases][plasma-releases]. ## End user Since this is a development library end users should instead look for support directly for the apps using this library. Please contact the support channels of your Linux distribution first. In case you find a bug in KScreen or if the bug is traced back to libkscreen, you can report it at the KDE [bug tracker][bug-tracker] (first look for duplicates). ## Contributing See the `CONTRIBUTING.md` file. [plasma-releases]: https://community.kde.org/Schedules/Plasma_5 [bug-tracker]: https://bugs.kde.org/describecomponents.cgi?product=KScreen 07070100000011000041ED0000000000000000000000036638555E00000000000000000000000000000000000000000000003800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests07070100000012000081A40000000000000000000000016638555E000006F1000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/CMakeLists.txtadd_definitions(-DTEST_DATA="${CMAKE_CURRENT_SOURCE_DIR}/configs/") include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/tests/kwayland/) macro(KSCREEN_ADD_TEST) foreach(_testname ${ARGN}) set(test_SRCS ${_testname}.cpp ${KSCREEN_WAYLAND_SRCS}) qt_add_dbus_interface(test_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.FakeBackend.xml fakebackendinterface) add_executable(${_testname} ${test_SRCS}) target_link_libraries(${_testname} Qt::Core Qt::Gui Qt::Test Qt::DBus KF5::Screen ${KSCREEN_WAYLAND_LIBS}) add_test(NAME kscreen-${_testname} COMMAND dbus-launch $<TARGET_FILE:${_testname}> ) ecm_mark_as_test(${_testname}) endforeach(_testname) endmacro(KSCREEN_ADD_TEST) kscreen_add_test(testscreenconfig) kscreen_add_test(testqscreenbackend) kscreen_add_test(testconfigserializer) kscreen_add_test(testconfigmonitor) kscreen_add_test(testinprocess) kscreen_add_test(testbackendloader) kscreen_add_test(testlog) kscreen_add_test(testmodelistchange) kscreen_add_test(testedid) if (NOT TARGET KF5::WaylandServer) message(WARNING "Skipping KF5::WaylandServer based unit tests!") return() endif() set(KSCREEN_WAYLAND_LIBS KF5::WaylandServer KF5::WaylandClient ) # For WaylandConfigReader and TestServer set(KSCREEN_WAYLAND_SRCS ${CMAKE_SOURCE_DIR}/tests/kwayland/waylandconfigreader.cpp ${CMAKE_SOURCE_DIR}/tests/kwayland/waylandtestserver.cpp ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../backends/kwayland) kscreen_add_test(testkwaylandbackend) kscreen_add_test(testkwaylandconfig) kscreen_add_test(testkwaylanddpms) set(KSCREEN_WAYLAND_LIBS "") set(KSCREEN_WAYLAND_SRCS "") if (ENABLE_XRANDR_TESTS) kscreen_add_test(testxrandr) endif() 07070100000013000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000004000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs07070100000014000081A40000000000000000000000016638555E00004F5E000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/default.json{ "outputs": [ { "clones": [ ], "connected": true, "currentModeId": "73", "enabled": false, "manufacturer": "Butterfly Visuals", "model": "smaragd", "icon": "", "id": 66, "modes": [ { "id": "73", "name": "1920x1080", "refreshRate": 60.06005859375, "size": { "height": 1080, "width": 1920 } }, { "id": "74", "name": "1400x1050", "refreshRate": 59.975616455078125, "size": { "height": 1050, "width": 1400 } }, { "id": "75", "name": "1280x1024", "refreshRate": 60.019741058349609, "size": { "height": 1024, "width": 1280 } }, { "id": "76", "name": "1280x960", "refreshRate": 60, "size": { "height": 960, "width": 1280 } }, { "id": "77", "name": "1024x768", "refreshRate": 60.003841400146484, "size": { "height": 768, "width": 1024 } }, { "id": "78", "name": "800x600", "refreshRate": 60.316539764404297, "size": { "height": 600, "width": 800 } }, { "id": "79", "name": "800x600", "refreshRate": 56.25, "size": { "height": 600, "width": 800 } }, { "id": "80", "name": "640x480", "refreshRate": 59.940475463867188, "size": { "height": 480, "width": 640 } } ], "name": "eDP1", "pos": { "x": 0, "y": 0 }, "preferredModes": [ "73" ], "primary": false, "rotation": 1, "sizeMM": { "height": 193, "width": 344 }, "type": 7 }, { "manufacturer": "Octopus Graphics", "model": "rubyled", "clones": [ ], "connected": true, "currentModeId": "869", "enabled": true, "icon": "", "id": 67, "modes": [ { "id": "75", "name": "1280x1024", "refreshRate": 60.019741058349609, "size": { "height": 1024, "width": 1280 } }, { "id": "76", "name": "1280x960", "refreshRate": 60, "size": { "height": 960, "width": 1280 } }, { "id": "77", "name": "1024x768", "refreshRate": 60.003841400146484, "size": { "height": 768, "width": 1024 } }, { "id": "78", "name": "800x600", "refreshRate": 60.316539764404297, "size": { "height": 600, "width": 800 } }, { "id": "79", "name": "800x600", "refreshRate": 56.25, "size": { "height": 600, "width": 800 } }, { "id": "854", "name": "1280x1024", "refreshRate": 75.024673461914062, "size": { "height": 1024, "width": 1280 } }, { "id": "855", "name": "1440x900", "refreshRate": 59.887443542480469, "size": { "height": 900, "width": 1440 } }, { "id": "856", "name": "1280x800", "refreshRate": 59.810325622558594, "size": { "height": 800, "width": 1280 } }, { "id": "857", "name": "1152x864", "refreshRate": 75, "size": { "height": 864, "width": 1152 } }, { "id": "859", "name": "1024x768", "refreshRate": 75.076217651367188, "size": { "height": 768, "width": 1024 } }, { "id": "860", "name": "1024x768", "refreshRate": 70.069358825683594, "size": { "height": 768, "width": 1024 } }, { "id": "861", "name": "832x624", "refreshRate": 74.55126953125, "size": { "height": 624, "width": 832 } }, { "id": "862", "name": "800x600", "refreshRate": 72.187568664550781, "size": { "height": 600, "width": 800 } }, { "id": "863", "name": "800x600", "refreshRate": 75, "size": { "height": 600, "width": 800 } }, { "id": "864", "name": "640x480", "refreshRate": 75, "size": { "height": 480, "width": 640 } }, { "id": "865", "name": "640x480", "refreshRate": 72.808799743652344, "size": { "height": 480, "width": 640 } }, { "id": "866", "name": "640x480", "refreshRate": 66.666664123535156, "size": { "height": 480, "width": 640 } }, { "id": "867", "name": "640x480", "refreshRate": 60, "size": { "height": 480, "width": 640 } }, { "id": "868", "name": "720x400", "refreshRate": 70.0816650390625, "size": { "height": 400, "width": 720 } }, { "id": "869", "name": "1680x1050", "refreshRate": 59.883251190185547, "size": { "height": 1050, "width": 1680 } }, { "id": "870", "name": "1440x900", "refreshRate": 74.984428405761719, "size": { "height": 900, "width": 1440 } }, { "id": "871", "name": "1280x800", "refreshRate": 74.93414306640625, "size": { "height": 800, "width": 1280 } } ], "name": "VGA1", "pos": { "x": 1920, "y": 0 }, "preferredModes": [ "869" ], "primary": false, "rotation": 1, "sizeMM": { "height": 296, "width": 474 }, "type": 0 }, { "manufacturer": "Gänseei", "model": "Dotterfleck", "clones": [ ], "connected": false, "currentModeId": "", "enabled": false, "icon": "", "id": 68, "modes": [ ], "name": "DP1", "pos": { "x": 0, "y": 0 }, "preferredModes": [ ], "primary": false, "rotation": 1, "sizeMM": { "height": 300, "width": 200 }, "type": 0 }, { "manufacturer": "Anarchy Electronics", "model": "Che", "clones": [ ], "connected": false, "currentModeId": "", "enabled": false, "icon": "", "id": 69, "modes": [ ], "name": "HDMI1", "pos": { "x": 0, "y": 0 }, "preferredModes": [ ], "primary": false, "rotation": 1, "sizeMM": { "height": 300, "width": 200 }, "type": 0 }, { "manufacturer": "Bonsai", "model": "marina", "clones": [ 71 ], "connected": true, "currentModeId": "850", "enabled": true, "icon": "", "id": 70, "modes": [ { "id": "75", "name": "1280x1024", "refreshRate": 60.019741058349609, "size": { "height": 1024, "width": 1280 } }, { "id": "76", "name": "1280x960", "refreshRate": 60, "size": { "height": 960, "width": 1280 } }, { "id": "77", "name": "1024x768", "refreshRate": 60.003841400146484, "size": { "height": 768, "width": 1024 } }, { "id": "78", "name": "800x600", "refreshRate": 60.316539764404297, "size": { "height": 600, "width": 800 } }, { "id": "79", "name": "800x600", "refreshRate": 56.25, "size": { "height": 600, "width": 800 } }, { "id": "850", "name": "1920x1080", "refreshRate": 60, "size": { "height": 1080, "width": 1920 } }, { "id": "851", "name": "1680x1050", "refreshRate": 59.954250335693359, "size": { "height": 1050, "width": 1680 } }, { "id": "852", "name": "1400x1050", "refreshRate": 59.978443145751953, "size": { "height": 1050, "width": 1400 } }, { "id": "853", "name": "1600x900", "refreshRate": 59.982475280761719, "size": { "height": 900, "width": 1600 } }, { "id": "854", "name": "1280x1024", "refreshRate": 75.024673461914062, "size": { "height": 1024, "width": 1280 } }, { "id": "855", "name": "1440x900", "refreshRate": 59.887443542480469, "size": { "height": 900, "width": 1440 } }, { "id": "856", "name": "1280x800", "refreshRate": 59.810325622558594, "size": { "height": 800, "width": 1280 } }, { "id": "857", "name": "1152x864", "refreshRate": 75, "size": { "height": 864, "width": 1152 } }, { "id": "858", "name": "1280x720", "refreshRate": 59.967262268066406, "size": { "height": 720, "width": 1280 } }, { "id": "859", "name": "1024x768", "refreshRate": 75.076217651367188, "size": { "height": 768, "width": 1024 } }, { "id": "860", "name": "1024x768", "refreshRate": 70.069358825683594, "size": { "height": 768, "width": 1024 } }, { "id": "861", "name": "832x624", "refreshRate": 74.55126953125, "size": { "height": 624, "width": 832 } }, { "id": "862", "name": "800x600", "refreshRate": 72.187568664550781, "size": { "height": 600, "width": 800 } }, { "id": "863", "name": "800x600", "refreshRate": 75, "size": { "height": 600, "width": 800 } }, { "id": "864", "name": "640x480", "refreshRate": 75, "size": { "height": 480, "width": 640 } }, { "id": "865", "name": "640x480", "refreshRate": 72.808799743652344, "size": { "height": 480, "width": 640 } }, { "id": "866", "name": "640x480", "refreshRate": 66.666664123535156, "size": { "height": 480, "width": 640 } }, { "id": "867", "name": "640x480", "refreshRate": 60, "size": { "height": 480, "width": 640 } }, { "id": "868", "name": "720x400", "refreshRate": 70.0816650390625, "size": { "height": 400, "width": 720 } } ], "name": "DP2", "pos": { "x": 0, "y": 0 }, "preferredModes": [ "850" ], "primary": true, "rotation": 1, "sizeMM": { "height": 287, "width": 510 }, "type": 0 }, { "manufacturer": "Tuna Pictures", "model": "52-1337LED", "clones": [ ], "connected": false, "currentModeId": "", "enabled": false, "icon": "", "id": 71, "modes": [ ], "name": "HDMI2", "pos": { "x": 0, "y": 0 }, "preferredModes": [ ], "primary": false, "rotation": 1, "sizeMM": { "height": 400, "width": 230 }, "type": 0, "edid" : "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==" } ], "screen": { "currentSize": { "height": 1080, "width": 3600 }, "id": 24, "maxActiveOutputsCount": 3, "maxSize": { "height": 8192, "width": 8192 }, "minSize": { "height": 200, "width": 320 } } } 07070100000015000081A40000000000000000000000016638555E00000ED6000000000000000000000000000000000000005300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/multipleclone.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 1024, "height" : 768 }, "maxActiveOutputsCount": 2 }, "outputs" : [ { "id" : 1, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 60, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 0, "y" : 0 }, "clones" : [2], "currentModeId" : 2, "preferredModes" : [2], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" }, { "id" : 2, "name" : "HDMI1", "type" : "HDMI", "modes" : [ { "id" : 4, "name" : "1920x1080", "refreshRate" : 60, "size" : { "width" : 1920, "height" : 1080 } }, { "id" : 3, "name" : "1600x1200", "refreshRate" : 60, "size" : { "width" : 1600, "height" : 1200 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 59.9, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 1280, "y" : 0 }, "currentModeId" : 2, "preferredModes" : [2], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : false, "edid" : "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==" } ] } 07070100000016000081A40000000000000000000000016638555E00000FA1000000000000000000000000000000000000005400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/multipleoutput.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 3200, "height" : 1880 }, "maxActiveOutputsCount": 2 }, "outputs" : [ { "id" : 1, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 60, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 0, "y" : 0 }, "size" : { "width" : 1280, "height" : 800 }, "currentModeId" : 3, "preferredModes" : [2], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" }, { "id" : 2, "name" : "HDMI1", "type" : "HDMI", "modes" : [ { "id" : 4, "name" : "1920x1080", "refreshRate" : 60, "size" : { "width" : 1920, "height" : 1080 } }, { "id" : 3, "name" : "1600x1200", "refreshRate" : 60, "size" : { "width" : 1600, "height" : 1200 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 59.9, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 1280, "y" : 0 }, "size" : { "width" : 1920, "height" : 1080 }, "scale" : 1.4, "currentModeId" : 4, "preferredModes" : [4], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : false, "edid" : "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==" } ] } 07070100000017000081A40000000000000000000000016638555E00000610000000000000000000000000000000000000006200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/singleOutputWithoutPreferred.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 1280, "height" : 800 }, "maxActiveOutputsCount": 2 }, "outputs" : [ { "id" : 1, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 60, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 0, "y" : 0 }, "currentModeId" : 3, "preferredModes" : [], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" } ] } 07070100000018000081A40000000000000000000000016638555E0000079A000000000000000000000000000000000000005200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/singleoutput.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 1280, "height" : 800 }, "maxActiveOutputsCount": 2 }, "outputs" : [ { "id" : 1, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 60, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 0, "y" : 0 }, "size" : { "width" : 1280, "height" : 800 }, "currentModeId" : 3, "preferredModes" : [3], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" } ] } 07070100000019000081A40000000000000000000000016638555E000006D0000000000000000000000000000000000000005800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/singleoutputBroken.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 1280, "height" : 800 }, "maxActiveOutputsCount": 2 }, "outputs" : [ { "id" : 2, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "128000x80000", "refreshRate" : 59.9, "size" : { "width" : 128000, "height" : 80000 } }, { "id" : 42, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } }, { "id" : 2, "name" : "1024x768", "refreshRate" : 59.9, "size" : { "width" : 1024, "height" : 768 } }, { "id" : 1, "name" : "800x600", "refreshRate" : 60, "size" : { "width" : 800, "height" : 600 } } ], "pos" : { "x" : 0, "y" : 0 }, "preferredModes" : [42], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" } ] } 0707010000001A000081A40000000000000000000000016638555E0000093F000000000000000000000000000000000000005400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/configs/tooManyOutputs.json{ "screen" : { "id" : 1, "maxSize" : { "width" : 8192, "height" : 8192 }, "minSize" : { "width" : 320, "height" : 200 }, "currentSize" : { "width" : 3200, "height" : 1880 }, "maxActiveOutputsCount": 1 }, "outputs" : [ { "id" : 1, "name" : "LVDS1", "type" : "LVDS", "modes" : [ { "id" : 3, "name" : "1280x800", "refreshRate" : 59.9, "size" : { "width" : 1280, "height" : 800 } } ], "pos" : { "x" : 0, "y" : 0 }, "currentModeId" : 3, "preferredModes" : [3], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : true, "edid" : "AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=" }, { "id" : 2, "name" : "HDMI1", "type" : "HDMI", "modes" : [ { "id" : 4, "name" : "1920x1080", "refreshRate" : 60, "size" : { "width" : 1920, "height" : 1080 } } ], "pos" : { "x" : 1280, "y" : 0 }, "currentModeId" : 4, "preferredModes" : [4], "rotation" : 1, "connected" : true, "enabled" : true, "primary" : false, "edid" : "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==" } ] } 0707010000001B000081A40000000000000000000000016638555E00000A5B000000000000000000000000000000000000004E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testbackendloader.cpp/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QObject> #include <QSignalSpy> #include <QtTest> #include "../src/backendmanager_p.h" Q_LOGGING_CATEGORY(KSCREEN, "kscreen") using namespace KScreen; class TestBackendLoader : public QObject { Q_OBJECT public: explicit TestBackendLoader(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testPreferredBackend(); void testEnv(); void testEnv_data(); void testFallback(); }; TestBackendLoader::TestBackendLoader(QObject *parent) : QObject(parent) { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND_INPROCESS", QByteArray()); qputenv("KSCREEN_BACKEND", QByteArray()); } void TestBackendLoader::initTestCase() { } void TestBackendLoader::cleanupTestCase() { // set to original value qputenv("KSCREEN_BACKEND", QByteArray()); } void TestBackendLoader::testPreferredBackend() { auto backends = BackendManager::instance()->listBackends(); QVERIFY(!backends.isEmpty()); auto preferred = BackendManager::instance()->preferredBackend(); QVERIFY(preferred.exists()); auto fake = BackendManager::instance()->preferredBackend(QStringLiteral("Fake")); QVERIFY(fake.fileName().startsWith(QLatin1String("KSC_Fake"))); } void TestBackendLoader::testEnv_data() { QTest::addColumn<QString>("var"); QTest::addColumn<QString>("backend"); // clang-format off QTest::newRow("all lower") << "kwayland" << "KSC_KWayland"; QTest::newRow("camel case") << "KWayland" << "KSC_KWayland"; QTest::newRow("all upper") << "KWAYLAND" << "KSC_KWayland"; QTest::newRow("mixed") << "kwAYlaND" << "KSC_KWayland"; QTest::newRow("xrandr 1.1") << "xrandr11" << "KSC_XRandR11"; QTest::newRow("qscreen") << "qscreen" << "KSC_QScreen"; QTest::newRow("mixed") << "fake" << "KSC_Fake"; // clang-format on } void TestBackendLoader::testEnv() { // We want to be pretty liberal, so this should work QFETCH(QString, var); QFETCH(QString, backend); qputenv("KSCREEN_BACKEND", var.toLocal8Bit()); auto preferred = BackendManager::instance()->preferredBackend(); QVERIFY(preferred.fileName().startsWith(backend)); } void TestBackendLoader::testFallback() { qputenv("KSCREEN_BACKEND", "nonsense"); auto preferred = BackendManager::instance()->preferredBackend(); QVERIFY(preferred.fileName().startsWith(QLatin1String("KSC_QScreen"))); } QTEST_GUILESS_MAIN(TestBackendLoader) #include "testbackendloader.moc" 0707010000001C000081A40000000000000000000000016638555E00000B11000000000000000000000000000000000000004E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testconfigmonitor.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include <QObject> #include <QSignalSpy> #include <QtTest> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/configmonitor.h" #include "../src/configoperation.h" #include "../src/getconfigoperation.h" #include "../src/output.h" #include "../src/setconfigoperation.h" #include <QSignalSpy> #include "fakebackendinterface.h" class TestConfigMonitor : public QObject { Q_OBJECT public: TestConfigMonitor() { } KScreen::ConfigPtr getConfig() { auto op = new KScreen::GetConfigOperation(); if (!op->exec()) { qWarning("Failed to retrieve backend: %s", qPrintable(op->errorString())); return KScreen::ConfigPtr(); } return op->config(); } private Q_SLOTS: void initTestCase() { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND", "Fake"); // This particular test is only useful for out of process operation, so enforce that qputenv("KSCREEN_BACKEND_INPROCESS", "0"); KScreen::BackendManager::instance()->shutdownBackend(); } void cleanupTestCase() { KScreen::BackendManager::instance()->shutdownBackend(); } void testChangeNotifyInProcess() { qputenv("KSCREEN_BACKEND_INPROCESS", "1"); KScreen::BackendManager::instance()->shutdownBackend(); KScreen::BackendManager::instance()->setMethod(KScreen::BackendManager::InProcess); // json file for the fake backend KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "singleoutput.json"}}); // Prepare monitor KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); QSignalSpy spy(monitor, SIGNAL(configurationChanged())); // Get config and monitor it for changes KScreen::ConfigPtr config = getConfig(); monitor->addConfig(config); QSignalSpy enabledSpy(config->outputs().first().data(), SIGNAL(isEnabledChanged())); auto output = config->outputs().first(); output->setEnabled(false); auto setop = new KScreen::SetConfigOperation(config); QVERIFY(!setop->hasError()); setop->exec(); QTRY_VERIFY(!spy.isEmpty()); QCOMPARE(spy.size(), 1); QCOMPARE(enabledSpy.size(), 1); QCOMPARE(config->output(1)->isEnabled(), false); output->setEnabled(false); auto setop2 = new KScreen::SetConfigOperation(config); QVERIFY(!setop2->hasError()); setop2->exec(); QTRY_VERIFY(!spy.isEmpty()); QCOMPARE(spy.size(), 2); } }; QTEST_MAIN(TestConfigMonitor) #include "testconfigmonitor.moc" 0707010000001D000081A40000000000000000000000016638555E00001C06000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testconfigserializer.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include <QObject> #include <QtTest> #include <cstdint> #include <utility> #include "../src/configserializer_p.h" #include "../src/mode.h" #include "../src/output.h" #include "../src/screen.h" #include "../src/types.h" class TestConfigSerializer : public QObject { Q_OBJECT public: TestConfigSerializer() { } private Q_SLOTS: void testSerializePoint() { const QPoint point(42, 24); const QJsonObject obj = KScreen::ConfigSerializer::serializePoint(point); QVERIFY(!obj.isEmpty()); QCOMPARE(obj[QLatin1String("x")].toInt(), point.x()); QCOMPARE(obj[QLatin1String("y")].toInt(), point.y()); } void testSerializeSize() { const QSize size(800, 600); const QJsonObject obj = KScreen::ConfigSerializer::serializeSize(size); QVERIFY(!obj.isEmpty()); QCOMPARE(obj[QLatin1String("width")].toInt(), size.width()); QCOMPARE(obj[QLatin1String("height")].toInt(), size.height()); } void testSerializeList() { QStringList stringList; stringList << QStringLiteral("Item 1") << QStringLiteral("Item 2") << QStringLiteral("Item 3") << QStringLiteral("Item 4"); QJsonArray arr = KScreen::ConfigSerializer::serializeList<QString>(stringList); QCOMPARE(arr.size(), stringList.size()); for (int i = 0; i < arr.size(); ++i) { QCOMPARE(arr.at(i).toString(), stringList.at(i)); } QList<int> intList; intList << 4 << 3 << 2 << 1; arr = KScreen::ConfigSerializer::serializeList<int>(intList); QCOMPARE(arr.size(), intList.size()); for (int i = 0; i < arr.size(); ++i) { QCOMPARE(arr.at(i).toInt(), intList[i]); } } void testSerializeScreen() { KScreen::ScreenPtr screen(new KScreen::Screen); screen->setId(12); screen->setMinSize(QSize(360, 360)); screen->setMaxSize(QSize(8192, 8192)); screen->setCurrentSize(QSize(3600, 1280)); screen->setMaxActiveOutputsCount(3); const QJsonObject obj = KScreen::ConfigSerializer::serializeScreen(screen); QVERIFY(!obj.isEmpty()); QCOMPARE(obj[QLatin1String("id")].toInt(), screen->id()); QCOMPARE(obj[QLatin1String("maxActiveOutputsCount")].toInt(), screen->maxActiveOutputsCount()); const QJsonObject minSize = obj[QLatin1String("minSize")].toObject(); QCOMPARE(minSize[QLatin1String("width")].toInt(), screen->minSize().width()); QCOMPARE(minSize[QLatin1String("height")].toInt(), screen->minSize().height()); const QJsonObject maxSize = obj[QLatin1String("maxSize")].toObject(); QCOMPARE(maxSize[QLatin1String("width")].toInt(), screen->maxSize().width()); QCOMPARE(maxSize[QLatin1String("height")].toInt(), screen->maxSize().height()); const QJsonObject currSize = obj[QLatin1String("currentSize")].toObject(); QCOMPARE(currSize[QLatin1String("width")].toInt(), screen->currentSize().width()); QCOMPARE(currSize[QLatin1String("height")].toInt(), screen->currentSize().height()); } void testSerializeMode() { KScreen::ModePtr mode(new KScreen::Mode); mode->setId(QStringLiteral("755")); mode->setName(QStringLiteral("1280x1024")); mode->setRefreshRate(50.666); mode->setSize(QSize(1280, 1024)); const QJsonObject obj = KScreen::ConfigSerializer::serializeMode(mode); QVERIFY(!obj.isEmpty()); QCOMPARE(obj[QLatin1String("id")].toString(), mode->id()); QCOMPARE(obj[QLatin1String("name")].toString(), mode->name()); QCOMPARE((float)obj[QLatin1String("refreshRate")].toDouble(), mode->refreshRate()); const QJsonObject size = obj[QLatin1String("size")].toObject(); QCOMPARE(size[QLatin1String("width")].toInt(), mode->size().width()); QCOMPARE(size[QLatin1String("height")].toInt(), mode->size().height()); } void testSerializeOutput() { KScreen::ModeList modes; KScreen::ModePtr mode(new KScreen::Mode); mode->setId(QStringLiteral("1")); mode->setName(QStringLiteral("800x600")); mode->setSize(QSize(800, 600)); mode->setRefreshRate(50.4); modes.insert(mode->id(), mode); KScreen::OutputPtr output(new KScreen::Output); output->setId(60); output->setName(QStringLiteral("LVDS-0")); output->setType(KScreen::Output::Panel); output->setIcon(QString()); output->setModes(modes); output->setPos(QPoint(1280, 0)); output->setSize(mode->size()); output->setRotation(KScreen::Output::None); output->setCurrentModeId(QStringLiteral("1")); output->setPreferredModes(QStringList() << QStringLiteral("1")); output->setConnected(true); output->setEnabled(true); output->setPriority(1); output->setClones(QList<int>() << 50 << 60); output->setSizeMm(QSize(310, 250)); const QJsonObject obj = KScreen::ConfigSerializer::serializeOutput(output); QVERIFY(!obj.isEmpty()); QCOMPARE(obj[QLatin1String("id")].toInt(), output->id()); QCOMPARE(obj[QLatin1String("name")].toString(), output->name()); QCOMPARE(static_cast<KScreen::Output::Type>(obj[QLatin1String("type")].toInt()), output->type()); QCOMPARE(obj[QLatin1String("icon")].toString(), output->icon()); const QJsonArray arr = obj[QLatin1String("modes")].toArray(); QCOMPARE(arr.size(), output->modes().count()); QJsonObject pos = obj[QLatin1String("pos")].toObject(); QCOMPARE(pos[QLatin1String("x")].toInt(), output->pos().x()); QCOMPARE(pos[QLatin1String("y")].toInt(), output->pos().y()); const QJsonObject size = obj[QLatin1String("size")].toObject(); QCOMPARE(size[QLatin1String("width")].toInt(), output->size().width()); QCOMPARE(size[QLatin1String("height")].toInt(), output->size().height()); QCOMPARE(static_cast<KScreen::Output::Rotation>(obj[QLatin1String("rotation")].toInt()), output->rotation()); QCOMPARE(obj[QLatin1String("currentModeId")].toString(), output->currentModeId()); QCOMPARE(obj[QLatin1String("connected")].toBool(), output->isConnected()); QCOMPARE(obj[QLatin1String("enabled")].toBool(), output->isEnabled()); QCOMPARE(obj[QLatin1String("priority")].toInt(), output->priority()); const QJsonArray clones = obj[QLatin1String("clones")].toArray(); QCOMPARE(clones.size(), output->clones().count()); for (int i = 0; i < clones.size(); ++i) { QCOMPARE(clones[i].toInt(), output->clones()[i]); } const QJsonObject sizeMm = obj[QLatin1String("sizeMM")].toObject(); QCOMPARE(sizeMm[QLatin1String("width")].toInt(), output->sizeMm().width()); QCOMPARE(sizeMm[QLatin1String("height")].toInt(), output->sizeMm().height()); } }; QTEST_MAIN(TestConfigSerializer) #include "testconfigserializer.moc" 0707010000001E000081A40000000000000000000000016638555E000019D3000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testedid.cpp/* * SPDX-FileCopyrightText: 2018 Frederik Gladhorn <gladhorn@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QObject> #include <QtTest> #include "../src/edid.h" using namespace KScreen; class TestEdid : public QObject { Q_OBJECT private Q_SLOTS: void testInvalid(); void testEdidParser_data(); void testEdidParser(); }; void TestEdid::testInvalid() { QScopedPointer<Edid> e(new Edid()); QCOMPARE(e->isValid(), false); QCOMPARE(e->name(), QString()); QScopedPointer<Edid> e2(e->clone()); QCOMPARE(e2->isValid(), false); QCOMPARE(e2->name(), QString()); QScopedPointer<Edid> e3(new Edid("some random data")); QCOMPARE(e3->isValid(), false); } void TestEdid::testEdidParser_data() { // The raw edid data QTest::addColumn<QByteArray>("raw_edid"); QTest::addColumn<QString>("deviceId"); QTest::addColumn<QString>("name"); QTest::addColumn<QString>("pnpId"); // List of potential vendor names, this depends on the availablility // of pnp.ids, otherwise it will be a three letter abbreviation. QTest::addColumn<QStringList>("vendor"); QTest::addColumn<QString>("serial"); QTest::addColumn<QString>("eisaId"); QTest::addColumn<QString>("hash"); QTest::addColumn<uint>("width"); QTest::addColumn<uint>("height"); QTest::addColumn<qreal>("gamma"); QTest::addColumn<QQuaternion>("red"); QTest::addColumn<QQuaternion>("green"); QTest::addColumn<QQuaternion>("blue"); QTest::addColumn<QQuaternion>("white"); // clang-format off QTest::addRow("cor") << QByteArray::fromBase64("AP///////wAN8iw0AAAAABwVAQOAHRB4CoPVlFdSjCccUFQAAAABAQEBAQEBAQEBAQEBAQEBEhtWWlAAGTAwIDYAJaQQAAAYEhtWWlAAGTAwIDYAJaQQAAAYAAAA/gBBVU8KICAgICAgICAgAAAA/gBCMTMzWFcwMyBWNCAKAIc=") << QStringLiteral("xrandr-Corollary Inc") << QStringLiteral("") << QStringLiteral("COR") << QStringList({QStringLiteral("COR"), QStringLiteral("Corollary Inc")}) << QStringLiteral("") << QStringLiteral("B133XW03 V4") << QStringLiteral("82266089b3f9da3a8c48de1ec81b09e1") << 29U << 16U << 2.2 << QQuaternion(1, QVector3D(0.580078, 0.339844, 0)) << QQuaternion(1, QVector3D(0.320313, 0.549805, 0)) << QQuaternion(1, QVector3D(0.155273, 0.110352, 0)) << QQuaternion(1, QVector3D(0.313477, 0.329102, 0)); QTest::addRow("dell") << QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg==") << QStringLiteral("xrandr-Dell Inc.-DELL U2410-F525M245AKLL") << QStringLiteral("DELL U2410") << QStringLiteral("DEL") << QStringList({QStringLiteral("DEL"), QStringLiteral("Dell Inc.")}) << QStringLiteral("F525M245AKLL") << QStringLiteral("") << QStringLiteral("be55eeb5fcc1e775f321c1ae3aa02ef0") << 52U << 32U << 2.2 << QQuaternion(1, QVector3D(0.679688, 0.308594, 0)) << QQuaternion(1, QVector3D(0.206055, 0.693359, 0)) << QQuaternion(1, QVector3D(0.151367, 0.0546875, 0)) << QQuaternion(1, QVector3D(0.313477, 0.329102, 0)); QTest::addRow("samsung") << QByteArray::fromBase64("AP///////wBMLcMFMzJGRQkUAQMOMx14Ku6Ro1RMmSYPUFQjCACBAIFAgYCVAKlAswABAQEBAjqAGHE4LUBYLEUA/h8RAAAeAAAA/QA4PB5REQAKICAgICAgAAAA/ABTeW5jTWFzdGVyCiAgAAAA/wBIOU1aMzAyMTk2CiAgAC4=") << QStringLiteral("xrandr-Samsung Electric Company-SyncMaster-H9MZ302196") << QStringLiteral("SyncMaster") << QStringLiteral("SAM") << QStringList({QStringLiteral("SAM"), QStringLiteral("Samsung Electric Company")}) << QStringLiteral("H9MZ302196") << QStringLiteral("") << QStringLiteral("9384061b2b87ad193f841e07d60e9e1a") << 51U << 29U << 2.2 << QQuaternion(1, QVector3D(0.639648, 0.328125, 0)) << QQuaternion(1, QVector3D(0.299805, 0.599609, 0)) << QQuaternion(1, QVector3D(0.150391, 0.0595703, 0)) << QQuaternion(1, QVector3D(0.3125, 0.329102, 0)); QTest::newRow("sharp") << QByteArray::fromBase64("AP///////wBNEEoUAAAAAB4ZAQSlHRF4Dt5Qo1RMmSYPUFQAAAABAQEBAQEBAQEBAQEBAQEBzZGAoMAINHAwIDUAJqUQAAAYpHSAoMAINHAwIDUAJqUQAAAYAAAA/gBSWE40OYFMUTEzM1oxAAAAAAACQQMoABIAAAsBCiAgAMw=") << QStringLiteral("xrandr-Sharp Corporation") << QString() // unsure why, this screen reports no name << QStringLiteral("SHP") << QStringList({QStringLiteral("SHP"), QStringLiteral("Sharp Corporation")}) << QStringLiteral("") << QStringLiteral("RXN49-LQ133Z1") << QStringLiteral("3627c3534e4c82871967b57237bf5b83") << 29U << 17U << 2.2 << QQuaternion(1, QVector3D(0.639648, 0.328125, 0)) << QQuaternion(1, QVector3D(0.299805, 0.599609, 0)) << QQuaternion(1, QVector3D(0.149414, 0.0595703, 0)) << QQuaternion(1, QVector3D(0.3125, 0.328125, 0)); // clang-format on } void TestEdid::testEdidParser() { QFETCH(QByteArray, raw_edid); QFETCH(QString, deviceId); QFETCH(QString, name); QFETCH(QString, pnpId); QFETCH(QStringList, vendor); QFETCH(QString, serial); QFETCH(QString, eisaId); QFETCH(QString, hash); QFETCH(uint, width); QFETCH(uint, height); QFETCH(qreal, gamma); QFETCH(QQuaternion, red); QFETCH(QQuaternion, green); QFETCH(QQuaternion, blue); QFETCH(QQuaternion, white); QScopedPointer<Edid> e(new Edid(raw_edid)); QCOMPARE(e->isValid(), true); // FIXME: we hard-code all deviceIds as xrandr-something, that makes no sense QCOMPARE(e->deviceId(), deviceId); QCOMPARE(e->name(), name); QCOMPARE(e->pnpId(), pnpId); // FIXME: needs to return at least the short ID // QVERIFY2(vendor.contains(e->vendor()), qPrintable(QString::fromLatin1("%1 not in list").arg(e->vendor()))); QCOMPARE(e->serial(), serial); QCOMPARE(e->eisaId(), eisaId); QCOMPARE(e->hash(), hash); QCOMPARE(e->width(), width); QCOMPARE(e->height(), height); QCOMPARE(e->gamma(), gamma); QVERIFY(qFuzzyCompare(e->red(), red)); QVERIFY(qFuzzyCompare(e->green(), green)); QVERIFY(qFuzzyCompare(e->blue(), blue)); QVERIFY(qFuzzyCompare(e->white(), white)); } QTEST_GUILESS_MAIN(TestEdid) #include "testedid.moc" 0707010000001F000081A40000000000000000000000016638555E00002976000000000000000000000000000000000000004A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testinprocess.cpp/* * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QDBusConnectionInterface> #include <QObject> #include <QSignalSpy> #include <QtTest> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/configmonitor.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" #include "../src/setconfigoperation.h" Q_LOGGING_CATEGORY(KSCREEN, "kscreen") using namespace KScreen; class TestInProcess : public QObject { Q_OBJECT public: explicit TestInProcess(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void loadConfig(); void testCreateJob(); void testModeSwitching(); void testBackendCaching(); void testConfigApply(); void testConfigMonitor(); private: ConfigPtr m_config; bool m_backendServiceInstalled = false; }; TestInProcess::TestInProcess(QObject *parent) : QObject(parent) , m_config(nullptr) { } void TestInProcess::initTestCase() { m_backendServiceInstalled = true; const QString kscreenServiceName = QStringLiteral("org.kde.KScreen"); QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); if (!bus->isServiceRegistered(kscreenServiceName)) { auto reply = bus->startService(kscreenServiceName); if (!reply.isValid()) { qDebug() << "D-Bus service org.kde.KScreen could not be started, skipping out-of-process tests"; m_backendServiceInstalled = false; } } } void TestInProcess::init() { qputenv("KSCREEN_LOGGING", "false"); // Make sure we do everything in-process qputenv("KSCREEN_BACKEND_INPROCESS", "1"); // Use Fake backend with one of the json configs qputenv("KSCREEN_BACKEND", "Fake"); KScreen::BackendManager::instance()->shutdownBackend(); KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "multipleoutput.json"}}); } void TestInProcess::cleanup() { KScreen::BackendManager::instance()->shutdownBackend(); } void TestInProcess::loadConfig() { qputenv("KSCREEN_BACKEND_INPROCESS", "1"); BackendManager::instance()->setMethod(BackendManager::InProcess); auto *op = new GetConfigOperation(); QVERIFY(op->exec()); m_config = op->config(); QVERIFY(m_config); QVERIFY(m_config->isValid()); } void TestInProcess::testModeSwitching() { KScreen::BackendManager::instance()->shutdownBackend(); BackendManager::instance()->setMethod(BackendManager::InProcess); // Load QScreen backend in-process qDebug() << "TT qscreen in-process"; qputenv("KSCREEN_BACKEND", "QScreen"); auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto oc = op->config(); QVERIFY(oc != nullptr); QVERIFY(oc->isValid()); qDebug() << "TT fake in-process"; // Load the Fake backend in-process qputenv("KSCREEN_BACKEND", "Fake"); auto ip = new GetConfigOperation(); QVERIFY(ip->exec()); auto ic = ip->config(); QVERIFY(ic != nullptr); QVERIFY(ic->isValid()); QVERIFY(ic->outputs().count()); KScreen::ConfigPtr xc(nullptr); if (m_backendServiceInstalled) { qDebug() << "TT xrandr out-of-process"; // Load the xrandr backend out-of-process qputenv("KSCREEN_BACKEND", "QScreen"); qputenv("KSCREEN_BACKEND_INPROCESS", "0"); BackendManager::instance()->setMethod(BackendManager::OutOfProcess); auto xp = new GetConfigOperation(); QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess); QVERIFY(xp->exec()); xc = xp->config(); QVERIFY(xc != nullptr); QVERIFY(xc->isValid()); QVERIFY(xc->outputs().count()); } qDebug() << "TT fake in-process"; qputenv("KSCREEN_BACKEND_INPROCESS", "1"); BackendManager::instance()->setMethod(BackendManager::InProcess); // Load the Fake backend in-process qputenv("KSCREEN_BACKEND", "Fake"); auto fp = new GetConfigOperation(); QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess); QVERIFY(fp->exec()); auto fc = fp->config(); QVERIFY(fc != nullptr); QVERIFY(fc->isValid()); QVERIFY(fc->outputs().count()); QVERIFY(oc->isValid()); QVERIFY(ic->isValid()); if (xc) { QVERIFY(xc->isValid()); } QVERIFY(fc->isValid()); } void TestInProcess::testBackendCaching() { KScreen::BackendManager::instance()->shutdownBackend(); qputenv("KSCREEN_BACKEND", "Fake"); QElapsedTimer t; BackendManager::instance()->setMethod(BackendManager::InProcess); QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess); int t_cold; int t_warm; { t.start(); auto cp = new GetConfigOperation(); cp->exec(); auto cc = cp->config(); t_cold = t.nsecsElapsed(); QVERIFY(cc != nullptr); QVERIFY(cc->isValid()); QVERIFY(cc->outputs().count()); } { // KScreen::BackendManager::instance()->shutdownBackend(); QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess); t.start(); auto cp = new GetConfigOperation(); cp->exec(); auto cc = cp->config(); t_warm = t.nsecsElapsed(); QVERIFY(cc != nullptr); QVERIFY(cc->isValid()); QVERIFY(cc->outputs().count()); } { auto cp = new GetConfigOperation(); QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess); cp->exec(); auto cc = cp->config(); QVERIFY(cc != nullptr); QVERIFY(cc->isValid()); QVERIFY(cc->outputs().count()); } // Check if all our configs are still valid after the backend is gone KScreen::BackendManager::instance()->shutdownBackend(); if (m_backendServiceInstalled) { // qputenv("KSCREEN_BACKEND", "QScreen"); qputenv("KSCREEN_BACKEND_INPROCESS", "0"); BackendManager::instance()->setMethod(BackendManager::OutOfProcess); QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess); int t_x_cold; { t.start(); auto xp = new GetConfigOperation(); xp->exec(); t_x_cold = t.nsecsElapsed(); auto xc = xp->config(); QVERIFY(xc != nullptr); } t.start(); auto xp = new GetConfigOperation(); xp->exec(); int t_x_warm = t.nsecsElapsed(); auto xc = xp->config(); QVERIFY(xc != nullptr); // Make sure in-process is faster QVERIFY(t_cold > t_warm); QVERIFY(t_x_cold > t_x_warm); QVERIFY(t_x_cold > t_cold); return; qDebug() << "ip speedup for cached access:" << (qreal)((qreal)t_cold / (qreal)t_warm); qDebug() << "oop speedup for cached access:" << (qreal)((qreal)t_x_cold / (qreal)t_x_warm); qDebug() << "out-of vs. in-process speedup:" << (qreal)((qreal)t_x_warm / (qreal)t_warm); qDebug() << "cold oop: " << ((qreal)t_x_cold / 1000000); qDebug() << "cached oop: " << ((qreal)t_x_warm / 1000000); qDebug() << "cold in process: " << ((qreal)t_cold / 1000000); qDebug() << "cached in process: " << ((qreal)t_warm / 1000000); } } void TestInProcess::testCreateJob() { KScreen::BackendManager::instance()->shutdownBackend(); { BackendManager::instance()->setMethod(BackendManager::InProcess); auto op = new GetConfigOperation(); auto _op = qobject_cast<GetConfigOperation *>(op); QVERIFY(_op != nullptr); QCOMPARE(BackendManager::instance()->method(), BackendManager::InProcess); QVERIFY(op->exec()); auto cc = op->config(); QVERIFY(cc != nullptr); QVERIFY(cc->isValid()); } if (m_backendServiceInstalled) { BackendManager::instance()->setMethod(BackendManager::OutOfProcess); auto op = new GetConfigOperation(); auto _op = qobject_cast<GetConfigOperation *>(op); QVERIFY(_op != nullptr); QCOMPARE(BackendManager::instance()->method(), BackendManager::OutOfProcess); QVERIFY(op->exec()); auto cc = op->config(); QVERIFY(cc != nullptr); QVERIFY(cc->isValid()); } KScreen::BackendManager::instance()->shutdownBackend(); BackendManager::instance()->setMethod(BackendManager::InProcess); } void TestInProcess::testConfigApply() { qputenv("KSCREEN_BACKEND", "Fake"); KScreen::BackendManager::instance()->shutdownBackend(); BackendManager::instance()->setMethod(BackendManager::InProcess); auto op = new GetConfigOperation(); op->exec(); auto config = op->config(); // qDebug() << "op:" << config->outputs().count(); auto output = config->outputs().first(); // qDebug() << "res:" << output->geometry(); // qDebug() << "modes:" << output->modes(); auto m0 = output->modes().first(); // qDebug() << "m0:" << m0->id() << m0; output->setCurrentModeId(m0->id()); QVERIFY(Config::canBeApplied(config)); // expected to fail, SetConfigOperation is out-of-process only auto setop = new SetConfigOperation(config); QVERIFY(!setop->hasError()); QVERIFY(setop->exec()); QVERIFY(!setop->hasError()); } void TestInProcess::testConfigMonitor() { qputenv("KSCREEN_BACKEND", "Fake"); KScreen::BackendManager::instance()->shutdownBackend(); BackendManager::instance()->setMethod(BackendManager::InProcess); auto op = new GetConfigOperation(); op->exec(); auto config = op->config(); // qDebug() << "op:" << config->outputs().count(); auto output = config->outputs().first(); // qDebug() << "res:" << output->geometry(); // qDebug() << "modes:" << output->modes(); auto m0 = output->modes().first(); // qDebug() << "m0:" << m0->id() << m0; output->setCurrentModeId(m0->id()); QVERIFY(Config::canBeApplied(config)); QSignalSpy monitorSpy(ConfigMonitor::instance(), &ConfigMonitor::configurationChanged); qDebug() << "MOnitorspy connencted."; ConfigMonitor::instance()->addConfig(config); auto setop = new SetConfigOperation(config); QVERIFY(!setop->hasError()); // do not cal setop->exec(), this must not block as the signalspy already blocks QVERIFY(monitorSpy.wait(500)); } QTEST_GUILESS_MAIN(TestInProcess) #include "testinprocess.moc" 07070100000020000081A40000000000000000000000016638555E00002537000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testkwaylandbackend.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QCryptographicHash> #include <QObject> #include <QtTest> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/configmonitor.h" #include "../src/edid.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" #include "../src/screen.h" #include "../src/setconfigoperation.h" // KWayland #include <KWayland/Server/display.h> #include <KWayland/Server/outputdevice_interface.h> #include "waylandtestserver.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND, "kscreen.kwayland") using namespace KScreen; class testWaylandBackend : public QObject { Q_OBJECT public: explicit testWaylandBackend(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void loadConfig(); void verifyConfig(); void verifyOutputs(); void verifyModes(); void verifyScreen(); void verifyIds(); void verifyFeatures(); void simpleWrite(); void addOutput(); void removeOutput(); void testEdid(); private: ConfigPtr m_config; WaylandTestServer *m_server; KWayland::Server::OutputDeviceInterface *m_serverOutputDevice; }; testWaylandBackend::testWaylandBackend(QObject *parent) : QObject(parent) , m_config(nullptr) { qputenv("KSCREEN_LOGGING", "false"); m_server = new WaylandTestServer(this); m_server->setConfig(QLatin1String(TEST_DATA) + QLatin1String("multipleoutput.json")); } void testWaylandBackend::initTestCase() { qputenv("KSCREEN_BACKEND", "kwayland"); KScreen::BackendManager::instance()->shutdownBackend(); // This is how KWayland will pick up the right socket, // and thus connect to our internal test server. setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit().constData(), 1); m_server->start(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); } void testWaylandBackend::loadConfig() { GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); QVERIFY(m_config->isValid()); qCDebug(KSCREEN_WAYLAND) << "ops" << m_config->outputs(); } void testWaylandBackend::verifyConfig() { QVERIFY(m_config != nullptr); if (!m_config) { QSKIP("Wayland backend invalid", SkipAll); } } void testWaylandBackend::verifyScreen() { ScreenPtr screen = m_config->screen(); QVERIFY(screen->minSize().width() <= screen->maxSize().width()); QVERIFY(screen->minSize().height() <= screen->maxSize().height()); QVERIFY(screen->minSize().width() <= screen->currentSize().width()); QVERIFY(screen->minSize().height() <= screen->currentSize().height()); QVERIFY(screen->maxSize().width() >= screen->currentSize().width()); QVERIFY(screen->maxSize().height() >= screen->currentSize().height()); QVERIFY(m_config->screen()->maxActiveOutputsCount() > 0); } void testWaylandBackend::verifyOutputs() { bool primaryFound = false; for (const KScreen::OutputPtr &op : m_config->outputs()) { if (op->isPrimary()) { primaryFound = true; } } // qCDebug(KSCREEN_WAYLAND) << "Primary found? " << primaryFound << m_config->outputs(); QVERIFY(primaryFound); QVERIFY(m_config->outputs().count()); QCOMPARE(m_server->outputCount(), m_config->outputs().count()); KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary->isEnabled()); QVERIFY(primary->isConnected()); QList<int> ids; for (const auto &output : m_config->outputs()) { QVERIFY(!output->name().isEmpty()); QVERIFY(output->id() > -1); QVERIFY(output->isConnected()); QVERIFY(output->geometry() != QRectF(1, 1, 1, 1)); QVERIFY(output->geometry() != QRectF()); QVERIFY(output->sizeMm() != QSize()); QVERIFY(output->edid() != nullptr); QVERIFY(output->preferredModes().size() == 1); QCOMPARE(output->rotation(), Output::None); QVERIFY(!ids.contains(output->id())); ids << output->id(); } } void testWaylandBackend::verifyModes() { KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary); QVERIFY(primary->modes().count() > 0); for (const auto &output : m_config->outputs()) { for (const auto &mode : output->modes()) { QVERIFY(!mode->name().isEmpty()); QVERIFY(mode->refreshRate() > 0); QVERIFY(mode->size().isValid()); } } } void testWaylandBackend::verifyIds() { QList<quint32> ids; for (const auto &output : m_config->outputs()) { QVERIFY(ids.contains(output->id()) == false); QVERIFY(output->id() > 0); ids << output->id(); } } void testWaylandBackend::simpleWrite() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); m_config = op->config(); auto output = m_config->output(3); QVERIFY(output); auto n_mode = QStringLiteral("800x600@60"); auto o_mode = output->currentModeId(); output->setCurrentModeId(n_mode); auto setop = new SetConfigOperation(m_config); QVERIFY(setop->exec()); } void testWaylandBackend::cleanupTestCase() { m_config->deleteLater(); KScreen::BackendManager::instance()->shutdownBackend(); } void testWaylandBackend::addOutput() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QCOMPARE(config->outputs().count(), 2); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); // Now add an outputdevice on the server side m_serverOutputDevice = m_server->display()->createOutputDevice(this); m_serverOutputDevice->setUuid("1337"); OutputDeviceInterface::Mode m0; m0.id = 0; m0.size = QSize(800, 600); m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); m_serverOutputDevice->addMode(m0); OutputDeviceInterface::Mode m1; m1.id = 1; m1.size = QSize(1024, 768); m_serverOutputDevice->addMode(m1); OutputDeviceInterface::Mode m2; m2.id = 2; m2.size = QSize(1280, 1024); m2.refreshRate = 90000; m_serverOutputDevice->addMode(m2); m_serverOutputDevice->setCurrentMode(1); m_serverOutputDevice->create(); QVERIFY(configSpy.wait()); // QTRY_VERIFY(configSpy.count()); GetConfigOperation *op2 = new GetConfigOperation(); op2->exec(); auto newconfig = op2->config(); QCOMPARE(newconfig->outputs().count(), 3); } void testWaylandBackend::removeOutput() { KScreen::BackendManager::instance()->shutdownBackend(); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QCOMPARE(config->outputs().count(), 3); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); delete m_serverOutputDevice; QVERIFY(configSpy.wait()); GetConfigOperation *op2 = new GetConfigOperation(); op2->exec(); auto newconfig = op2->config(); QCOMPARE(newconfig->outputs().count(), 2); } void testWaylandBackend::testEdid() { m_server->showOutputs(); QByteArray data = QByteArray::fromBase64( "AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/" "QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHow" "K0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); QScopedPointer<Edid> edid(new Edid(data)); QVERIFY(edid->isValid()); GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QVERIFY(config->outputs().count() > 0); auto o = config->outputs().last(); qCDebug(KSCREEN_WAYLAND) << "Edid: " << o->edid()->isValid(); QVERIFY(o->edid()->isValid()); QCOMPARE(o->edid()->deviceId(), edid->deviceId()); QCOMPARE(o->edid()->name(), edid->name()); QCOMPARE(o->edid()->vendor(), edid->vendor()); QCOMPARE(o->edid()->eisaId(), edid->eisaId()); QCOMPARE(o->edid()->serial(), edid->serial()); QCOMPARE(o->edid()->hash(), edid->hash()); QCOMPARE(o->edid()->width(), edid->width()); QCOMPARE(o->edid()->height(), edid->height()); QCOMPARE(o->edid()->gamma(), edid->gamma()); QCOMPARE(o->edid()->red(), edid->red()); QCOMPARE(o->edid()->green(), edid->green()); QCOMPARE(o->edid()->blue(), edid->blue()); QCOMPARE(o->edid()->white(), edid->white()); } void testWaylandBackend::verifyFeatures() { GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(Config::Feature::Writable)); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::PrimaryDisplay)); } QTEST_GUILESS_MAIN(testWaylandBackend) #include "testkwaylandbackend.moc" 07070100000021000081A40000000000000000000000016638555E000022B2000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testkwaylandconfig.cpp/* * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QObject> #include <QSignalSpy> #include <QtTest> #include "backendmanager_p.h" #include "config.h" #include "configmonitor.h" #include "getconfigoperation.h" #include "output.h" #include "setconfigoperation.h" #include "waylandtestserver.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND, "kscreen.kwayland") using namespace KScreen; class TestKWaylandConfig : public QObject { Q_OBJECT public: explicit TestKWaylandConfig(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void changeConfig(); void testPositionChange(); void testRotationChange(); void testRotationChange_data(); void testScaleChange(); void testModeChange(); void testApplyOnPending(); private: WaylandTestServer *m_server; }; TestKWaylandConfig::TestKWaylandConfig(QObject *parent) : QObject(parent) , m_server(nullptr) { qputenv("KSCREEN_LOGGING", "false"); } void TestKWaylandConfig::initTestCase() { setenv("KSCREEN_BACKEND", "kwayland", 1); KScreen::BackendManager::instance()->shutdownBackend(); // This is how KWayland will pick up the right socket, // and thus connect to our internal test server. setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit().constData(), 1); m_server = new WaylandTestServer(this); m_server->start(); } void TestKWaylandConfig::cleanupTestCase() { qDebug() << "Shutting down"; KScreen::BackendManager::instance()->shutdownBackend(); delete m_server; } void TestKWaylandConfig::changeConfig() { auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); // Prepare monitor & spy KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); // The first output is currently disabled, let's try to enable it auto output = config->outputs().first(); QVERIFY(output->isEnabled() == false); output->setEnabled(true); output->setCurrentModeId(QStringLiteral("76")); auto output2 = config->outputs()[2]; // is this id stable enough? output2->setPos(QPoint(4000, 1080)); output2->setRotation(KScreen::Output::Left); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); auto sop = new SetConfigOperation(config, this); sop->exec(); // fire and forget... QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); monitor->removeConfig(config); m_server->showOutputs(); } void TestKWaylandConfig::testPositionChange() { auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); // Prepare monitor & spy KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); auto output = config->outputs()[2]; // is this id stable enough? auto new_pos = QPoint(3840, 1080); output->setPos(new_pos); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); auto sop = new SetConfigOperation(config, this); sop->exec(); // fire and forget... QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); } void TestKWaylandConfig::testRotationChange_data() { QTest::addColumn<KScreen::Output::Rotation>("rotation"); QTest::newRow("left") << KScreen::Output::Left; QTest::newRow("inverted") << KScreen::Output::Inverted; QTest::newRow("right") << KScreen::Output::Right; QTest::newRow("none") << KScreen::Output::None; } void TestKWaylandConfig::testRotationChange() { QFETCH(KScreen::Output::Rotation, rotation); auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); // Prepare monitor & spy KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); auto output = config->outputs().first(); // is this id stable enough? output->setRotation(rotation); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); auto sop = new SetConfigOperation(config, this); sop->exec(); // fire and forget... QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); // Get a new config, then compare the output with the expected new value auto newop = new GetConfigOperation(); QVERIFY(newop->exec()); auto newconfig = newop->config(); QVERIFY(newconfig); auto newoutput = newconfig->outputs().first(); QCOMPARE(newoutput->rotation(), rotation); } void TestKWaylandConfig::testScaleChange() { auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); auto op2 = new GetConfigOperation(); QVERIFY(op2->exec()); auto config2 = op2->config(); QVERIFY(config2); // Prepare monitor & spy KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); monitor->addConfig(config2); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); QSignalSpy configSpy2(monitor, &KScreen::ConfigMonitor::configurationChanged); auto output2 = config2->outputs()[2]; // is this id stable enough? QSignalSpy outputSpy(output2.data(), &KScreen::Output::scaleChanged); QCOMPARE(output2->scale(), 1.0); auto output = config->outputs()[2]; // is this id stable enough? output->setScale(2); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); auto sop = new SetConfigOperation(config, this); sop->exec(); // fire and forget... QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); QCOMPARE(outputSpy.count(), 1); QCOMPARE(configSpy2.count(), 1); QCOMPARE(output2->scale(), 2.0); } void TestKWaylandConfig::testModeChange() { auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); auto output = config->outputs()[1]; // is this id stable enough? QString new_mode = QStringLiteral("74"); output->setCurrentModeId(new_mode); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); auto sop = new SetConfigOperation(config, this); sop->exec(); QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); } void TestKWaylandConfig::testApplyOnPending() { auto op = new GetConfigOperation(); QVERIFY(op->exec()); auto config = op->config(); QVERIFY(config); auto op2 = new GetConfigOperation(); QVERIFY(op2->exec()); auto config2 = op2->config(); QVERIFY(config2); KScreen::ConfigMonitor *monitor = KScreen::ConfigMonitor::instance(); monitor->addConfig(config); QSignalSpy configSpy(monitor, &KScreen::ConfigMonitor::configurationChanged); auto output = config->outputs()[1]; // is this id stable enough? QCOMPARE(output->scale(), 1.0); output->setScale(2); QSignalSpy serverSpy(m_server, &WaylandTestServer::configChanged); QSignalSpy serverReceivedSpy(m_server, &WaylandTestServer::configReceived); m_server->suspendChanges(true); new SetConfigOperation(config, this); /* Apply next config */ auto output2 = config2->outputs()[2]; // is this id stable enough? QCOMPARE(output2->scale(), 2.0); output2->setScale(3); new SetConfigOperation(config2, this); QVERIFY(serverReceivedSpy.wait()); QCOMPARE(serverReceivedSpy.count(), 1); m_server->suspendChanges(false); QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 1); QCOMPARE(configSpy.count(), 1); QCOMPARE(output->scale(), 2.0); QCOMPARE(output2->scale(), 3.0); QVERIFY(configSpy.wait()); // check if the server changed QCOMPARE(serverSpy.count(), 2); QCOMPARE(configSpy.count(), 2); QCOMPARE(output2->scale(), 3.0); } QTEST_GUILESS_MAIN(TestKWaylandConfig) #include "testkwaylandconfig.moc" 07070100000022000081A40000000000000000000000016638555E00000A95000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testkwaylanddpms.cpp/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QCoreApplication> #include <QObject> #include <QSignalSpy> #include <QtTest> #include <KWayland/Client/connection_thread.h> #include <KWayland/Client/dpms.h> #include <KWayland/Client/registry.h> #include "waylandtestserver.h" static const QString s_socketName = QStringLiteral("libkscreen-test-wayland-backend-0"); // static const QString s_socketName = QStringLiteral("wayland-0"); Q_LOGGING_CATEGORY(KSCREEN, "kscreen") using namespace KWayland::Client; class TestDpmsClient : public QObject { Q_OBJECT public: explicit TestDpmsClient(QObject *parent = nullptr); Q_SIGNALS: void dpmsAnnounced(); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testDpmsConnect(); private: ConnectionThread *m_connection; QThread *m_thread; Registry *m_registry; KScreen::WaylandTestServer *m_server; }; TestDpmsClient::TestDpmsClient(QObject *parent) : QObject(parent) , m_server(nullptr) { setenv("WAYLAND_DISPLAY", s_socketName.toLocal8Bit().constData(), true); m_server = new KScreen::WaylandTestServer(this); m_server->start(); } void TestDpmsClient::initTestCase() { // setup connection m_connection = new KWayland::Client::ConnectionThread; m_connection->setSocketName(s_socketName); QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); QSignalSpy dpmsSpy(this, &TestDpmsClient::dpmsAnnounced); m_connection->initConnection(); QVERIFY(connectedSpy.wait(100)); m_registry = new KWayland::Client::Registry; m_registry->create(m_connection); QObject::connect(m_registry, &Registry::interfacesAnnounced, this, [this] { const bool hasDpms = m_registry->hasInterface(Registry::Interface::Dpms); if (hasDpms) { qDebug() << QStringLiteral("Compositor provides a DpmsManager"); } else { qDebug() << QStringLiteral("Compositor does not provid a DpmsManager"); } Q_EMIT this->dpmsAnnounced(); }); m_registry->setup(); QVERIFY(dpmsSpy.wait(100)); } void TestDpmsClient::cleanupTestCase() { m_thread->exit(); m_thread->wait(); delete m_registry; delete m_thread; delete m_connection; } void TestDpmsClient::testDpmsConnect() { QVERIFY(m_registry->isValid()); } QTEST_GUILESS_MAIN(TestDpmsClient) #include "testkwaylanddpms.moc" 07070100000023000081A40000000000000000000000016638555E00000A34000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testlog.cpp/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QLoggingCategory> #include <QObject> #include <QtTest> #include "../src/log.h" Q_DECLARE_LOGGING_CATEGORY(KSCREEN_TESTLOG) Q_LOGGING_CATEGORY(KSCREEN_TESTLOG, "kscreen.testlog") using namespace KScreen; auto KSCREEN_LOGGING = "KSCREEN_LOGGING"; class TestLog : public QObject { Q_OBJECT private Q_SLOTS: void init(); void initTestCase(); void cleanupTestCase(); void testContext(); void testEnabled(); void testLog(); private: QString m_defaultLogFile; }; void TestLog::init() { QStandardPaths::setTestModeEnabled(true); m_defaultLogFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kscreen/kscreen.log"); } void TestLog::initTestCase() { qputenv(KSCREEN_LOGGING, QByteArray("true")); } void TestLog::cleanupTestCase() { qunsetenv(KSCREEN_LOGGING); } void TestLog::testContext() { auto log = Log::instance(); QString ctx = QStringLiteral("context text"); QVERIFY(log != nullptr); log->setContext(ctx); QCOMPARE(log->context(), ctx); delete log; } void TestLog::testEnabled() { qputenv(KSCREEN_LOGGING, QByteArray("faLSe")); auto log = Log::instance(); QCOMPARE(log->enabled(), false); QCOMPARE(log->logFile(), QString()); delete log; qunsetenv(KSCREEN_LOGGING); log = Log::instance(); QCOMPARE(log->enabled(), false); QCOMPARE(log->logFile(), QString()); delete log; qputenv(KSCREEN_LOGGING, QByteArray("truE")); log = Log::instance(); QCOMPARE(log->enabled(), true); QCOMPARE(log->logFile(), m_defaultLogFile); delete log; } void TestLog::testLog() { auto log = Log::instance(); Q_UNUSED(log); QFile lf(m_defaultLogFile); lf.remove(); QVERIFY(!lf.exists()); QString logmsg = QStringLiteral("This is a log message. ♥"); Log::log(logmsg); QVERIFY(lf.exists()); QVERIFY(lf.remove()); qCDebug(KSCREEN_TESTLOG) << "qCDebug message from testlog"; QVERIFY(lf.exists()); QVERIFY(lf.remove()); delete Log::instance(); // Make sure on log file gets written when disabled qputenv(KSCREEN_LOGGING, "false"); qCDebug(KSCREEN_TESTLOG) << logmsg; QCOMPARE(Log::instance()->enabled(), false); QVERIFY(!lf.exists()); Log::log(logmsg); QVERIFY(!lf.exists()); // Make sure we don't crash on cleanup delete Log::instance(); delete Log::instance(); } QTEST_MAIN(TestLog) #include "testlog.moc" 07070100000024000081A40000000000000000000000016638555E0000124B000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testmodelistchange.cpp/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QObject> #include <QtTest> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/configmonitor.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" using namespace KScreen; class TestModeListChange : public QObject { Q_OBJECT private: KScreen::ConfigPtr getConfig(); KScreen::ModeList createModeList(); bool compareModeList(KScreen::ModeList before, KScreen::ModeList &after); QSize s0 = QSize(1920, 1080); QSize s1 = QSize(1600, 1200); QSize s2 = QSize(1280, 1024); QSize s3 = QSize(800, 600); QSize snew = QSize(777, 888); QString idnew = QStringLiteral("666"); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void modeListChange(); }; ConfigPtr TestModeListChange::getConfig() { qputenv("KSCREEN_BACKEND_INPROCESS", "1"); auto *op = new GetConfigOperation(); if (!op->exec()) { qWarning("ConfigOperation error: %s", qPrintable(op->errorString())); BackendManager::instance()->shutdownBackend(); return ConfigPtr(); } BackendManager::instance()->shutdownBackend(); return op->config(); } KScreen::ModeList TestModeListChange::createModeList() { KScreen::ModeList newmodes; { QString _id = QString::number(11); KScreen::ModePtr kscreenMode(new KScreen::Mode); kscreenMode->setId(_id); kscreenMode->setName(_id); kscreenMode->setSize(s0); kscreenMode->setRefreshRate(60); newmodes.insert(_id, kscreenMode); } { QString _id = QString::number(22); KScreen::ModePtr kscreenMode(new KScreen::Mode); kscreenMode->setId(_id); kscreenMode->setName(_id); kscreenMode->setSize(s1); kscreenMode->setRefreshRate(60); newmodes.insert(_id, kscreenMode); } { QString _id = QString::number(33); KScreen::ModePtr kscreenMode(new KScreen::Mode); kscreenMode->setId(_id); kscreenMode->setName(_id); kscreenMode->setSize(s2); kscreenMode->setRefreshRate(60); newmodes.insert(_id, kscreenMode); } return newmodes; } void TestModeListChange::initTestCase() { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND", "Fake"); } void TestModeListChange::cleanupTestCase() { BackendManager::instance()->shutdownBackend(); } void TestModeListChange::modeListChange() { // json file for the fake backend KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "singleoutput.json"}}); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); auto output = config->outputs().first(); QVERIFY(!output.isNull()); auto modelist = output->modes(); auto mode = modelist.first(); mode->setId(QStringLiteral("44")); mode->setSize(QSize(880, 440)); output->setModes(modelist); QCOMPARE(output->modes().first()->id(), QStringLiteral("44")); QCOMPARE(output->modes().first()->size(), QSize(880, 440)); QVERIFY(!modelist.isEmpty()); ConfigMonitor::instance()->addConfig(config); QSignalSpy outputChangedSpy(output.data(), &Output::outputChanged); QVERIFY(outputChangedSpy.isValid()); QSignalSpy modesChangedSpy(output.data(), &Output::modesChanged); QVERIFY(modesChangedSpy.isValid()); auto before = createModeList(); output->setModes(before); QCOMPARE(modesChangedSpy.count(), 1); output->setModes(before); QCOMPARE(modesChangedSpy.count(), 1); output->setModes(before); QCOMPARE(modesChangedSpy.count(), 1); QCOMPARE(output->modes().first()->size(), s0); QCOMPARE(output->modes().first()->id(), QStringLiteral("11")); auto after = createModeList(); auto firstmode = after.first(); QVERIFY(!firstmode.isNull()); QCOMPARE(firstmode->size(), s0); QCOMPARE(firstmode->id(), QStringLiteral("11")); firstmode->setSize(snew); firstmode->setId(idnew); output->setModes(after); QCOMPARE(modesChangedSpy.count(), 2); QString _id = QString::number(11); KScreen::ModePtr kscreenMode(new KScreen::Mode); kscreenMode->setId(_id); kscreenMode->setName(_id); kscreenMode->setSize(s0); kscreenMode->setRefreshRate(60); before.insert(_id, kscreenMode); output->setModes(before); QCOMPARE(modesChangedSpy.count(), 3); QCOMPARE(outputChangedSpy.count(), modesChangedSpy.count()); } QTEST_MAIN(TestModeListChange) #include "testmodelistchange.moc" 07070100000025000081A40000000000000000000000016638555E00001A11000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testqscreenbackend.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QElapsedTimer> #include <QObject> #include <QtTest> #include <cstdint> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/edid.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" #include "../src/screen.h" Q_LOGGING_CATEGORY(KSCREEN_QSCREEN, "kscreen.qscreen") using namespace KScreen; class testQScreenBackend : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void verifyConfig(); void verifyScreen(); void verifyOutputs(); void verifyModes(); void verifyFeatures(); void commonUsagePattern(); void cleanupTestCase(); private: QProcess m_process; ConfigPtr m_config; QString m_backend; }; void testQScreenBackend::initTestCase() { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND", "qscreen"); qputenv("KSCREEN_BACKEND_INPROCESS", "1"); KScreen::BackendManager::instance()->shutdownBackend(); m_backend = QString::fromLocal8Bit(qgetenv("KSCREEN_BACKEND")); QElapsedTimer t; t.start(); auto *op = new GetConfigOperation(); op->exec(); m_config = op->config(); const int n = t.nsecsElapsed(); qDebug() << "Test took: " << n << "ns"; } void testQScreenBackend::verifyConfig() { QVERIFY(!m_config.isNull()); if (!m_config) { QSKIP("QScreenbackend invalid", SkipAll); } } void testQScreenBackend::verifyScreen() { ScreenPtr screen = m_config->screen(); QVERIFY(screen->minSize().width() <= screen->maxSize().width()); QVERIFY(screen->minSize().height() <= screen->maxSize().height()); QVERIFY(screen->minSize().width() <= screen->currentSize().width()); QVERIFY(screen->minSize().height() <= screen->currentSize().height()); QVERIFY(screen->maxSize().width() >= screen->currentSize().width()); QVERIFY(screen->maxSize().height() >= screen->currentSize().height()); QVERIFY(m_config->screen()->maxActiveOutputsCount() > 0); } void testQScreenBackend::verifyOutputs() { bool primaryFound = false; for (const KScreen::OutputPtr &op : m_config->outputs()) { if (op->isPrimary()) { primaryFound = true; } } qDebug() << "Primary found? " << primaryFound; QVERIFY(primaryFound); if (m_backend == QLatin1String("screen")) { QCOMPARE(m_config->outputs().count(), QGuiApplication::screens().count()); } const KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary->isEnabled()); QVERIFY(primary->isConnected()); // qDebug() << "Primary geometry? " << primary->geometry(); // qDebug() << " prim modes: " << primary->modes(); QList<int> ids; for (const KScreen::OutputPtr &output : m_config->outputs()) { qDebug() << " _____________________ Output: " << output; qDebug() << " output name: " << output->name(); qDebug() << " output modes: " << output->modes().count() << output->modes(); qDebug() << " output enabled: " << output->isEnabled(); qDebug() << " output connect: " << output->isConnected(); qDebug() << " output sizeMm : " << output->sizeMm(); QVERIFY(!output->name().isEmpty()); QVERIFY(output->id() > -1); QVERIFY(output->isConnected()); QVERIFY(output->isEnabled()); QVERIFY(output->geometry() != QRectF(1, 1, 1, 1)); QVERIFY(output->geometry() != QRectF()); // Pass, but leave a note, when the x server doesn't report physical size if (!output->sizeMm().isValid()) { QEXPECT_FAIL("", "The X server doesn't return a sensible physical output size", Continue); QVERIFY(output->sizeMm() != QSize()); } QVERIFY(output->edid() != nullptr); QCOMPARE(output->rotation(), Output::None); QVERIFY(!ids.contains(output->id())); ids << output->id(); } } void testQScreenBackend::verifyModes() { const KScreen::OutputPtr primary = m_config->primaryOutput(); QVERIFY(primary); QVERIFY(primary->modes().count() > 0); for (const KScreen::OutputPtr &output : m_config->outputs()) { for (const KScreen::ModePtr &mode : output->modes()) { qDebug() << " Mode : " << mode->name(); QVERIFY(!mode->name().isEmpty()); QVERIFY(mode->refreshRate() > 0); QVERIFY(mode->size() != QSize()); } } } void testQScreenBackend::commonUsagePattern() { auto *op = new GetConfigOperation(); op->exec(); const KScreen::OutputList outputs = op->config()->outputs(); QVariantList outputList; for (const KScreen::OutputPtr &output : outputs) { if (!output->isConnected()) { continue; } QVariantMap info; info[QStringLiteral("id")] = output->id(); info[QStringLiteral("priority")] = output->priority(); info[QStringLiteral("enabled")] = output->isEnabled(); info[QStringLiteral("rotation")] = output->rotation(); QVariantMap pos; pos[QStringLiteral("x")] = output->pos().x(); pos[QStringLiteral("y")] = output->pos().y(); info[QStringLiteral("pos")] = pos; if (output->isEnabled()) { const KScreen::ModePtr mode = output->currentMode(); if (!mode) { // qWarning() << "CurrentMode is null" << output->name(); return; } QVariantMap modeInfo; modeInfo[QStringLiteral("refresh")] = mode->refreshRate(); QVariantMap modeSize; modeSize[QStringLiteral("width")] = mode->size().width(); modeSize[QStringLiteral("height")] = mode->size().height(); modeInfo[QStringLiteral("size")] = modeSize; info[QStringLiteral("mode")] = modeInfo; } outputList.append(info); } } void testQScreenBackend::cleanupTestCase() { KScreen::BackendManager::instance()->shutdownBackend(); qApp->exit(0); } void testQScreenBackend::verifyFeatures() { GetConfigOperation *op = new GetConfigOperation(); op->exec(); auto config = op->config(); QVERIFY(config->supportedFeatures().testFlag(Config::Feature::None)); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::Writable)); QVERIFY(!config->supportedFeatures().testFlag(Config::Feature::PrimaryDisplay)); } QTEST_MAIN(testQScreenBackend) #include "testqscreenbackend.moc" 07070100000026000081A40000000000000000000000016638555E00002D15000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testscreenconfig.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include <QObject> #include <QtTest> #include "../src/backendmanager_p.h" #include "../src/config.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" #include "../src/screen.h" #include "../src/setconfigoperation.h" using namespace KScreen; class testScreenConfig : public QObject { Q_OBJECT private: KScreen::ConfigPtr getConfig(); private Q_SLOTS: void initTestCase(); void singleOutput(); void singleOutputWithoutPreferred(); void multiOutput(); void clonesOutput(); void configCanBeApplied(); void supportedFeatures(); void testInvalidMode(); void cleanupTestCase(); void testOutputPositionNormalization(); }; ConfigPtr testScreenConfig::getConfig() { qputenv("KSCREEN_BACKEND_INPROCESS", "1"); auto *op = new GetConfigOperation(); if (!op->exec()) { qWarning("ConfigOperation error: %s", qPrintable(op->errorString())); BackendManager::instance()->shutdownBackend(); return ConfigPtr(); } BackendManager::instance()->shutdownBackend(); return op->config(); } void testScreenConfig::initTestCase() { qputenv("KSCREEN_LOGGING", "false"); qputenv("KSCREEN_BACKEND", "Fake"); } void testScreenConfig::cleanupTestCase() { BackendManager::instance()->shutdownBackend(); } void testScreenConfig::singleOutput() { // json file for the fake backend KScreen::BackendManager::instance()->setBackendArgs( {{QStringLiteral("TEST_DATA"), TEST_DATA "singleoutput.json"}, {QStringLiteral("SUPPORTED_FEATURES"), QVariant::fromValue(KScreen::Config::Feature::PerOutputScaling)}}); // QVERIFY2(kscreen, KScreen::errorString().toLatin1()); // QVERIFY2(!kscreen->backend().isEmpty(), "No backend loaded"); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(1280, 800)); QCOMPARE(config->outputs().count(), 1); const OutputPtr output = config->outputs().take(1); QVERIFY(!output.isNull()); QCOMPARE(output->name(), QLatin1String("LVDS1")); QCOMPARE(output->type(), Output::Panel); QCOMPARE(output->modes().count(), 3); QCOMPARE(output->pos(), QPoint(0, 0)); QCOMPARE(output->geometry(), QRect(0, 0, 1280, 800)); QCOMPARE(output->currentModeId(), QLatin1String("3")); QCOMPARE(output->preferredModeId(), QLatin1String("3")); QCOMPARE(output->rotation(), Output::None); QCOMPARE(output->scale(), 1.0); QCOMPARE(output->isConnected(), true); QCOMPARE(output->isEnabled(), true); QCOMPARE(output->isPrimary(), true); // QCOMPARE(output->isEmbedded(), true); QVERIFY2(output->clones().isEmpty(), "In singleOutput is impossible to have clones"); const ModePtr mode = output->currentMode(); QCOMPARE(mode->size(), QSize(1280, 800)); QCOMPARE(mode->refreshRate(), (float)59.9); } void testScreenConfig::singleOutputWithoutPreferred() { KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "singleOutputWithoutPreferred.json"}}); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const OutputPtr output = config->outputs().take(1); QVERIFY(!output.isNull()); QVERIFY(output->preferredModes().isEmpty()); QCOMPARE(output->preferredModeId(), QLatin1String("3")); } void testScreenConfig::multiOutput() { KScreen::BackendManager::instance()->setBackendArgs( {{QStringLiteral("TEST_DATA"), TEST_DATA "multipleoutput.json"}, {QStringLiteral("SUPPORTED_FEATURES"), QVariant::fromValue(KScreen::Config::Feature::PerOutputScaling)}}); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(3200, 1880)); QCOMPARE(config->outputs().count(), 2); const OutputPtr output = config->outputs().take(2); QVERIFY(!output.isNull()); QCOMPARE(output->name(), QStringLiteral("HDMI1")); QCOMPARE(output->type(), Output::HDMI); QCOMPARE(output->modes().count(), 4); QCOMPARE(output->pos(), QPoint(1280, 0)); QCOMPARE(output->geometry(), QRect(1280, 0, 1920 / 1.4, 1080 / 1.4)); QCOMPARE(output->currentModeId(), QLatin1String("4")); QCOMPARE(output->preferredModeId(), QLatin1String("4")); QCOMPARE(output->rotation(), Output::None); QCOMPARE(output->scale(), 1.4); QCOMPARE(output->isConnected(), true); QCOMPARE(output->isEnabled(), true); QCOMPARE(output->isPrimary(), false); QVERIFY2(output->clones().isEmpty(), "This simulates extended output, no clones"); const ModePtr mode = output->currentMode(); QVERIFY(!mode.isNull()); QCOMPARE(mode->size(), QSize(1920, 1080)); QCOMPARE(mode->refreshRate(), (float)60.0); } void testScreenConfig::clonesOutput() { KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "multipleclone.json"}}); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); const ScreenPtr screen = config->screen(); QVERIFY(!screen.isNull()); QCOMPARE(screen->minSize(), QSize(320, 200)); QCOMPARE(screen->maxSize(), QSize(8192, 8192)); QCOMPARE(screen->currentSize(), QSize(1024, 768)); const OutputPtr one = config->outputs()[1]; const OutputPtr two = config->outputs()[2]; QCOMPARE(one->currentMode()->size(), two->currentMode()->size()); QCOMPARE(one->clones().count(), 1); QCOMPARE(one->clones().first(), two->id()); QVERIFY2(two->clones().isEmpty(), "Output two should have no clones"); } void testScreenConfig::configCanBeApplied() { KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "singleoutputBroken.json"}}); const ConfigPtr brokenConfig = getConfig(); KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "singleoutput.json"}}); const ConfigPtr currentConfig = getConfig(); QVERIFY(!currentConfig.isNull()); const OutputPtr primaryBroken = brokenConfig->outputs()[2]; QVERIFY(!primaryBroken.isNull()); const OutputPtr currentPrimary = currentConfig->outputs()[1]; QVERIFY(!currentPrimary.isNull()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setId(currentPrimary->id()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setConnected(currentPrimary->isConnected()); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setCurrentModeId(QStringLiteral("42")); QVERIFY(!Config::canBeApplied(brokenConfig)); primaryBroken->setCurrentModeId(currentPrimary->currentModeId()); QVERIFY(!Config::canBeApplied(brokenConfig)); qDebug() << "brokenConfig.modes" << primaryBroken->mode(QStringLiteral("3")); primaryBroken->mode(QStringLiteral("3"))->setSize(QSize(1280, 800)); qDebug() << "brokenConfig.modes" << primaryBroken->mode(QStringLiteral("3")); QVERIFY(Config::canBeApplied(brokenConfig)); KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "tooManyOutputs.json"}}); const ConfigPtr brokenConfig2 = getConfig(); QVERIFY(!brokenConfig2.isNull()); int enabledOutputsCount = 0; for (const OutputPtr &output : brokenConfig2->outputs()) { if (output->isEnabled()) { ++enabledOutputsCount; } } QVERIFY(brokenConfig2->screen()->maxActiveOutputsCount() < enabledOutputsCount); QVERIFY(!Config::canBeApplied(brokenConfig2)); const ConfigPtr nulllConfig; QVERIFY(!Config::canBeApplied(nulllConfig)); } void testScreenConfig::supportedFeatures() { ConfigPtr config = getConfig(); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); config->setSupportedFeatures(KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); config->setSupportedFeatures(KScreen::Config::Feature::None); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); config->setSupportedFeatures(KScreen::Config::Feature::PerOutputScaling | KScreen::Config::Feature::Writable); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); config->setSupportedFeatures(KScreen::Config::Feature::PerOutputScaling | KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay); QVERIFY(!config->supportedFeatures().testFlag(KScreen::Config::Feature::None)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::Writable)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PrimaryDisplay)); QVERIFY(config->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling)); } void testScreenConfig::testInvalidMode() { ModeList modes; ModePtr invalidMode = modes.value(QStringLiteral("99")); QVERIFY(invalidMode.isNull()); auto output = new KScreen::Output(); auto currentMode = output->currentMode(); QVERIFY(currentMode.isNull()); QVERIFY(!currentMode); delete output; } void testScreenConfig::testOutputPositionNormalization() { KScreen::BackendManager::instance()->setBackendArgs({{QStringLiteral("TEST_DATA"), TEST_DATA "multipleoutput.json"}}); const ConfigPtr config = getConfig(); QVERIFY(!config.isNull()); auto left = config->outputs().first(); auto right = config->outputs().last(); QVERIFY(!left.isNull()); QVERIFY(!right.isNull()); left->setPos(QPoint(-5000, 700)); right->setPos(QPoint(-3720, 666)); QCOMPARE(left->pos(), QPoint(-5000, 700)); QCOMPARE(right->pos(), QPoint(-3720, 666)); // start a set operation to fix up the positions { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(left->pos(), QPoint(0, 34)); QCOMPARE(right->pos(), QPoint(1280, 0)); // make sure it doesn't touch a valid config { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(left->pos(), QPoint(0, 34)); QCOMPARE(right->pos(), QPoint(1280, 0)); // positions of single outputs should be at 0, 0 left->setEnabled(false); { auto setop = new SetConfigOperation(config); setop->exec(); } QCOMPARE(right->pos(), QPoint()); } QTEST_MAIN(testScreenConfig) #include "testscreenconfig.moc" 07070100000027000081A40000000000000000000000016638555E000005B6000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/autotests/testxrandr.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define QT_GUI_LIB #include <QObject> #include <QtTest> #include "../src/config.h" #include "../src/getconfigoperation.h" #include "../src/output.h" using namespace KScreen; class testXRandR : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void singleOutput(); private: QProcess m_process; }; void testXRandR::initTestCase() { } void testXRandR::singleOutput() { qputenv("KSCREEN_BACKEND", "XRandR"); GetConfigOperation *op = new GetConfigOperation(); QVERIFY(op->exec()); const ConfigPtr config = op->config(); if (!config) { QSKIP("XRandR X extension is not available", SkipAll); } QCOMPARE(config->outputs().count(), 1); const OutputPtr output = config->outputs().take(327); QCOMPARE(output->name(), QString("default")); QCOMPARE(output->type(), Output::Unknown); QCOMPARE(output->modes().count(), 15); QCOMPARE(output->pos(), QPoint(0, 0)); QCOMPARE(output->currentModeId(), QLatin1String("338")); QCOMPARE(output->rotation(), Output::None); QCOMPARE(output->isConnected(), true); QCOMPARE(output->isEnabled(), true); QCOMPARE(output->isPrimary(), false); QVERIFY2(output->clones().isEmpty(), "In singleOutput is impossible to have clones"); } QTEST_MAIN(testXRandR) #include "testxrandr.moc" 07070100000028000041ED0000000000000000000000076638555E00000000000000000000000000000000000000000000003700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends07070100000029000081A40000000000000000000000016638555E0000012C000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/CMakeLists.txtadd_subdirectory(fake) add_subdirectory(qscreen) add_subdirectory(kwayland) if(${XCB_RANDR_FOUND}) message(STATUS "Will build xrandr backend.") add_subdirectory(xrandr) add_subdirectory(xrandr1.1) else() message(STATUS "Not building xrandr backend, no XCB_RANDR_FOUND set.") endif() 0707010000002A000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake0707010000002B000081A40000000000000000000000016638555E000002D4000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake/CMakeLists.txtinclude_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BUILD_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) set(fake_SRCS fake.cpp parser.cpp fake.h parser.h ) qt_add_dbus_adaptor(fake_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.FakeBackend.xml fake.h Fake ) add_library(KSC_Fake MODULE ${fake_SRCS}) set_target_properties(KSC_Fake PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kscreen") set_target_properties(KSC_Fake PROPERTIES PREFIX "") target_link_libraries(KSC_Fake Qt::Core Qt::DBus KF5::Screen ) install(TARGETS KSC_Fake DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kscreen/) 0707010000002C000081A40000000000000000000000016638555E000011DF000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake/fake.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "fake.h" #include "parser.h" #include "edid.h" #include <output.h> #include <stdlib.h> #include <QFile> #include <QTimer> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QDBusConnection> #include "fakebackendadaptor.h" using namespace KScreen; Q_LOGGING_CATEGORY(KSCREEN_FAKE, "kscreen.fake") Fake::Fake() : KScreen::AbstractBackend() { QLoggingCategory::setFilterRules(QStringLiteral("kscreen.fake.debug = true")); if (qgetenv("KSCREEN_BACKEND_INPROCESS") != QByteArray("1")) { QTimer::singleShot(0, this, &Fake::delayedInit); } } void Fake::init(const QVariantMap &arguments) { if (!mConfig.isNull()) { mConfig.clear(); } mConfigFile = arguments[QStringLiteral("TEST_DATA")].toString(); if (arguments.contains(QStringLiteral("SUPPORTED_FEATURES"))) { bool ok = false; const int features = arguments[QStringLiteral("SUPPORTED_FEATURES")].toInt(&ok); if (ok) { mSupportedFeatures = static_cast<KScreen::Config::Features>(features); } } qCDebug(KSCREEN_FAKE) << "Fake profile file:" << mConfigFile << "features" << mSupportedFeatures; } void Fake::delayedInit() { new FakeBackendAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/fake"), this); } Fake::~Fake() { } QString Fake::name() const { return QStringLiteral("Fake"); } QString Fake::serviceName() const { return QStringLiteral("org.kde.KScreen.Backend.Fake"); } ConfigPtr Fake::config() const { if (mConfig.isNull()) { mConfig = Parser::fromJson(mConfigFile); if (mConfig) { mConfig->setSupportedFeatures(mSupportedFeatures); } } return mConfig; } void Fake::setConfig(const ConfigPtr &config) { qCDebug(KSCREEN_FAKE) << "set config" << config->outputs(); mConfig = config->clone(); Q_EMIT configChanged(mConfig); } bool Fake::isValid() const { return true; } QByteArray Fake::edid(int outputId) const { Q_UNUSED(outputId); QFile file(mConfigFile); file.open(QIODevice::ReadOnly); const QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll()); const QJsonObject json = jsonDoc.object(); const QJsonArray outputs = json[QStringLiteral("outputs")].toArray(); for (const QJsonValue &value : outputs) { const QVariantMap output = value.toObject().toVariantMap(); if (output[QStringLiteral("id")].toInt() != outputId) { continue; } return QByteArray::fromBase64(output[QStringLiteral("edid")].toByteArray()); } return QByteArray(); } void Fake::setConnected(int outputId, bool connected) { KScreen::OutputPtr output = config()->output(outputId); if (output->isConnected() == connected) { return; } output->setConnected(connected); qCDebug(KSCREEN_FAKE) << "emitting configChanged in Fake"; Q_EMIT configChanged(mConfig); } void Fake::setEnabled(int outputId, bool enabled) { KScreen::OutputPtr output = config()->output(outputId); if (output->isEnabled() == enabled) { return; } output->setEnabled(enabled); Q_EMIT configChanged(mConfig); } void Fake::setPrimary(int outputId, bool primary) { KScreen::OutputPtr output = config()->output(outputId); if (!output || output->isPrimary() == primary) { return; } mConfig->setPrimaryOutput(output); Q_EMIT configChanged(mConfig); } void Fake::setCurrentModeId(int outputId, const QString &modeId) { KScreen::OutputPtr output = config()->output(outputId); if (output->currentModeId() == modeId) { return; } output->setCurrentModeId(modeId); Q_EMIT configChanged(mConfig); } void Fake::setRotation(int outputId, int rotation) { KScreen::OutputPtr output = config()->output(outputId); const KScreen::Output::Rotation rot = static_cast<KScreen::Output::Rotation>(rotation); if (output->rotation() == rot) { return; } output->setRotation(rot); Q_EMIT configChanged(mConfig); } void Fake::addOutput(int outputId, const QString &name) { KScreen::OutputPtr output(new KScreen::Output); output->setId(outputId); output->setName(name); mConfig->addOutput(output); Q_EMIT configChanged(mConfig); } void Fake::removeOutput(int outputId) { mConfig->removeOutput(outputId); Q_EMIT configChanged(mConfig); } 0707010000002D000081A40000000000000000000000016638555E00000581000000000000000000000000000000000000004300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake/fake.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef FAKE_BACKEND_H #define FAKE_BACKEND_H #include "abstractbackend.h" #include "config.h" #include <QLoggingCategory> #include <QObject> class Fake : public KScreen::AbstractBackend { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kf5.kscreen.backends.fake") public: explicit Fake(); ~Fake() override; void init(const QVariantMap &arguments) override; QString name() const override; QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; QByteArray edid(int outputId) const override; bool isValid() const override; void setConnected(int outputId, bool connected); void setEnabled(int outputId, bool enabled); void setPrimary(int outputId, bool primary); void setCurrentModeId(int outputId, const QString &modeId); void setRotation(int outputId, int rotation); void addOutput(int outputId, const QString &name); void removeOutput(int outputId); private Q_SLOTS: void delayedInit(); private: QString mConfigFile; KScreen::Config::Features mSupportedFeatures = KScreen::Config::Feature::None; mutable KScreen::ConfigPtr mConfig; }; Q_DECLARE_LOGGING_CATEGORY(KSCREEN_FAKE) #endif // FAKE_BACKEND_H 0707010000002E000081A40000000000000000000000016638555E000021EB000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake/parser.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "parser.h" #include "fake.h" #include "config.h" #include "mode.h" #include "output.h" #include "screen.h" #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QLoggingCategory> #include <QMetaObject> #include <QMetaProperty> using namespace KScreen; ConfigPtr Parser::fromJson(const QByteArray &data) { ConfigPtr config(new Config); const QJsonObject json = QJsonDocument::fromJson(data).object(); ScreenPtr screen = Parser::screenFromJson(json[QStringLiteral("screen")].toObject().toVariantMap()); config->setScreen(screen); const QVariantList outputs = json[QStringLiteral("outputs")].toArray().toVariantList(); if (outputs.isEmpty()) { return config; } OutputList outputList; for (const QVariant &value : outputs) { const OutputPtr output = Parser::outputFromJson(value.toMap()); outputList.insert(output->id(), output); } config->setOutputs(outputList); return config; } ConfigPtr Parser::fromJson(const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { qWarning() << file.errorString(); qWarning() << "File: " << path; return ConfigPtr(); } return Parser::fromJson(file.readAll()); } ScreenPtr Parser::screenFromJson(const QVariantMap &data) { ScreenPtr screen(new Screen); screen->setId(data[QStringLiteral("id")].toInt()); screen->setMinSize(Parser::sizeFromJson(data[QStringLiteral("minSize")].toMap())); screen->setMaxSize(Parser::sizeFromJson(data[QStringLiteral("maxSize")].toMap())); screen->setCurrentSize(Parser::sizeFromJson(data[QStringLiteral("currentSize")].toMap())); screen->setMaxActiveOutputsCount(data[QStringLiteral("maxActiveOutputsCount")].toInt()); return screen; } void Parser::qvariant2qobject(const QVariantMap &variant, QObject *object) { const QMetaObject *metaObject = object->metaObject(); for (QVariantMap::const_iterator iter = variant.begin(); iter != variant.end(); ++iter) { const int propertyIndex = metaObject->indexOfProperty(qPrintable(iter.key())); if (propertyIndex == -1) { // qWarning() << "Skipping non-existent property" << iter.key(); continue; } const QMetaProperty metaProperty = metaObject->property(propertyIndex); if (!metaProperty.isWritable()) { // qWarning() << "Skipping read-only property" << iter.key(); continue; } const QVariant property = object->property(iter.key().toLatin1().constData()); Q_ASSERT(property.isValid()); if (property.isValid()) { QVariant value = iter.value(); if (value.canConvert(property.type())) { value.convert(property.type()); object->setProperty(iter.key().toLatin1().constData(), value); } else if (QLatin1String("QVariant") == QLatin1String(property.typeName())) { object->setProperty(iter.key().toLatin1().constData(), value); } } } } OutputPtr Parser::outputFromJson(QMap<QString, QVariant> map) { OutputPtr output(new Output); output->setId(map[QStringLiteral("id")].toInt()); QStringList preferredModes; const QVariantList prefModes = map[QStringLiteral("preferredModes")].toList(); for (const QVariant &mode : prefModes) { preferredModes.append(mode.toString()); } output->setPreferredModes(preferredModes); map.remove(QStringLiteral("preferredModes")); ModeList modelist; const QVariantList modes = map[QStringLiteral("modes")].toList(); for (const QVariant &modeValue : modes) { const ModePtr mode = Parser::modeFromJson(modeValue); modelist.insert(mode->id(), mode); } output->setModes(modelist); map.remove(QStringLiteral("modes")); if (map.contains(QStringLiteral("clones"))) { QList<int> clones; for (const QVariant &id : map[QStringLiteral("clones")].toList()) { clones.append(id.toInt()); } output->setClones(clones); map.remove(QStringLiteral("clones")); } const QByteArray type = map[QStringLiteral("type")].toByteArray().toUpper(); if (type.contains("LVDS") || type.contains("EDP") || type.contains("IDP") || type.contains("7")) { output->setType(Output::Panel); } else if (type.contains("VGA")) { output->setType(Output::VGA); } else if (type.contains("DVI")) { output->setType(Output::DVI); } else if (type.contains("DVI-I")) { output->setType(Output::DVII); } else if (type.contains("DVI-A")) { output->setType(Output::DVIA); } else if (type.contains("DVI-D")) { output->setType(Output::DVID); } else if (type.contains("HDMI") || type.contains("6")) { output->setType(Output::HDMI); } else if (type.contains("Panel")) { output->setType(Output::Panel); } else if (type.contains("TV")) { output->setType(Output::TV); } else if (type.contains("TV-Composite")) { output->setType(Output::TVComposite); } else if (type.contains("TV-SVideo")) { output->setType(Output::TVSVideo); } else if (type.contains("TV-Component")) { output->setType(Output::TVComponent); } else if (type.contains("TV-SCART")) { output->setType(Output::TVSCART); } else if (type.contains("TV-C4")) { output->setType(Output::TVC4); } else if (type.contains("DisplayPort") || type.contains("14")) { output->setType(Output::DisplayPort); } else if (type.contains("Unknown")) { output->setType(Output::Unknown); } else { qCWarning(KSCREEN_FAKE) << "Output Type not translated:" << type; } map.remove(QStringLiteral("type")); if (map.contains(QStringLiteral("pos"))) { output->setPos(Parser::pointFromJson(map[QStringLiteral("pos")].toMap())); map.remove(QStringLiteral("pos")); } if (map.contains(QStringLiteral("size"))) { output->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap())); map.remove(QStringLiteral("size")); } // This is not supported in real configs; only set this value in fake test // configs and don't add logic to kscreen to set this data in real configs. if (map.contains(QStringLiteral("sizeMM"))) { output->setSizeMm(Parser::sizeFromJson(map[QStringLiteral("sizeMM")].toMap())); map.remove(QStringLiteral("sizeMM")); } auto scale = QStringLiteral("scale"); if (map.contains(scale)) { qDebug() << "Scale found:" << map[scale].toReal(); output->setScale(map[scale].toReal()); map.remove(scale); } // the deprecated "primary" property may exist for compatibility, but "priority" should override it whenever present. if (map.contains(QStringLiteral("primary"))) { output->setPriority(map[QStringLiteral("primary")].toBool() ? 1 : 2); map.remove(QStringLiteral("primary")); } if (map.contains(QStringLiteral("priority"))) { output->setPriority(map[QStringLiteral("priority")].toUInt()); map.remove(QStringLiteral("priority")); } // Remove some extra properties that we do not want or need special treatment map.remove(QStringLiteral("edid")); Parser::qvariant2qobject(map, output.data()); return output; } ModePtr Parser::modeFromJson(const QVariant &data) { const QVariantMap map = data.toMap(); ModePtr mode(new Mode); Parser::qvariant2qobject(map, mode.data()); mode->setSize(Parser::sizeFromJson(map[QStringLiteral("size")].toMap())); return mode; } QSize Parser::sizeFromJson(const QVariant &data) { const QVariantMap map = data.toMap(); QSize size; size.setWidth(map[QStringLiteral("width")].toInt()); size.setHeight(map[QStringLiteral("height")].toInt()); return size; } QPoint Parser::pointFromJson(const QVariant &data) { const QVariantMap map = data.toMap(); QPoint point; point.setX(map[QStringLiteral("x")].toInt()); point.setY(map[QStringLiteral("y")].toInt()); return point; } QRect Parser::rectFromJson(const QVariant &data) { QRect rect; rect.setSize(Parser::sizeFromJson(data)); rect.setBottomLeft(Parser::pointFromJson(data)); return rect; } bool Parser::validate(const QByteArray &data) { Q_UNUSED(data); return true; } bool Parser::validate(const QString &data) { Q_UNUSED(data); return true; } 0707010000002F000081A40000000000000000000000016638555E00000421000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/fake/parser.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef PARSER_H #define PARSER_H #include <QByteArray> #include <QPoint> #include <QRect> #include <QSize> #include <QString> #include <QVariant> #include "types.h" class Parser { public: static KScreen::ConfigPtr fromJson(const QByteArray &data); static KScreen::ConfigPtr fromJson(const QString &path); static bool validate(const QByteArray &data); static bool validate(const QString &data); private: static void qvariant2qobject(const QVariantMap &variant, QObject *object); static KScreen::ScreenPtr screenFromJson(const QMap<QString, QVariant> &data); static KScreen::OutputPtr outputFromJson(QMap<QString, QVariant> data /* sic */); static KScreen::ModePtr modeFromJson(const QVariant &data); static QSize sizeFromJson(const QVariant &data); static QRect rectFromJson(const QVariant &data); static QPoint pointFromJson(const QVariant &data); }; #endif // PARSER_H 07070100000030000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000004000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland07070100000031000081A40000000000000000000000016638555E00000814000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/CMakeLists.txt set(wayland_SRCS waylandbackend.cpp waylandconfig.cpp waylandoutputdevice.cpp waylandoutputmanagement.cpp waylandoutputdevicemode.cpp waylandscreen.cpp ../utils.cpp ) qt_add_dbus_interface(wayland_SRCS org.kde.KWin.TabletModeManager.xml tabletmodemanager_interface) ecm_qt_declare_logging_category(wayland_SRCS HEADER kscreen_kwayland_logging.h IDENTIFIER KSCREEN_WAYLAND CATEGORY_NAME kscreen.kwayland ) add_library(KSC_KWayland MODULE) if (QT_MAJOR_VERSION EQUAL "5") ecm_add_qtwayland_client_protocol(wayland_SRCS PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml BASENAME kde-output-device-v2 ) ecm_add_qtwayland_client_protocol(wayland_SRCS PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml BASENAME kde-output-management-v2 ) ecm_add_qtwayland_client_protocol(wayland_SRCS PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-order-v1.xml BASENAME kde-output-order-v1 ) else() qt6_generate_wayland_protocol_client_sources(KSC_KWayland FILES ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-order-v1.xml ) endif() target_sources(KSC_KWayland PRIVATE ${wayland_SRCS}) set_target_properties(KSC_KWayland PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kscreen") set_target_properties(KSC_KWayland PROPERTIES PREFIX "") target_link_libraries(KSC_KWayland Qt::Core Qt::DBus Qt::Gui KF5::ConfigCore KF5::Screen KF5::WaylandClient Wayland::Client ) install(TARGETS KSC_KWayland DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kscreen/) 07070100000032000081A40000000000000000000000016638555E000008D2000000000000000000000000000000000000004A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/README.md# Design of libkscreen's Wayland backend This backend uses KWayland's OutputManagement protocol for listing and configuring devices. This is described here. ## Listing outputs KScreen's outputs are created from KWayland::Client::OutputDevice objects, they copy the data into kscreen's Outputs, and update these objects. A list of outputs is requested from the client Registry object. ## Configuring outputs The backend asks the global OutputManagement interface for an OutputConfiguration object, then sets the changes per outputdevice on this object, and asks the compositor to apply() this configuration. For this to work, the compositor should support the Wayland org_kde_kwin_outputdevice and org_kde_kwin_outputmanagement protocols, for example through KWayland::Server classes OutputDevice, OutputManagmenent and OuputConfiguration. ## General working WaylandBackend creates a global static internal config, available through WaylandBackend::internalConfig(). WaylandConfig binds to the wl_registry callbacks and catches org_kde_kwin_outputdevice creation and destruction. It passes org_kde_kwin_outputdevice creation and removal on to WB::internalConfig() to handle its internal data representation as WaylandOutput. WaylandOutput binds to org_kde_kwin_outputdevice's callback, and gets notified of geometry and modes, including changes. WaylandOutput administrates the internal representation of these objects, and invokes the global notifier, which then runs the pointers it holds through the updateK* methods in Wayland{Screen,Output,...}. KScreen:{Screen,Output,Edid,Mode} objects are created from the internal representation as requested (usually triggered by the creation of a KScreen::Config object through KScreen::Config::current()). As with other backends, the objects which are handed out to the lib's user are expected to be deleted by the user, the backend only takes ownership of its internal data representation objects. ## Note about scope of output ids The ids of the outputdevices are internal to the wayland backend. The id is generated in the wayland backend, and does not match kwin's output ids. Do not try to read kwin's config from here. <sebas@kde.org> 07070100000033000081A40000000000000000000000016638555E00000246000000000000000000000000000000000000006300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/org.kde.KWin.TabletModeManager.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.kde.KWin.TabletModeManager"> <property name="tabletModeAvailable" type="b" access="read"/> <property name="tabletMode" type="b" access="read"/> <signal name="tabletModeAvailableChanged"> <arg name="tabletModeAvailable" type="b" direction="out"/> </signal> <signal name="tabletModeChanged"> <arg name="tabletMode" type="b" direction="out"/> </signal> </interface> </node> 07070100000034000081A40000000000000000000000016638555E00000BB5000000000000000000000000000000000000005300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandbackend.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandbackend.h" #include "waylandconfig.h" #include "waylandoutputdevice.h" #include "kscreen_kwayland_logging.h" #include <configmonitor.h> #include <mode.h> #include <output.h> #include <QProcess> #include <QSettings> #include <QStandardPaths> #include <KConfig> #include <KConfigGroup> using namespace KScreen; WaylandBackend::WaylandBackend() : KScreen::AbstractBackend() , m_internalConfig(new WaylandConfig(this)) { qCDebug(KSCREEN_WAYLAND) << "Loading Wayland backend."; connect(m_internalConfig, &WaylandConfig::configChanged, this, [this]() { const auto newConfig = m_internalConfig->currentConfig(); KConfig cfg(QStringLiteral("kdeglobals")); KConfigGroup kscreenGroup = cfg.group("KScreen"); const bool xwaylandClientsScale = kscreenGroup.readEntry("XwaylandClientsScale", true); KConfig kwinCfg(QStringLiteral("kwinrc")); KConfigGroup xwaylandGroup = kwinCfg.group("Xwayland"); if (xwaylandClientsScale) { qreal scaleFactor = 1; const auto outputs = newConfig->outputs(); for (auto output : outputs) { if (output->isEnabled()) { scaleFactor = std::max(scaleFactor, output->scale()); } } xwaylandGroup.writeEntry("Scale", scaleFactor, KConfig::Notify); } else { xwaylandGroup.deleteEntry("Scale", KConfig::Notify); } // here we rerun the fonts kcm init that does the appropriate xrdb call with the new settings QProcess::startDetached("kcminit", {"kcm_fonts"}); Q_EMIT configChanged(newConfig); }); } QString WaylandBackend::name() const { return QStringLiteral("kwayland"); } QString WaylandBackend::serviceName() const { return QStringLiteral("org.kde.KScreen.Backend.KWayland"); } ConfigPtr WaylandBackend::config() const { // Note: This should ONLY be called from GetConfigOperation! return m_internalConfig->currentConfig(); } void WaylandBackend::setConfig(const KScreen::ConfigPtr &newconfig) { if (!newconfig) { return; } // wait for KWin reply QEventLoop loop; connect(m_internalConfig, &WaylandConfig::configChanged, &loop, &QEventLoop::quit); m_internalConfig->applyConfig(newconfig); loop.exec(); } QByteArray WaylandBackend::edid(int outputId) const { WaylandOutputDevice *output = m_internalConfig->outputMap().value(outputId); if (!output) { return QByteArray(); } return output->edid(); } bool WaylandBackend::isValid() const { return m_internalConfig->isReady(); } 07070100000035000081A40000000000000000000000016638555E000003D9000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandbackend.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "abstractbackend.h" #include <QLoggingCategory> namespace KScreen { class WaylandConfig; class WaylandBackend : public KScreen::AbstractBackend { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kf5.kscreen.backends.kwayland") public: explicit WaylandBackend(); ~WaylandBackend() override = default; QString name() const override; QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; QByteArray edid(int outputId) const override; private: WaylandConfig *m_internalConfig; }; } Q_DECLARE_LOGGING_CATEGORY(KSCREEN_WAYLAND) 07070100000036000081A40000000000000000000000016638555E00002B03000000000000000000000000000000000000005200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandconfig.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandconfig.h" #include "kscreen_kwayland_logging.h" #include "waylandbackend.h" #include "waylandoutputdevice.h" #include "waylandoutputmanagement.h" #include "waylandscreen.h" #include "tabletmodemanager_interface.h" #include <QThread> #include <QTimer> #include <configmonitor.h> #include <mode.h> #include <output.h> #include <KWayland/Client/connection_thread.h> #include <KWayland/Client/event_queue.h> #include <KWayland/Client/registry.h> #include <utility> using namespace KScreen; WaylandConfig::WaylandConfig(QObject *parent) : QObject(parent) , m_outputManagement(nullptr) , m_registryInitialized(false) , m_blockSignals(true) , m_kscreenConfig(new Config) , m_kscreenPendingConfig(nullptr) , m_screen(new WaylandScreen(this)) , m_tabletModeAvailable(false) , m_tabletModeEngaged(false) { initKWinTabletMode(); connect(this, &WaylandConfig::initialized, &m_syncLoop, &QEventLoop::quit); QTimer::singleShot(3000, this, [this] { if (m_syncLoop.isRunning()) { qCWarning(KSCREEN_WAYLAND) << "Connection to Wayland server timed out."; m_syncLoop.quit(); } }); initConnection(); m_syncLoop.exec(); } WaylandConfig::~WaylandConfig() { m_syncLoop.quit(); } void WaylandConfig::initKWinTabletMode() { auto *interface = new OrgKdeKWinTabletModeManagerInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin"), QDBusConnection::sessionBus(), this); if (!interface->isValid()) { m_tabletModeAvailable = false; m_tabletModeEngaged = false; return; } m_tabletModeAvailable = interface->tabletModeAvailable(); m_tabletModeEngaged = interface->tabletMode(); connect(interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeChanged, this, [this](bool tabletMode) { if (m_tabletModeEngaged == tabletMode) { return; } m_tabletModeEngaged = tabletMode; if (!m_blockSignals && m_initializingOutputs.empty()) { Q_EMIT configChanged(); } }); connect(interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeAvailableChanged, this, [this](bool available) { if (m_tabletModeAvailable == available) { return; } m_tabletModeAvailable = available; if (!m_blockSignals && m_initializingOutputs.empty()) { Q_EMIT configChanged(); } }); } void WaylandConfig::initConnection() { m_connection = KWayland::Client::ConnectionThread::fromApplication(this); setupRegistry(); } void WaylandConfig::blockSignals() { Q_ASSERT(m_blockSignals == false); m_blockSignals = true; } void WaylandConfig::unblockSignals() { Q_ASSERT(m_blockSignals == true); m_blockSignals = false; } void WaylandConfig::setupRegistry() { if (!m_connection) { return; } m_registry = new KWayland::Client::Registry(this); connect(m_registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this](const QByteArray &interface, quint32 name, quint32 version) { if (interface == WaylandOutputDevice::interface()->name) { addOutput(name, std::min(2u, version)); } if (interface == WaylandOutputManagement::interface()->name) { m_outputManagement = new WaylandOutputManagement(m_registry->registry(), name, std::min(3u, version)); } if (interface == WaylandOutputOrder::interface()->name) { m_outputOrder = std::make_unique<WaylandOutputOrder>(m_registry->registry(), name, std::min(1u, version)); connect(m_outputOrder.get(), &WaylandOutputOrder::outputOrderChanged, this, [this](const QVector<QString> &names) { bool change = false; for (const auto &output : std::as_const(m_outputMap)) { const uint32_t newIndex = names.indexOf(output->name()) + 1; change = change || output->index() != newIndex; output->setIndex(newIndex); } if (change && !m_blockSignals) { Q_EMIT configChanged(); } }); } }); connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { m_registryInitialized = true; unblockSignals(); checkInitialized(); }); m_registry->create(m_connection); m_registry->setup(); } int s_outputId = 0; void WaylandConfig::addOutput(quint32 name, quint32 version) { qCDebug(KSCREEN_WAYLAND) << "adding output" << name; auto device = new WaylandOutputDevice(++s_outputId); m_initializingOutputs << device; connect(m_registry, &KWayland::Client::Registry::interfaceRemoved, this, [name, device, this](const quint32 &interfaceName) { if (name == interfaceName) { removeOutput(device); } }); QMetaObject::Connection *const connection = new QMetaObject::Connection; *connection = connect(device, &WaylandOutputDevice::done, this, [this, connection, device]() { QObject::disconnect(*connection); delete connection; m_initializingOutputs.removeOne(device); m_outputMap.insert(device->id(), device); if (m_outputOrder) { device->setIndex(m_outputOrder->order().indexOf(device->name()) + 1); } checkInitialized(); if (m_initializingOutputs.isEmpty()) { m_screen->setOutputs(m_outputMap.values()); } if (!m_blockSignals && m_initializingOutputs.isEmpty()) { Q_EMIT configChanged(); } connect(device, &WaylandOutputDevice::done, this, [this]() { // output got update must update current config if (!m_blockSignals) { Q_EMIT configChanged(); } }); }); device->init(*m_registry, name, version); } void WaylandConfig::removeOutput(WaylandOutputDevice *output) { qCDebug(KSCREEN_WAYLAND) << "removing output" << output->name(); if (m_initializingOutputs.removeOne(output)) { // output was not yet fully initialized, just remove here and return delete output; return; } // remove the output from output mapping const auto removedOutput = m_outputMap.take(output->id()); Q_ASSERT(removedOutput == output); Q_UNUSED(removedOutput); m_screen->setOutputs(m_outputMap.values()); delete output; if (!m_blockSignals) { Q_EMIT configChanged(); } } bool WaylandConfig::isReady() const { // clang-format off return !m_blockSignals && m_registryInitialized && m_initializingOutputs.isEmpty() && m_outputMap.count() > 0 && m_outputManagement != nullptr; // clang-format on } void WaylandConfig::checkInitialized() { if (!m_initialized && isReady()) { m_initialized = true; m_screen->setOutputs(m_outputMap.values()); Q_EMIT initialized(); } } KScreen::ConfigPtr WaylandConfig::currentConfig() { m_kscreenConfig->setScreen(m_screen->toKScreenScreen(m_kscreenConfig)); const auto features = Config::Feature::Writable | Config::Feature::PerOutputScaling | Config::Feature::AutoRotation | Config::Feature::TabletMode | Config::Feature::PrimaryDisplay | Config::Feature::XwaylandScales | Config::Feature::SynchronousOutputChanges; m_kscreenConfig->setSupportedFeatures(features); m_kscreenConfig->setValid(m_connection->display()); KScreen::ScreenPtr screen = m_kscreenConfig->screen(); m_screen->updateKScreenScreen(screen); // Removing removed outputs const KScreen::OutputList outputs = m_kscreenConfig->outputs(); for (const auto &output : outputs) { if (!m_outputMap.contains(output->id())) { m_kscreenConfig->removeOutput(output->id()); } } // Add KScreen::Outputs that aren't in the list yet KScreen::OutputList kscreenOutputs = m_kscreenConfig->outputs(); QMap<OutputPtr, uint32_t> priorities; for (const auto &output : m_outputMap) { KScreen::OutputPtr kscreenOutput; if (m_kscreenConfig->outputs().contains(output->id())) { kscreenOutput = m_kscreenConfig->outputs()[output->id()]; output->updateKScreenOutput(kscreenOutput); } else { kscreenOutput = output->toKScreenOutput(); m_kscreenConfig->addOutput(kscreenOutput); } priorities[kscreenOutput] = output->index(); } m_kscreenConfig->setOutputPriorities(priorities); m_kscreenConfig->setTabletModeAvailable(m_tabletModeAvailable); m_kscreenConfig->setTabletModeEngaged(m_tabletModeEngaged); return m_kscreenConfig; } QMap<int, WaylandOutputDevice *> WaylandConfig::outputMap() const { return m_outputMap; } void WaylandConfig::tryPendingConfig() { if (!m_kscreenPendingConfig) { return; } applyConfig(m_kscreenPendingConfig); m_kscreenPendingConfig = nullptr; } WaylandOutputDevice *WaylandConfig::findOutputDevice(struct ::kde_output_device_v2 *outputdevice) const { for (WaylandOutputDevice *device : m_outputMap) { if (device->object() == outputdevice) { return device; } } return nullptr; } void WaylandConfig::applyConfig(const KScreen::ConfigPtr &newConfig) { using namespace KWayland::Client; newConfig->adjustPriorities(); // never trust input // Create a new configuration object auto wlConfig = m_outputManagement->createConfiguration(); bool changed = false; if (m_blockSignals) { // Last apply still pending, remember new changes and apply afterwards m_kscreenPendingConfig = newConfig; return; } for (const auto &output : newConfig->outputs()) { changed |= m_outputMap[output->id()]->setWlConfig(wlConfig, output); } if (!changed) { return; } // We now block changes in order to compress events while the compositor is doing its thing // once it's done or failed, we'll trigger configChanged() only once, and not per individual // property change. connect(wlConfig, &WaylandOutputConfiguration::applied, this, [this, wlConfig] { wlConfig->deleteLater(); unblockSignals(); Q_EMIT configChanged(); tryPendingConfig(); }); connect(wlConfig, &WaylandOutputConfiguration::failed, this, [this, wlConfig] { wlConfig->deleteLater(); unblockSignals(); Q_EMIT configChanged(); tryPendingConfig(); }); // Now block signals and ask the compositor to apply the changes. blockSignals(); wlConfig->apply(); } 07070100000037000081A40000000000000000000000016638555E00000B14000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandconfig.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "abstractbackend.h" #include "config.h" #include <QDir> #include <QEventLoop> #include <QLoggingCategory> #include <QScreen> #include <QSize> #include <QSocketNotifier> struct kde_output_device_v2; namespace KWayland { namespace Client { class ConnectionThread; class EventQueue; class Registry; class OutputManagement; } } namespace KScreen { class Output; class WaylandOutputDevice; class WaylandScreen; class WaylandOutputManagement; class WaylandOutputOrder; /** * @class WaylandConfig * * This class holds the basic skeleton of the configuration and takes care of * fetching the information from the Wayland server and synchronizing the * configuration out to the "clients" that receive the config from the backend. * We initialize a wayland connection, using a threaded event queue when * querying the wayland server for data. * Initially, the creation of a WaylandConfig blocks until all data has been * received, signalled by the initialized() signal. This means that the * wayland client has received information about all interfaces, and that all * outputs are completely initialized. From then on, we properly notifyUpdate(). */ class WaylandConfig : public QObject { Q_OBJECT public: explicit WaylandConfig(QObject *parent = nullptr); ~WaylandConfig() override; KScreen::ConfigPtr currentConfig(); QMap<int, WaylandOutputDevice *> outputMap() const; void applyConfig(const KScreen::ConfigPtr &newConfig); WaylandOutputDevice *findOutputDevice(struct ::kde_output_device_v2 *outputdevice) const; bool isReady() const; Q_SIGNALS: void configChanged(); void initialized(); private: void setupRegistry(); void checkInitialized(); void disconnected(); void initKWinTabletMode(); void initConnection(); void addOutput(quint32 name, quint32 version); void removeOutput(WaylandOutputDevice *output); void blockSignals(); void unblockSignals(); void tryPendingConfig(); KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Registry *m_registry; WaylandOutputManagement *m_outputManagement = nullptr; std::unique_ptr<WaylandOutputOrder> m_outputOrder; // KWayland names as keys QMap<int, WaylandOutputDevice *> m_outputMap; // KWayland names QList<WaylandOutputDevice *> m_initializingOutputs; int m_lastOutputId = -1; bool m_registryInitialized; bool m_blockSignals; QEventLoop m_syncLoop; KScreen::ConfigPtr m_kscreenConfig; KScreen::ConfigPtr m_kscreenPendingConfig; WaylandScreen *m_screen; bool m_tabletModeAvailable; bool m_tabletModeEngaged; bool m_initialized = false; }; } 07070100000038000081A40000000000000000000000016638555E00002D4C000000000000000000000000000000000000005800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputdevice.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandoutputdevice.h" #include "waylandoutputmanagement.h" #include "kscreen_kwayland_logging.h" #include "../utils.h" #include <mode.h> #include <output.h> #include <wayland-server-protocol.h> #include <utility> using namespace KScreen; WaylandOutputDevice::WaylandOutputDevice(int id) : QObject() , kde_output_device_v2() , m_id(id) { } WaylandOutputDevice::~WaylandOutputDevice() { qDeleteAll(m_modes); kde_output_device_v2_destroy(object()); } void WaylandOutputDevice::kde_output_device_v2_geometry(int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const QString &make, const QString &model, int32_t transform) { m_pos = QPoint(x, y); m_physicalSize = QSize(physical_width, physical_height); m_subpixel = subpixel; m_manufacturer = make; m_model = model; m_transform = transform; } void WaylandOutputDevice::kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode) { auto m = WaylandOutputDeviceMode::get(mode); if (*m == *m_mode) { // unchanged return; } m_mode = m; } void WaylandOutputDevice::kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode) { WaylandOutputDeviceMode *m = new WaylandOutputDeviceMode(mode); // last mode sent is the current one m_mode = m; m_modes.append(m); connect(m, &WaylandOutputDeviceMode::removed, this, [this, m]() { m_modes.removeOne(m); if (m_mode == m) { if (!m_modes.isEmpty()) { m_mode = m_modes.first(); } else { // was last mode qFatal("KWaylandBackend: no output modes available anymore, this seems like a compositor bug"); } } delete m; }); } OutputPtr WaylandOutputDevice::toKScreenOutput() { OutputPtr output(new Output()); output->setId(m_id); updateKScreenOutput(output); return output; } Output::Rotation toKScreenRotation(int32_t transform) { switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: return Output::None; case WL_OUTPUT_TRANSFORM_90: return Output::Left; case WL_OUTPUT_TRANSFORM_180: return Output::Inverted; case WL_OUTPUT_TRANSFORM_270: return Output::Right; case WL_OUTPUT_TRANSFORM_FLIPPED: qCWarning(KSCREEN_WAYLAND) << "flipped transform is unsupported by kscreen"; return Output::None; case WL_OUTPUT_TRANSFORM_FLIPPED_90: qCWarning(KSCREEN_WAYLAND) << "flipped-90 transform is unsupported by kscreen"; return Output::Left; case WL_OUTPUT_TRANSFORM_FLIPPED_180: qCWarning(KSCREEN_WAYLAND) << "flipped-180 transform is unsupported by kscreen"; return Output::Inverted; case WL_OUTPUT_TRANSFORM_FLIPPED_270: qCWarning(KSCREEN_WAYLAND) << "flipped-270 transform is unsupported by kscreen"; return Output::Right; default: Q_UNREACHABLE(); } } wl_output_transform toKWaylandTransform(const Output::Rotation rotation) { switch (rotation) { case Output::None: return WL_OUTPUT_TRANSFORM_NORMAL; case Output::Left: return WL_OUTPUT_TRANSFORM_90; case Output::Inverted: return WL_OUTPUT_TRANSFORM_180; case Output::Right: return WL_OUTPUT_TRANSFORM_270; default: Q_UNREACHABLE(); } } void KScreen::WaylandOutputDevice::updateKScreenModes(OutputPtr &output) { ModeList modeList; QStringList preferredModeIds; QString currentModeId = QStringLiteral("-1"); int modeId = 0; for (const WaylandOutputDeviceMode *wlMode : std::as_const(m_modes)) { ModePtr mode(new Mode()); const QString modeIdStr = QString::number(modeId); // KWayland gives the refresh rate as int in mHz mode->setId(modeIdStr); mode->setRefreshRate(wlMode->refreshRate() / 1000.0); mode->setSize(wlMode->size()); mode->setName(modeName(wlMode)); if (m_mode == wlMode) { currentModeId = modeIdStr; } if (wlMode->preferred()) { preferredModeIds << modeIdStr; } // Add to the modelist which gets set on the output modeList[modeIdStr] = mode; modeId++; } output->setCurrentModeId(currentModeId); output->setPreferredModes(preferredModeIds); output->setModes(modeList); } void WaylandOutputDevice::updateKScreenOutput(OutputPtr &output) { // Initialize primary output output->setId(m_id); output->setEnabled(enabled()); output->setConnected(true); output->setName(name()); output->setSizeMm(m_physicalSize); output->setPos(m_pos); output->setRotation(toKScreenRotation(m_transform)); if (!output->edid()) { output->setEdid(m_edid); } QSize currentSize = m_mode->size(); output->setSize(output->isHorizontal() ? currentSize : currentSize.transposed()); output->setScale(m_factor); output->setType(Utils::guessOutputType(m_outputName, m_outputName)); output->setCapabilities(static_cast<Output::Capabilities>(static_cast<uint32_t>(m_flags))); output->setOverscan(m_overscan); output->setVrrPolicy(static_cast<Output::VrrPolicy>(m_vrr_policy)); output->setRgbRange(static_cast<Output::RgbRange>(m_rgbRange)); updateKScreenModes(output); } QString WaylandOutputDevice::modeId() const { return QString::number(m_modes.indexOf(m_mode)); } WaylandOutputDeviceMode *WaylandOutputDevice::deviceModeFromId(const int modeId) const { return m_modes.at(modeId); } bool WaylandOutputDevice::setWlConfig(WaylandOutputConfiguration *wlConfig, const KScreen::OutputPtr &output) { bool changed = false; // enabled? if (enabled() != output->isEnabled()) { changed = true; wlConfig->enable(object(), output->isEnabled()); } // position if (globalPosition() != output->pos()) { changed = true; wlConfig->position(object(), output->pos().x(), output->pos().y()); } // scale if (!qFuzzyCompare(scale(), output->scale())) { changed = true; wlConfig->scale(object(), wl_fixed_from_double(output->scale())); } // rotation if (toKScreenRotation(m_transform) != output->rotation()) { changed = true; wlConfig->transform(object(), toKWaylandTransform(output->rotation())); } // mode const ModePtr mode = output->currentMode(); if (mode->size() != pixelSize() || mode->refreshRate() != refreshRate()) { bool toIntOk; int modeId = mode->id().toInt(&toIntOk); Q_ASSERT(toIntOk); changed = true; wlConfig->mode(object(), deviceModeFromId(modeId)->object()); } // overscan if ((output->capabilities() & Output::Capability::Overscan) && overscan() != output->overscan()) { wlConfig->overscan(object(), output->overscan()); changed = true; } // vrr if ((output->capabilities() & Output::Capability::Vrr) && vrrPolicy() != static_cast<uint32_t>(output->vrrPolicy())) { wlConfig->set_vrr_policy(object(), static_cast<uint32_t>(output->vrrPolicy())); changed = true; } if ((output->capabilities() & Output::Capability::RgbRange) && rgbRange() != static_cast<uint32_t>(output->rgbRange())) { wlConfig->set_rgb_range(object(), static_cast<uint32_t>(output->rgbRange())); changed = true; } if (output->priority() != m_index) { changed = true; } // always send all outputs if (kde_output_configuration_v2_get_version(wlConfig->object()) >= KDE_OUTPUT_CONFIGURATION_V2_SET_PRIORITY_SINCE_VERSION) { wlConfig->set_priority(object(), output->priority()); } return changed; } QString WaylandOutputDevice::modeName(const WaylandOutputDeviceMode *m) const { return QString::number(m->size().width()) + QLatin1Char('x') + QString::number(m->size().height()) + QLatin1Char('@') + QString::number(qRound(m->refreshRate() / 1000.0)); } QString WaylandOutputDevice::name() const { return m_outputName; } QDebug operator<<(QDebug dbg, const WaylandOutputDevice *output) { dbg << "WaylandOutput(Id:" << output->id() << ", Name:" << QString(output->manufacturer() + QLatin1Char(' ') + output->model()) << ")"; return dbg; } void WaylandOutputDevice::setIndex(uint32_t index) { m_index = index; } uint32_t WaylandOutputDevice::index() const { return m_index; } void WaylandOutputDevice::kde_output_device_v2_done() { Q_EMIT done(); } void WaylandOutputDevice::kde_output_device_v2_scale(wl_fixed_t factor) { const double factorAsDouble = wl_fixed_to_double(factor); // the fractional scaling protocol only speaks in unit of 120ths // using the same scale throughout makes that simpler // this also eliminates most loss from wl_fixed m_factor = std::round(factorAsDouble * 120) / 120; } void WaylandOutputDevice::kde_output_device_v2_edid(const QString &edid) { m_edid = QByteArray::fromBase64(edid.toUtf8()); } void WaylandOutputDevice::kde_output_device_v2_enabled(int32_t enabled) { m_enabled = enabled; } void WaylandOutputDevice::kde_output_device_v2_uuid(const QString &uuid) { m_uuid = uuid; } void WaylandOutputDevice::kde_output_device_v2_serial_number(const QString &serialNumber) { m_serialNumber = serialNumber; } void WaylandOutputDevice::kde_output_device_v2_eisa_id(const QString &eisaId) { m_eisaId = eisaId; } void WaylandOutputDevice::kde_output_device_v2_capabilities(uint32_t flags) { m_flags = flags; } void WaylandOutputDevice::kde_output_device_v2_overscan(uint32_t overscan) { m_overscan = overscan; } void WaylandOutputDevice::kde_output_device_v2_vrr_policy(uint32_t vrr_policy) { m_vrr_policy = vrr_policy; } void WaylandOutputDevice::kde_output_device_v2_rgb_range(uint32_t rgb_range) { m_rgbRange = rgb_range; } void WaylandOutputDevice::kde_output_device_v2_name(const QString &outputName) { m_outputName = outputName; } QByteArray WaylandOutputDevice::edid() const { return m_edid; } bool WaylandOutputDevice::enabled() const { return m_enabled; } int WaylandOutputDevice::id() const { return m_id; } qreal WaylandOutputDevice::scale() const { return m_factor; } QString WaylandOutputDevice::manufacturer() const { return m_manufacturer; } QString WaylandOutputDevice::model() const { return m_model; } QPoint WaylandOutputDevice::globalPosition() const { return m_pos; } QSize WaylandOutputDevice::pixelSize() const { return m_mode->size(); } int WaylandOutputDevice::refreshRate() const { return m_mode->refreshRate(); } uint32_t WaylandOutputDevice::vrrPolicy() const { return m_vrr_policy; } uint32_t WaylandOutputDevice::overscan() const { return m_overscan; } uint32_t WaylandOutputDevice::capabilities() const { return m_flags; } uint32_t WaylandOutputDevice::rgbRange() const { return m_rgbRange; } 07070100000039000081A40000000000000000000000016638555E00000ED9000000000000000000000000000000000000005600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputdevice.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef WAYLANDOUTPUTDEVICE_H #define WAYLANDOUTPUTDEVICE_H #include "waylandoutputdevicemode.h" #include "qwayland-kde-output-device-v2.h" #include "kscreen_export.h" #include "types.h" #include <QPoint> #include <QSize> namespace KScreen { class WaylandOutputConfiguration; class WaylandOutputDevice : public QObject, public QtWayland::kde_output_device_v2 { Q_OBJECT public: WaylandOutputDevice(int id); ~WaylandOutputDevice(); QByteArray edid() const; bool enabled() const; int id() const; QString name() const; QString model() const; QString manufacturer() const; qreal scale() const; QPoint globalPosition() const; QSize pixelSize() const; int refreshRate() const; uint32_t vrrPolicy() const; uint32_t overscan() const; uint32_t capabilities() const; uint32_t rgbRange() const; OutputPtr toKScreenOutput(); void updateKScreenOutput(OutputPtr &output); void updateKScreenModes(OutputPtr &output); bool isPrimary() const; void setPrimary(bool primary); void setIndex(uint32_t priority); uint32_t index() const; bool setWlConfig(WaylandOutputConfiguration *wlConfig, const KScreen::OutputPtr &output); QString modeId() const; QString uuid() const { return m_uuid; } Q_SIGNALS: void done(); protected: void kde_output_device_v2_geometry(int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const QString &make, const QString &model, int32_t transform) override; void kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode) override; void kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode) override; void kde_output_device_v2_done() override; void kde_output_device_v2_scale(wl_fixed_t factor) override; void kde_output_device_v2_edid(const QString &raw) override; void kde_output_device_v2_enabled(int32_t enabled) override; void kde_output_device_v2_uuid(const QString &uuid) override; void kde_output_device_v2_serial_number(const QString &serialNumber) override; void kde_output_device_v2_eisa_id(const QString &eisaId) override; void kde_output_device_v2_capabilities(uint32_t flags) override; void kde_output_device_v2_overscan(uint32_t overscan) override; void kde_output_device_v2_vrr_policy(uint32_t vrr_policy) override; void kde_output_device_v2_rgb_range(uint32_t rgb_range) override; void kde_output_device_v2_name(const QString &name) override; private: QString modeName(const WaylandOutputDeviceMode *m) const; WaylandOutputDeviceMode *deviceModeFromId(const int modeId) const; WaylandOutputDeviceMode *m_mode; QList<WaylandOutputDeviceMode *> m_modes; int m_id; QPoint m_pos; QSize m_physicalSize; int32_t m_subpixel; QString m_manufacturer; QString m_model; int32_t m_transform; qreal m_factor; QByteArray m_edid; int32_t m_enabled; QString m_uuid; QString m_serialNumber; QString m_outputName; QString m_eisaId; uint32_t m_flags; uint32_t m_overscan; uint32_t m_vrr_policy; uint32_t m_rgbRange; uint32_t m_index; }; } KSCREEN_EXPORT QDebug operator<<(QDebug dbg, const KScreen::WaylandOutputDevice *output); #endif // WAYLANDOUTPUTDEVICE_H 0707010000003A000081A40000000000000000000000016638555E000005FD000000000000000000000000000000000000005C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputdevicemode.cpp/* * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandoutputdevicemode.h" using namespace KScreen; WaylandOutputDeviceMode::WaylandOutputDeviceMode(struct ::kde_output_device_mode_v2 *object) : QtWayland::kde_output_device_mode_v2(object) { } WaylandOutputDeviceMode::~WaylandOutputDeviceMode() { kde_output_device_mode_v2_destroy(object()); } void WaylandOutputDeviceMode::kde_output_device_mode_v2_size(int32_t width, int32_t height) { m_size = QSize(width, height); } void WaylandOutputDeviceMode::kde_output_device_mode_v2_refresh(int32_t refresh) { m_refreshRate = refresh; } void WaylandOutputDeviceMode::kde_output_device_mode_v2_preferred() { m_preferred = true; } void WaylandOutputDeviceMode::kde_output_device_mode_v2_removed() { Q_EMIT removed(); } int WaylandOutputDeviceMode::refreshRate() const { return m_refreshRate; } QSize WaylandOutputDeviceMode::size() const { return m_size; } bool WaylandOutputDeviceMode::preferred() const { return m_preferred; } bool WaylandOutputDeviceMode::operator==(const WaylandOutputDeviceMode &other) { return m_size == other.m_size && m_refreshRate == other.m_refreshRate && m_preferred == other.m_preferred; } WaylandOutputDeviceMode *WaylandOutputDeviceMode::get(struct ::kde_output_device_mode_v2 *object) { auto mode = QtWayland::kde_output_device_mode_v2::fromObject(object); return static_cast<WaylandOutputDeviceMode *>(mode); } 0707010000003B000081A40000000000000000000000016638555E000004A8000000000000000000000000000000000000005A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputdevicemode.h/* * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef WAYLANDOUTPUTDEVICEMODE_H #define WAYLANDOUTPUTDEVICEMODE_H #include "qwayland-kde-output-device-v2.h" #include <QObject> #include <QSize> namespace KScreen { class WaylandOutputDeviceMode : public QObject, public QtWayland::kde_output_device_mode_v2 { Q_OBJECT public: WaylandOutputDeviceMode(struct ::kde_output_device_mode_v2 *object); ~WaylandOutputDeviceMode() override; int refreshRate() const; QSize size() const; bool preferred() const; bool operator==(const WaylandOutputDeviceMode &other); static WaylandOutputDeviceMode *get(struct ::kde_output_device_mode_v2 *object); Q_SIGNALS: void removed(); protected: void kde_output_device_mode_v2_size(int32_t width, int32_t height) override; void kde_output_device_mode_v2_refresh(int32_t refresh) override; void kde_output_device_mode_v2_preferred() override; void kde_output_device_mode_v2_removed() override; private: int m_refreshRate = 60000; QSize m_size; bool m_preferred = false; }; } #endif // WAYLANDOUTPUTDEVICEMODE_H 0707010000003C000081A40000000000000000000000016638555E0000063A000000000000000000000000000000000000005C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputmanagement.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandoutputmanagement.h" #include <QDebug> namespace KScreen { WaylandOutputManagement::WaylandOutputManagement(struct ::wl_registry *registry, int id, int version) : QObject() , QtWayland::kde_output_management_v2(registry, id, version) { } WaylandOutputConfiguration *WaylandOutputManagement::createConfiguration() { return new WaylandOutputConfiguration(create_configuration()); } WaylandOutputConfiguration::WaylandOutputConfiguration(struct ::kde_output_configuration_v2 *object) : QObject() , QtWayland::kde_output_configuration_v2() { init(object); } void WaylandOutputConfiguration::kde_output_configuration_v2_applied() { Q_EMIT applied(); } void WaylandOutputConfiguration::kde_output_configuration_v2_failed() { Q_EMIT failed(); } WaylandOutputOrder::WaylandOutputOrder(struct ::wl_registry *registry, int id, int version) : QtWayland::kde_output_order_v1(registry, id, version) { } WaylandOutputOrder::~WaylandOutputOrder() { } QVector<QString> WaylandOutputOrder::order() const { return m_outputOrder; } void WaylandOutputOrder::kde_output_order_v1_output(const QString &output_name) { m_pendingOutputOrder.push_back(output_name); } void WaylandOutputOrder::kde_output_order_v1_done() { m_outputOrder = m_pendingOutputOrder; Q_EMIT outputOrderChanged(m_pendingOutputOrder); m_pendingOutputOrder.clear(); } } 0707010000003D000081A40000000000000000000000016638555E00000682000000000000000000000000000000000000005A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandoutputmanagement.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef WAYLANDOUTPUTMANAGEMENT_H #define WAYLANDOUTPUTMANAGEMENT_H #include "qwayland-kde-output-management-v2.h" #include "qwayland-kde-output-order-v1.h" #include <QObject> #include <QSize> namespace KScreen { class WaylandConfig; class WaylandOutputDevice; class WaylandOutputConfiguration : public QObject, public QtWayland::kde_output_configuration_v2 { Q_OBJECT public: WaylandOutputConfiguration(struct ::kde_output_configuration_v2 *object); Q_SIGNALS: void applied(); void failed(); protected: void kde_output_configuration_v2_applied() override; void kde_output_configuration_v2_failed() override; }; class WaylandOutputManagement : public QObject, public QtWayland::kde_output_management_v2 { Q_OBJECT public: WaylandOutputManagement(struct ::wl_registry *registry, int id, int version); WaylandOutputConfiguration *createConfiguration(); }; class WaylandOutputOrder : public QObject, public QtWayland::kde_output_order_v1 { Q_OBJECT public: WaylandOutputOrder(struct ::wl_registry *registry, int id, int version); ~WaylandOutputOrder(); QVector<QString> order() const; Q_SIGNALS: void outputOrderChanged(const QVector<QString> &outputs); private: void kde_output_order_v1_output(const QString &output_name) override; void kde_output_order_v1_done() override; QVector<QString> m_outputOrder; QVector<QString> m_pendingOutputOrder; }; } #endif // WAYLANDOUTPUTMANAGEMENT_H 0707010000003E000081A40000000000000000000000016638555E000004CD000000000000000000000000000000000000005200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandscreen.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandscreen.h" #include "waylandconfig.h" #include "waylandoutputdevice.h" #include <mode.h> #include <screen.h> using namespace KScreen; WaylandScreen::WaylandScreen(WaylandConfig *config) : QObject(config) , m_outputCount(0) { } ScreenPtr WaylandScreen::toKScreenScreen(KScreen::ConfigPtr &parent) const { Q_UNUSED(parent); KScreen::ScreenPtr kscreenScreen(new KScreen::Screen); updateKScreenScreen(kscreenScreen); return kscreenScreen; } void WaylandScreen::setOutputs(const QList<WaylandOutputDevice *> &outputs) { m_outputCount = outputs.count(); QRect r; for (const auto *out : outputs) { if (out->enabled()) { r |= QRect(out->globalPosition(), out->pixelSize() / out->scale()); } } m_size = r.size(); } void WaylandScreen::updateKScreenScreen(KScreen::ScreenPtr &screen) const { screen->setMinSize(QSize(0, 0)); // 64000^2 should be enough for everyone. screen->setMaxSize(QSize(64000, 64000)); screen->setCurrentSize(m_size); screen->setMaxActiveOutputsCount(m_outputCount); } 0707010000003F000081A40000000000000000000000016638555E000002AC000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/kwayland/waylandscreen.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "types.h" #include <QObject> #include <QSize> namespace KScreen { class WaylandConfig; class WaylandOutputDevice; class WaylandScreen : public QObject { Q_OBJECT public: explicit WaylandScreen(WaylandConfig *config); ~WaylandScreen() override = default; KScreen::ScreenPtr toKScreenScreen(KScreen::ConfigPtr &parent) const; void updateKScreenScreen(KScreen::ScreenPtr &screen) const; void setOutputs(const QList<WaylandOutputDevice *> &outputs); private: QSize m_size; int m_outputCount; }; } 07070100000040000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen07070100000041000081A40000000000000000000000016638555E0000033A000000000000000000000000000000000000004E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/CMakeLists.txtinclude_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BUILD_DIR} ) add_library(KSC_QScreen MODULE) target_sources(KSC_QScreen PRIVATE qscreenbackend.cpp qscreenconfig.cpp qscreenscreen.cpp qscreenoutput.cpp qscreenbackend.h qscreenconfig.h qscreenscreen.h qscreenoutput.h ) set_target_properties(KSC_QScreen PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kscreen") set_target_properties(KSC_QScreen PROPERTIES PREFIX "") target_link_libraries(KSC_QScreen Qt::Core Qt::Gui KF5::Screen ) if (TARGET Qt::X11Extras) target_link_libraries(KSC_QScreen Qt::X11Extras) endif() install(TARGETS KSC_QScreen DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kscreen/) 07070100000042000081A40000000000000000000000016638555E000005D9000000000000000000000000000000000000005200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenbackend.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qscreenbackend.h" #include "qscreenconfig.h" #include <config.h> using namespace KScreen; Q_LOGGING_CATEGORY(KSCREEN_QSCREEN, "kscreen.qscreen") QScreenConfig *QScreenBackend::s_internalConfig = nullptr; QScreenBackend::QScreenBackend() : KScreen::AbstractBackend() , m_isValid(true) { if (s_internalConfig == nullptr) { s_internalConfig = new QScreenConfig(); connect(s_internalConfig, &QScreenConfig::configChanged, this, &QScreenBackend::configChanged); } } QScreenBackend::~QScreenBackend() { } QString QScreenBackend::name() const { return QStringLiteral("QScreen"); } QString QScreenBackend::serviceName() const { return QStringLiteral("org.kde.KScreen.Backend.QScreen"); } ConfigPtr QScreenBackend::config() const { return s_internalConfig->toKScreenConfig(); } void QScreenBackend::setConfig(const ConfigPtr &config) { if (!config) { return; } qWarning() << "The QScreen backend for libkscreen is read-only,"; qWarning() << "setting a configuration is not supported."; qWarning() << "You can force another backend using the KSCREEN_BACKEND env var."; } bool QScreenBackend::isValid() const { return m_isValid; } 07070100000043000081A40000000000000000000000016638555E0000040C000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenbackend.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef QSCREEN_BACKEND_H #define QSCREEN_BACKEND_H #include "abstractbackend.h" #include <QLoggingCategory> namespace KScreen { class QScreenConfig; class QScreenBackend : public KScreen::AbstractBackend { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kf5.kscreen.backends.qscreen") public: explicit QScreenBackend(); ~QScreenBackend() override; QString name() const override; QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; private: bool m_isValid; static KScreen::QScreenConfig *s_internalConfig; }; } // namespace Q_DECLARE_LOGGING_CATEGORY(KSCREEN_QSCREEN) #endif // QSCREEN_BACKEND_H 07070100000044000081A40000000000000000000000016638555E00000D3D000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenconfig.cpp/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qscreenconfig.h" #include "qscreenbackend.h" #include "qscreenoutput.h" #include "qscreenscreen.h" #include <config.h> #include <mode.h> #include <output.h> #include <QGuiApplication> #include <QRect> #include <utility> using namespace KScreen; QScreenConfig::QScreenConfig(QObject *parent) : QObject(parent) , m_screen(new QScreenScreen(this)) , m_blockSignals(true) { for (const QScreen *qscreen : QGuiApplication::screens()) { screenAdded(qscreen); } m_blockSignals = false; connect(qApp, &QGuiApplication::screenAdded, this, &QScreenConfig::screenAdded); connect(qApp, &QGuiApplication::screenRemoved, this, &QScreenConfig::screenRemoved); } QScreenConfig::~QScreenConfig() { qDeleteAll(m_outputMap); } ConfigPtr QScreenConfig::toKScreenConfig() const { ConfigPtr config(new Config); config->setScreen(m_screen->toKScreenScreen()); updateKScreenConfig(config); return config; } int QScreenConfig::outputId(const QScreen *qscreen) { for (auto output : std::as_const(m_outputMap)) { if (qscreen == output->qscreen()) { return output->id(); } } m_lastOutputId++; return m_lastOutputId; } void QScreenConfig::screenAdded(const QScreen *qscreen) { qCDebug(KSCREEN_QSCREEN) << "Screen added" << qscreen << qscreen->name(); QScreenOutput *qscreenoutput = new QScreenOutput(qscreen, this); qscreenoutput->setId(outputId(qscreen)); m_outputMap.insert(qscreenoutput->id(), qscreenoutput); if (!m_blockSignals) { Q_EMIT configChanged(toKScreenConfig()); } } void QScreenConfig::screenRemoved(QScreen *qscreen) { qCDebug(KSCREEN_QSCREEN) << "Screen removed" << qscreen << QGuiApplication::screens().count(); // Find output matching the QScreen object and remove it int removedOutputId = -1; for (auto output : m_outputMap) { if (output->qscreen() == qscreen) { removedOutputId = output->id(); m_outputMap.remove(removedOutputId); delete output; } } Q_EMIT configChanged(toKScreenConfig()); } void QScreenConfig::updateKScreenConfig(ConfigPtr &config) const { KScreen::ScreenPtr screen = config->screen(); m_screen->updateKScreenScreen(screen); config->setScreen(screen); // Removing removed outputs KScreen::OutputList outputs = config->outputs(); for (const KScreen::OutputPtr &output : outputs) { if (!m_outputMap.contains(output->id())) { config->removeOutput(output->id()); } } // Add KScreen::Outputs that aren't in the list yet, handle primaryOutput for (QScreenOutput *output : m_outputMap) { KScreen::OutputPtr kscreenOutput; if (config->outputs().contains(output->id())) { kscreenOutput = config->outputs()[output->id()]; output->updateKScreenOutput(kscreenOutput); } else { kscreenOutput = output->toKScreenOutput(); config->addOutput(kscreenOutput); } if (QGuiApplication::primaryScreen() == output->qscreen()) { config->setPrimaryOutput(kscreenOutput); } } } QMap<int, QScreenOutput *> QScreenConfig::outputMap() const { return m_outputMap; } 07070100000045000081A40000000000000000000000016638555E000003EE000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenconfig.h/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef QSCREEN_CONFIG_H #define QSCREEN_CONFIG_H #include "types.h" #include <QScreen> namespace KScreen { class Output; class QScreenOutput; class QScreenScreen; class QScreenConfig : public QObject { Q_OBJECT public: explicit QScreenConfig(QObject *parent = nullptr); ~QScreenConfig() override; KScreen::ConfigPtr toKScreenConfig() const; void updateKScreenConfig(KScreen::ConfigPtr &config) const; QMap<int, QScreenOutput *> outputMap() const; int outputId(const QScreen *qscreen); private Q_SLOTS: void screenAdded(const QScreen *qscreen); void screenRemoved(QScreen *qscreen); Q_SIGNALS: void configChanged(const KScreen::ConfigPtr &config); private: QMap<int, QScreenOutput *> m_outputMap; QScreenScreen *m_screen; int m_lastOutputId = -1; bool m_blockSignals; }; } // namespace #endif // QSCREEN_CONFIG_H 07070100000046000081A40000000000000000000000016638555E00000A6E000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenoutput.cpp/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qscreenoutput.h" #include <edid.h> #include <mode.h> #include <output.h> #include <QGuiApplication> using namespace KScreen; QScreenOutput::QScreenOutput(const QScreen *qscreen, QObject *parent) : QObject(parent) , m_qscreen(qscreen) , m_id(-1) { } QScreenOutput::~QScreenOutput() { } int QScreenOutput::id() const { return m_id; } void QScreenOutput::setId(const int newId) { m_id = newId; } const QScreen *QScreenOutput::qscreen() const { return m_qscreen; } OutputPtr QScreenOutput::toKScreenOutput() const { OutputPtr output(new Output); output->setId(m_id); output->setName(m_qscreen->name()); updateKScreenOutput(output); return output; } void QScreenOutput::updateKScreenOutput(OutputPtr &output) const { // Initialize primary output output->setEnabled(true); output->setConnected(true); // Rotation - translate QScreen::primaryOrientation() to Output::rotation() if (m_qscreen->primaryOrientation() == Qt::PortraitOrientation) { // 90 degrees output->setRotation(Output::Right); } else if (m_qscreen->primaryOrientation() == Qt::InvertedLandscapeOrientation) { // 180 degrees output->setRotation(Output::Inverted); } else if (m_qscreen->primaryOrientation() == Qt::InvertedPortraitOrientation) { // 270 degrees output->setRotation(Output::Left); } // Physical size, geometry, etc. QSize mm; qreal physicalWidth; physicalWidth = m_qscreen->size().width() / (m_qscreen->physicalDotsPerInchX() / 25.4); mm.setWidth(qRound(physicalWidth)); qreal physicalHeight; physicalHeight = m_qscreen->size().height() / (m_qscreen->physicalDotsPerInchY() / 25.4); mm.setHeight(qRound(physicalHeight)); output->setSizeMm(mm); output->setPos(m_qscreen->availableGeometry().topLeft()); output->setSize(m_qscreen->availableGeometry().size()); // Modes: we create a single default mode and go with that ModePtr mode(new Mode); const QString modeid = QStringLiteral("defaultmode"); mode->setId(modeid); mode->setRefreshRate(m_qscreen->refreshRate()); mode->setSize(m_qscreen->size()); const QString modename = QString::number(m_qscreen->size().width()) + QLatin1String("x") + QString::number(m_qscreen->size().height()) + QLatin1String("@") + QString::number(m_qscreen->refreshRate()); mode->setName(modename); ModeList modes; modes[modeid] = mode; output->setModes(modes); output->setCurrentModeId(modeid); } 07070100000047000081A40000000000000000000000016638555E000002B4000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenoutput.h/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef QSCREEN_OUTPUT_H #define QSCREEN_OUTPUT_H #include "types.h" #include <QScreen> namespace KScreen { class QScreenOutput : public QObject { Q_OBJECT public: explicit QScreenOutput(const QScreen *qscreen, QObject *parent = nullptr); ~QScreenOutput() override; KScreen::OutputPtr toKScreenOutput() const; void updateKScreenOutput(KScreen::OutputPtr &output) const; int id() const; void setId(const int newId); const QScreen *qscreen() const; private: const QScreen *m_qscreen; int m_id; }; } // namespace #endif 07070100000048000081A40000000000000000000000016638555E00000426000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenscreen.cpp/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "qscreenscreen.h" #include <configmonitor.h> #include <mode.h> #include <screen.h> #include <QGuiApplication> using namespace KScreen; QScreenScreen::QScreenScreen(QScreenConfig *config) : QObject(config) { } QScreenScreen::~QScreenScreen() { } ScreenPtr QScreenScreen::toKScreenScreen() const { KScreen::ScreenPtr kscreenScreen(new KScreen::Screen); updateKScreenScreen(kscreenScreen); return kscreenScreen; } void QScreenScreen::updateKScreenScreen(ScreenPtr &screen) const { if (!screen) { return; } auto primary = QGuiApplication::primaryScreen(); if (primary) { QSize _s = primary->availableVirtualGeometry().size(); screen->setCurrentSize(_s); screen->setId(1); screen->setMaxSize(_s); screen->setMinSize(_s); screen->setCurrentSize(_s); screen->setMaxActiveOutputsCount(QGuiApplication::screens().count()); } } 07070100000049000081A40000000000000000000000016638555E00000272000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/qscreen/qscreenscreen.h/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef QSCREEN_SCREEN_H #define QSCREEN_SCREEN_H #include "qscreenconfig.h" #include "types.h" #include <QLoggingCategory> #include <QScreen> #include <QSize> namespace KScreen { class Output; class QScreenScreen : public QObject { Q_OBJECT public: explicit QScreenScreen(QScreenConfig *config); ~QScreenScreen() override; KScreen::ScreenPtr toKScreenScreen() const; void updateKScreenScreen(KScreen::ScreenPtr &screen) const; }; } // namespace #endif // QSCREEN_SCREEN_H 0707010000004A000081A40000000000000000000000016638555E00000847000000000000000000000000000000000000004100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/utils.cpp/* * SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "utils.h" #include <QVector> KScreen::Output::Type Utils::guessOutputType(const QString &type, const QString &name) { static const auto embedded = {QLatin1String("LVDS"), QLatin1String("IDP"), QLatin1String("EDP"), QLatin1String("LCD"), QLatin1String("DSI")}; for (const QLatin1String &pre : embedded) { if (name.startsWith(pre, Qt::CaseInsensitive)) { return KScreen::Output::Panel; } } if (type.contains(QLatin1String("VGA"))) { return KScreen::Output::VGA; } else if (type.contains(QLatin1String("DVI"))) { return KScreen::Output::DVI; } else if (type.contains(QLatin1String("DVI-I"))) { return KScreen::Output::DVII; } else if (type.contains(QLatin1String("DVI-A"))) { return KScreen::Output::DVIA; } else if (type.contains(QLatin1String("DVI-D"))) { return KScreen::Output::DVID; } else if (type.contains(QLatin1String("HDMI"))) { return KScreen::Output::HDMI; } else if (type.contains(QLatin1String("Panel"))) { return KScreen::Output::Panel; } else if (type.contains(QLatin1String("TV-Composite"))) { return KScreen::Output::TVComposite; } else if (type.contains(QLatin1String("TV-SVideo"))) { return KScreen::Output::TVSVideo; } else if (type.contains(QLatin1String("TV-Component"))) { return KScreen::Output::TVComponent; } else if (type.contains(QLatin1String("TV-SCART"))) { return KScreen::Output::TVSCART; } else if (type.contains(QLatin1String("TV-C4"))) { return KScreen::Output::TVC4; } else if (type.contains(QLatin1String("TV"))) { return KScreen::Output::TV; } else if (type.contains(QLatin1String("DisplayPort")) || type.startsWith(QLatin1String("DP"))) { return KScreen::Output::DisplayPort; } else if (type.contains(QLatin1String("unknown"))) { return KScreen::Output::Unknown; } else { return KScreen::Output::Unknown; } } 0707010000004B000081A40000000000000000000000016638555E00000142000000000000000000000000000000000000003F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/utils.h/* * SPDX-FileCopyrightText: 2018 Daniel Vrátil <dvratil@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_BACKEND_UTILS_H_ #define KSCREEN_BACKEND_UTILS_H_ #include "output.h" namespace Utils { KScreen::Output::Type guessOutputType(const QString &type, const QString &name); } #endif 0707010000004C000081A40000000000000000000000016638555E00002032000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xcbeventlistener.cpp/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xcbeventlistener.h" #include "../xcbwrapper.h" #include <QGuiApplication> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif Q_LOGGING_CATEGORY(KSCREEN_XCB_HELPER, "kscreen.xcb.helper") XCBEventListener::XCBEventListener() : m_isRandrPresent(false) , m_randrBase(0) , m_randrErrorBase(0) , m_majorOpcode(0) , m_versionMajor(0) , m_versionMinor(0) , m_window(0) { xcb_connection_t *c = QX11Info::connection(); xcb_prefetch_extension_data(c, &xcb_randr_id); auto cookie = xcb_randr_query_version(c, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); const auto *queryExtension = xcb_get_extension_data(c, &xcb_randr_id); if (!queryExtension) { qCDebug(KSCREEN_XCB_HELPER) << "Fail to query for xrandr extension"; return; } if (!queryExtension->present) { qCDebug(KSCREEN_XCB_HELPER) << "XRandR extension is not present at all"; return; } m_isRandrPresent = queryExtension->present; m_randrBase = queryExtension->first_event; m_randrErrorBase = queryExtension->first_error; m_majorOpcode = queryExtension->major_opcode; xcb_generic_error_t *error = nullptr; auto *versionReply = xcb_randr_query_version_reply(c, cookie, &error); Q_ASSERT_X(versionReply, "xrandrxcbhelper", "Query to fetch xrandr version failed"); if (error) { qFatal("Error while querying for xrandr version: %d", error->error_code); } m_versionMajor = versionReply->major_version; m_versionMinor = versionReply->minor_version; free(versionReply); qCDebug(KSCREEN_XCB_HELPER).nospace() << "Detected XRandR " << m_versionMajor << "." << m_versionMinor; qCDebug(KSCREEN_XCB_HELPER) << "Event Base: " << m_randrBase; qCDebug(KSCREEN_XCB_HELPER) << "Event Error: " << m_randrErrorBase; uint32_t rWindow = QX11Info::appRootWindow(); m_window = xcb_generate_id(c); xcb_create_window(c, XCB_COPY_FROM_PARENT, m_window, rWindow, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr); xcb_randr_select_input(c, m_window, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); qApp->installNativeEventFilter(this); } XCBEventListener::~XCBEventListener() { if (m_window && QX11Info::connection()) { xcb_destroy_window(QX11Info::connection(), m_window); } } QString XCBEventListener::rotationToString(xcb_randr_rotation_t rotation) { switch (rotation) { case XCB_RANDR_ROTATION_ROTATE_0: return QStringLiteral("Rotate_0"); case XCB_RANDR_ROTATION_ROTATE_90: return QStringLiteral("Rotate_90"); case XCB_RANDR_ROTATION_ROTATE_180: return QStringLiteral("Rotate_180"); case XCB_RANDR_ROTATION_ROTATE_270: return QStringLiteral("Rotate_270"); case XCB_RANDR_ROTATION_REFLECT_X: return QStringLiteral("Reflect_X"); case XCB_RANDR_ROTATION_REFLECT_Y: return QStringLiteral("Reflect_Y"); } return QStringLiteral("invalid value (%1)").arg(rotation); } QString XCBEventListener::connectionToString(xcb_randr_connection_t connection) { switch (connection) { case XCB_RANDR_CONNECTION_CONNECTED: return QStringLiteral("Connected"); case XCB_RANDR_CONNECTION_DISCONNECTED: return QStringLiteral("Disconnected"); case XCB_RANDR_CONNECTION_UNKNOWN: return QStringLiteral("UnknownConnection"); } return QStringLiteral("invalid value (%1)").arg(connection); } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) bool XCBEventListener::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) #else bool XCBEventListener::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) #endif { Q_UNUSED(result); if (eventType != "xcb_generic_event_t") { return false; } auto *e = static_cast<xcb_generic_event_t *>(message); const uint8_t xEventType = e->response_type & ~0x80; // If this event is not xcb_randr_notify, we don't want it if (xEventType == m_randrBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { handleScreenChange(e); } if (xEventType == m_randrBase + XCB_RANDR_NOTIFY) { handleXRandRNotify(e); } return false; } void XCBEventListener::handleScreenChange(xcb_generic_event_t *e) { auto *e2 = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(e); // Only accept notifications for our window if (e2->request_window != m_window) { return; } qCDebug(KSCREEN_XCB_HELPER) << "RRScreenChangeNotify"; qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << e2->timestamp; qCDebug(KSCREEN_XCB_HELPER) << "\tConfig_timestamp: " << e2->config_timestamp; qCDebug(KSCREEN_XCB_HELPER) << "\tWindow:" << e2->request_window; qCDebug(KSCREEN_XCB_HELPER) << "\tRoot:" << e2->root; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)e2->rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tSize ID:" << e2->sizeID; qCDebug(KSCREEN_XCB_HELPER) << "\tSize: " << e2->width << e2->height; qCDebug(KSCREEN_XCB_HELPER) << "\tSizeMM: " << e2->mwidth << e2->mheight; Q_EMIT screenChanged((xcb_randr_rotation_t)e2->rotation, QSize(e2->width, e2->height), QSize(e2->mwidth, e2->mheight)); Q_EMIT outputsChanged(); } void XCBEventListener::handleXRandRNotify(xcb_generic_event_t *e) { auto *randrEvent = reinterpret_cast<xcb_randr_notify_event_t *>(e); if (randrEvent->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) { xcb_randr_crtc_change_t crtc = randrEvent->u.cc; qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_CrtcChange"; qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << crtc.timestamp; qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << crtc.crtc; qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << crtc.mode; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)crtc.rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tGeometry: " << crtc.x << crtc.y << crtc.width << crtc.height; Q_EMIT crtcChanged(crtc.crtc, crtc.mode, (xcb_randr_rotation_t)crtc.rotation, QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.timestamp); } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) { xcb_randr_output_change_t output = randrEvent->u.oc; qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputChange"; qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << output.timestamp; qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << output.output; qCDebug(KSCREEN_XCB_HELPER) << "\tCRTC: " << output.crtc; qCDebug(KSCREEN_XCB_HELPER) << "\tMode: " << output.mode; qCDebug(KSCREEN_XCB_HELPER) << "\tRotation: " << rotationToString((xcb_randr_rotation_t)output.rotation); qCDebug(KSCREEN_XCB_HELPER) << "\tConnection: " << connectionToString((xcb_randr_connection_t)output.connection); qCDebug(KSCREEN_XCB_HELPER) << "\tSubpixel Order: " << output.subpixel_order; Q_EMIT outputChanged(output.output, output.crtc, output.mode, (xcb_randr_connection_t)output.connection); } else if (randrEvent->subCode == XCB_RANDR_NOTIFY_OUTPUT_PROPERTY) { xcb_randr_output_property_t property = randrEvent->u.op; XCB::ScopedPointer<xcb_get_atom_name_reply_t> reply( xcb_get_atom_name_reply(QX11Info::connection(), xcb_get_atom_name(QX11Info::connection(), property.atom), nullptr)); qCDebug(KSCREEN_XCB_HELPER) << "RRNotify_OutputProperty (ignored)"; qCDebug(KSCREEN_XCB_HELPER) << "\tTimestamp: " << property.timestamp; qCDebug(KSCREEN_XCB_HELPER) << "\tOutput: " << property.output; qCDebug(KSCREEN_XCB_HELPER) << "\tProperty: " << xcb_get_atom_name_name(reply.data()); qCDebug(KSCREEN_XCB_HELPER) << "\tState (newValue, Deleted): " << property.status; } } 0707010000004D000081A40000000000000000000000016638555E0000075E000000000000000000000000000000000000004A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xcbeventlistener.h/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef XRANDRX11HELPER_H #define XRANDRX11HELPER_H #include <QAbstractNativeEventFilter> #include <QLoggingCategory> #include <QObject> #include <QRect> #include <xcb/randr.h> #include <xcb/xcb.h> class XCBEventListener : public QObject, public QAbstractNativeEventFilter { Q_OBJECT public: XCBEventListener(); ~XCBEventListener() override; #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; #else bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; #endif Q_SIGNALS: /* Emitted when only XRandR 1.1 or older is available */ void screenChanged(xcb_randr_rotation_t rotation, const QSize &sizePx, const QSize &sizeMm); void outputsChanged(); /* Emitted only when XRandR 1.2 or newer is available */ void crtcChanged(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, const QRect &geom, xcb_timestamp_t timestamp); void outputChanged(xcb_randr_output_t output, xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t connection); void outputPropertyChanged(xcb_randr_output_t output); private: QString rotationToString(xcb_randr_rotation_t rotation); QString connectionToString(xcb_randr_connection_t connection); void handleScreenChange(xcb_generic_event_t *e); void handleXRandRNotify(xcb_generic_event_t *e); protected: bool m_isRandrPresent; bool m_event11; uint8_t m_randrBase; uint8_t m_randrErrorBase; uint8_t m_majorOpcode; uint32_t m_versionMajor; uint32_t m_versionMinor; uint32_t m_window; }; Q_DECLARE_LOGGING_CATEGORY(KSCREEN_XCB_HELPER) #endif // XRANDRX11HELPER_H 0707010000004E000081A40000000000000000000000016638555E0000050B000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xcbwrapper.cpp/* K Win - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2012, 2013 Martin Gräßlin <mgraesslin@kde.org> SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com> SPDX-License-Identifier: GPL-2.0-or-later */ #include "xcbwrapper.h" static xcb_connection_t *sXRandR11XCBConnection = nullptr; xcb_connection_t *XCB::connection() { // Use our own connection to make sure that we won't mess up Qt's connection // if something goes wrong on our side. if (sXRandR11XCBConnection == nullptr) { sXRandR11XCBConnection = xcb_connect(nullptr, nullptr); } return sXRandR11XCBConnection; } void XCB::closeConnection() { if (sXRandR11XCBConnection) { xcb_disconnect(sXRandR11XCBConnection); sXRandR11XCBConnection = nullptr; } } xcb_screen_t *XCB::screenOfDisplay(xcb_connection_t *c, int screen) { for (auto iter = xcb_setup_roots_iterator(xcb_get_setup(c)); iter.rem; --screen, xcb_screen_next(&iter)) { if (screen == 0) { return iter.data; } } return nullptr; } XCB::GrabServer::GrabServer() { xcb_grab_server(connection()); } XCB::GrabServer::~GrabServer() { xcb_ungrab_server(connection()); xcb_flush(connection()); } 0707010000004F000081A40000000000000000000000016638555E0000194F000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xcbwrapper.h/* K Win - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2012, 2013 Martin Gräßlin <mgraesslin@kde.org> SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com> SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef XCB_WRAPPER_H #define XCB_WRAPPER_H #include <functional> #include <type_traits> #include <QScopedPointer> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif #include <xcb/randr.h> #include <xcb/xcb.h> namespace XCB { template<typename T> using ScopedPointer = QScopedPointer<T, QScopedPointerPodDeleter>; xcb_connection_t *connection(); void closeConnection(); xcb_screen_t *screenOfDisplay(xcb_connection_t *c, int screen); struct GrabServer { GrabServer(); ~GrabServer(); }; template<typename Reply, typename Cookie, typename ReplyFunc, ReplyFunc replyFunc, typename RequestFunc, RequestFunc requestFunc, typename... RequestFuncArgs> class Wrapper { public: Wrapper() : m_retrieved(false) , m_window(XCB_WINDOW_NONE) , m_reply(nullptr) { m_cookie.sequence = 0; } explicit Wrapper(const RequestFuncArgs &... args) : m_retrieved(false) , m_cookie(requestFunc(connection(), args...)) , m_window(requestWindow<RequestFuncArgs...>(args...)) , m_reply(nullptr) { } explicit Wrapper(const Wrapper &other) : m_retrieved(other.m_retrieved) , m_cookie(other.m_cookie) , m_window(other.m_window) , m_reply(nullptr) { takeFromOther(const_cast<Wrapper &>(other)); } virtual ~Wrapper() { cleanup(); } inline Wrapper &operator=(const Wrapper &other) { if (this != &other) { // if we had managed a reply, free it cleanup(); // copy members m_retrieved = other.m_retrieved; m_cookie = other.m_cookie; m_window = other.m_window; m_reply = other.m_reply; // take over the responsibility for the reply pointer takeFromOther(const_cast<Wrapper &>(other)); } return *this; } inline operator const Reply *() const { getReply(); return m_reply; } inline const Reply *operator->() const { getReply(); return m_reply; } inline bool isNull() const { getReply(); return m_reply == nullptr; } inline operator bool() const { return !isNull(); } inline const Reply *data() const { getReply(); return m_reply; } inline xcb_window_t window() const { return m_window; } inline bool isRetrieved() const { return m_retrieved; } /** * Returns the value of the reply pointer referenced by this object. The reply pointer of * this object will be reset to null. Calling any method which requires the reply to be valid * will crash. * * Callers of this function take ownership of the pointer. **/ inline Reply *take() { getReply(); Reply *ret = m_reply; m_reply = nullptr; m_window = XCB_WINDOW_NONE; return ret; } protected: void getReply() const { if (m_retrieved || !m_cookie.sequence) { return; } m_reply = replyFunc(connection(), m_cookie, nullptr); m_retrieved = true; } private: inline void cleanup() { if (!m_retrieved && m_cookie.sequence) { xcb_discard_reply(connection(), m_cookie.sequence); } else if (m_reply) { free(m_reply); } } inline void takeFromOther(Wrapper &other) { if (m_retrieved) { m_reply = other.take(); } else { // ensure that other object doesn't try to get the reply or discards it in the dtor other.m_retrieved = true; other.m_window = XCB_WINDOW_NONE; } } template<typename... Args> constexpr xcb_window_t requestWindow(const Args &... args) const { return std::is_same<typename std::tuple_element<0, std::tuple<Args...>>::type, xcb_window_t>::value ? std::get<0>(std::tuple<Args...>(args...)) : static_cast<xcb_window_t>(XCB_WINDOW_NONE); } mutable bool m_retrieved; Cookie m_cookie; xcb_window_t m_window; mutable Reply *m_reply; }; #define XCB_DECLARE_TYPE(name, xcb_request, ...) \ typedef Wrapper<xcb_request##_reply_t, \ xcb_request##_cookie_t, \ decltype(&xcb_request##_reply), \ xcb_request##_reply, \ decltype(&xcb_request), \ xcb_request, \ ##__VA_ARGS__> \ name XCB_DECLARE_TYPE(ScreenInfo, xcb_randr_get_screen_info, xcb_window_t); XCB_DECLARE_TYPE(ScreenSize, xcb_randr_get_screen_size_range, xcb_window_t); XCB_DECLARE_TYPE(PrimaryOutput, xcb_randr_get_output_primary, xcb_window_t); XCB_DECLARE_TYPE(InternAtom, xcb_intern_atom, uint8_t, uint16_t, const char *); XCB_DECLARE_TYPE(OutputInfo, xcb_randr_get_output_info, xcb_randr_output_t, xcb_timestamp_t); XCB_DECLARE_TYPE(CRTCInfo, xcb_randr_get_crtc_info, xcb_randr_crtc_t, xcb_timestamp_t); XCB_DECLARE_TYPE(AtomName, xcb_get_atom_name, xcb_atom_t); } #endif 07070100000050000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr07070100000051000081A40000000000000000000000016638555E000003B4000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/CMakeLists.txtinclude_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BUILD_DIR} ) add_library(KSC_XRandR MODULE) target_sources(KSC_XRandR PRIVATE xrandr.cpp xrandrconfig.cpp xrandrcrtc.cpp xrandroutput.cpp xrandrmode.cpp xrandrscreen.cpp ../xcbwrapper.cpp ../xcbeventlistener.cpp ../utils.cpp ) set_target_properties(KSC_XRandR PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kscreen") set_target_properties(KSC_XRandR PROPERTIES PREFIX "") target_link_libraries(KSC_XRandR Qt::Core Qt::Gui ${XCB_LIBRARIES} KF5::Screen ) if (QT_MAJOR_VERSION EQUAL "5") target_link_libraries(KSC_XRandR Qt::X11Extras) else() target_link_libraries(KSC_XRandR Qt::GuiPrivate) endif() install(TARGETS KSC_XRandR DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kscreen/) 07070100000052000081A40000000000000000000000016638555E00002666000000000000000000000000000000000000004900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandr.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandr.h" #include "xrandrconfig.h" #include "xrandrscreen.h" #include "../xcbeventlistener.h" #include "../xcbwrapper.h" #include "types.h" #include <QRect> #include <QTime> #include <QTimer> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif xcb_screen_t *XRandR::s_screen = nullptr; xcb_window_t XRandR::s_rootWindow = 0; XRandRConfig *XRandR::s_internalConfig = nullptr; int XRandR::s_randrBase = 0; int XRandR::s_randrError = 0; bool XRandR::s_monitorInitialized = false; bool XRandR::s_has_1_3 = false; bool XRandR::s_xorgCacheInitialized = false; using namespace KScreen; Q_LOGGING_CATEGORY(KSCREEN_XRANDR, "kscreen.xrandr") XRandR::XRandR() : KScreen::AbstractBackend() , m_x11Helper(nullptr) , m_isValid(false) , m_configChangeCompressor(nullptr) { qRegisterMetaType<xcb_randr_output_t>("xcb_randr_output_t"); qRegisterMetaType<xcb_randr_crtc_t>("xcb_randr_crtc_t"); qRegisterMetaType<xcb_randr_mode_t>("xcb_randr_mode_t"); qRegisterMetaType<xcb_randr_connection_t>("xcb_randr_connection_t"); qRegisterMetaType<xcb_randr_rotation_t>("xcb_randr_rotation_t"); qRegisterMetaType<xcb_timestamp_t>("xcb_timestamp_t"); // Use our own connection to make sure that we won't mess up Qt's connection // if something goes wrong on our side. xcb_generic_error_t *error = nullptr; xcb_randr_query_version_reply_t *version; XCB::connection(); version = xcb_randr_query_version_reply(XCB::connection(), // xcb_randr_query_version(XCB::connection(), XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &error); if (!version || error) { XCB::closeConnection(); free(error); return; } if ((version->major_version > 1) || ((version->major_version == 1) && (version->minor_version >= 2))) { m_isValid = true; } else { XCB::closeConnection(); qCWarning(KSCREEN_XRANDR) << "XRandR extension not available or unsupported version"; return; } if (s_screen == nullptr) { s_screen = XCB::screenOfDisplay(XCB::connection(), QX11Info::appScreen()); s_rootWindow = s_screen->root; xcb_prefetch_extension_data(XCB::connection(), &xcb_randr_id); auto reply = xcb_get_extension_data(XCB::connection(), &xcb_randr_id); s_randrBase = reply->first_event; s_randrError = reply->first_error; } XRandR::s_has_1_3 = (version->major_version > 1 || (version->major_version == 1 && version->minor_version >= 3)); if (s_internalConfig == nullptr) { s_internalConfig = new XRandRConfig(); } if (!s_monitorInitialized) { m_x11Helper = new XCBEventListener(); connect(m_x11Helper, &XCBEventListener::outputChanged, this, &XRandR::outputChanged, Qt::QueuedConnection); connect(m_x11Helper, &XCBEventListener::crtcChanged, this, &XRandR::crtcChanged, Qt::QueuedConnection); connect(m_x11Helper, &XCBEventListener::screenChanged, this, &XRandR::screenChanged, Qt::QueuedConnection); m_configChangeCompressor = new QTimer(this); m_configChangeCompressor->setSingleShot(true); m_configChangeCompressor->setInterval(500); connect(m_configChangeCompressor, &QTimer::timeout, [&]() { qCDebug(KSCREEN_XRANDR) << "Emitting configChanged()"; Q_EMIT configChanged(config()); }); s_monitorInitialized = true; } } XRandR::~XRandR() { delete m_x11Helper; } QString XRandR::name() const { return QStringLiteral("XRandR"); } QString XRandR::serviceName() const { return QStringLiteral("org.kde.KScreen.Backend.XRandR"); } void XRandR::outputChanged(xcb_randr_output_t output, xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t connection) { m_configChangeCompressor->start(); XRandROutput *xOutput = s_internalConfig->output(output); if (!xOutput) { s_internalConfig->addNewOutput(output); return; } // check if this output disappeared if (crtc == XCB_NONE && mode == XCB_NONE && connection == XCB_RANDR_CONNECTION_DISCONNECTED) { XCB::OutputInfo info(output, XCB_TIME_CURRENT_TIME); if (info.isNull()) { s_internalConfig->removeOutput(output); qCDebug(KSCREEN_XRANDR) << "Output" << output << " removed"; return; } // info is valid: the output is still there } xOutput->update(crtc, mode, connection); qCDebug(KSCREEN_XRANDR) << "Output" << xOutput->id() << ": connected =" << xOutput->isConnected() << ", enabled =" << xOutput->isEnabled(); } void XRandR::crtcChanged(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, const QRect &geom, xcb_timestamp_t timestamp) { XRandRCrtc *xCrtc = s_internalConfig->crtc(crtc); if (!xCrtc) { s_internalConfig->addNewCrtc(crtc); xCrtc = s_internalConfig->crtc(crtc); } xCrtc->update(mode, rotation, geom); // BUG 472280 Still need to call update(...) because the timestamp is newer xCrtc->updateConfigTimestamp(timestamp); m_configChangeCompressor->start(); } void XRandR::screenChanged(xcb_randr_rotation_t rotation, const QSize &sizePx, const QSize &sizeMm) { Q_UNUSED(sizeMm); QSize newSizePx = sizePx; if (rotation == XCB_RANDR_ROTATION_ROTATE_90 || rotation == XCB_RANDR_ROTATION_ROTATE_270) { newSizePx.transpose(); } XRandRScreen *xScreen = s_internalConfig->screen(); Q_ASSERT(xScreen); xScreen->update(newSizePx); m_configChangeCompressor->start(); } ConfigPtr XRandR::config() const { return s_internalConfig->toKScreenConfig(); } void XRandR::setConfig(const ConfigPtr &config) { if (!config) { return; } qCDebug(KSCREEN_XRANDR) << "XRandR::setConfig"; s_internalConfig->applyKScreenConfig(config); qCDebug(KSCREEN_XRANDR) << "XRandR::setConfig done!"; } QByteArray XRandR::edid(int outputId) const { const XRandROutput *output = s_internalConfig->output(outputId); if (!output) { return QByteArray(); } return output->edid(); } bool XRandR::isValid() const { return m_isValid; } quint8 *XRandR::getXProperty(xcb_randr_output_t output, xcb_atom_t atom, size_t &len) { quint8 *result; auto cookie = xcb_randr_get_output_property(XCB::connection(), output, atom, XCB_ATOM_ANY, 0, 100, false, false); auto reply = xcb_randr_get_output_property_reply(XCB::connection(), cookie, nullptr); if (reply->type == XCB_ATOM_INTEGER && reply->format == 8) { result = new quint8[reply->num_items]; memcpy(result, xcb_randr_get_output_property_data(reply), reply->num_items); len = reply->num_items; } else { result = nullptr; } free(reply); return result; } QByteArray XRandR::outputEdid(xcb_randr_output_t outputId) { size_t len = 0; quint8 *result; auto edid_atom = XCB::InternAtom(false, 4, "EDID")->atom; result = XRandR::getXProperty(outputId, edid_atom, len); if (result == nullptr) { auto edid_atom = XCB::InternAtom(false, 9, "EDID_DATA")->atom; result = XRandR::getXProperty(outputId, edid_atom, len); } if (result == nullptr) { auto edid_atom = XCB::InternAtom(false, 25, "XFree86_DDC_EDID1_RAWDATA")->atom; result = XRandR::getXProperty(outputId, edid_atom, len); } QByteArray edid; if (result != nullptr) { if (len % 128 == 0) { edid = QByteArray(reinterpret_cast<const char *>(result), len); } delete[] result; } return edid; } bool XRandR::hasProperty(xcb_randr_output_t output, const QByteArray &name) { xcb_generic_error_t *error = nullptr; auto atom = XCB::InternAtom(false, name.length(), name.constData())->atom; auto cookie = xcb_randr_get_output_property(XCB::connection(), output, atom, XCB_ATOM_ANY, 0, 1, false, false); auto prop_reply = xcb_randr_get_output_property_reply(XCB::connection(), cookie, &error); const bool ret = prop_reply->num_items == 1; free(prop_reply); return ret; } xcb_randr_get_screen_resources_reply_t *XRandR::screenResources() { if (XRandR::s_has_1_3) { if (XRandR::s_xorgCacheInitialized) { // HACK: This abuses the fact that xcb_randr_get_screen_resources_reply_t // and xcb_randr_get_screen_resources_current_reply_t are the same return reinterpret_cast<xcb_randr_get_screen_resources_reply_t *>( xcb_randr_get_screen_resources_current_reply(XCB::connection(), xcb_randr_get_screen_resources_current(XCB::connection(), XRandR::rootWindow()), nullptr)); } else { /* XRRGetScreenResourcesCurrent is faster then XRRGetScreenResources * because it returns cached values. However the cached values are not * available until someone calls XRRGetScreenResources first. In case * we happen to be the first ones, we need to fill the cache first. */ XRandR::s_xorgCacheInitialized = true; } } return xcb_randr_get_screen_resources_reply(XCB::connection(), xcb_randr_get_screen_resources(XCB::connection(), XRandR::rootWindow()), nullptr); } xcb_window_t XRandR::rootWindow() { return s_rootWindow; } xcb_screen_t *XRandR::screen() { return s_screen; } 07070100000053000081A40000000000000000000000016638555E0000080B000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandr.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "abstractbackend.h" #include <QLoggingCategory> #include <QSize> #include <xcb/randr.h> #include <xcb/xcb.h> class QRect; class QTimer; class XCBEventListener; class XRandRConfig; class XRandR : public KScreen::AbstractBackend { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kf5.kscreen.backends.xrandr") public: explicit XRandR(); ~XRandR() override; QString name() const override; QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; QByteArray edid(int outputId) const override; static QByteArray outputEdid(xcb_randr_output_t outputId); static xcb_randr_get_screen_resources_reply_t *screenResources(); static xcb_screen_t *screen(); static xcb_window_t rootWindow(); static bool hasProperty(xcb_randr_output_t outputId, const QByteArray &name); private: void outputChanged(xcb_randr_output_t output, xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t connection); void crtcChanged(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, const QRect &geom, xcb_timestamp_t timestamp); void screenChanged(xcb_randr_rotation_t rotation, const QSize &sizePx, const QSize &sizeMm); static quint8 *getXProperty(xcb_randr_output_t output, xcb_atom_t atom, size_t &len); static xcb_screen_t *s_screen; static xcb_window_t s_rootWindow; static XRandRConfig *s_internalConfig; static int s_randrBase; static int s_randrError; static bool s_monitorInitialized; static bool s_has_1_3; static bool s_xorgCacheInitialized; XCBEventListener *m_x11Helper; bool m_isValid; QTimer *m_configChangeCompressor; }; Q_DECLARE_LOGGING_CATEGORY(KSCREEN_XRANDR) 07070100000054000081A40000000000000000000000016638555E00006290000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrconfig.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012-2015 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandrconfig.h" #include "xrandr.h" #include "xrandrmode.h" #include "xrandrscreen.h" #include "../xcbwrapper.h" #include "config.h" #include "edid.h" #include "mode.h" #include "output.h" #include "screen.h" #include <QRect> #include <QScopedPointer> #include <optional> #include <utility> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif using namespace KScreen; XRandRConfig::XRandRConfig() : QObject() , m_screen(nullptr) { m_screen = new XRandRScreen(this); XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> resources(XRandR::screenResources()); xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_crtcs(resources.data()); const int crtcsCount = xcb_randr_get_screen_resources_crtcs_length(resources.data()); for (int i = 0; i < crtcsCount; ++i) { addNewCrtc(crtcs[i]); } xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources.data()); const int outputsCount = xcb_randr_get_screen_resources_outputs_length(resources.data()); for (int i = 0; i < outputsCount; ++i) { addNewOutput(outputs[i]); } } XRandRConfig::~XRandRConfig() { qDeleteAll(m_outputs); qDeleteAll(m_crtcs); delete m_screen; } XRandROutput::Map XRandRConfig::outputs() const { return m_outputs; } XRandROutput *XRandRConfig::output(xcb_randr_output_t output) const { return m_outputs[output]; } XRandRCrtc::Map XRandRConfig::crtcs() const { return m_crtcs; } XRandRCrtc *XRandRConfig::crtc(xcb_randr_crtc_t crtc) const { return m_crtcs[crtc]; } XRandRScreen *XRandRConfig::screen() const { return m_screen; } void XRandRConfig::addNewOutput(xcb_randr_output_t id) { XRandROutput *xOutput = new XRandROutput(id, this); m_outputs.insert(id, xOutput); } void XRandRConfig::addNewCrtc(xcb_randr_crtc_t crtc) { m_crtcs.insert(crtc, new XRandRCrtc(crtc, this)); } void XRandRConfig::removeOutput(xcb_randr_output_t id) { delete m_outputs.take(id); } KScreen::ConfigPtr XRandRConfig::toKScreenConfig() const { KScreen::ConfigPtr config(new KScreen::Config); const Config::Features features = Config::Feature::Writable | Config::Feature::PrimaryDisplay | Config::Feature::OutputReplication; config->setSupportedFeatures(features); KScreen::OutputList kscreenOutputs; for (const XRandROutput *output : std::as_const(m_outputs)) { KScreen::OutputPtr kscreenOutput = output->toKScreenOutput(); kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); } config->setOutputs(kscreenOutputs); config->setScreen(m_screen->toKScreenScreen()); return config; } void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config) { config->adjustPriorities(); // never trust input const KScreen::OutputList kscreenOutputs = config->outputs(); const QSize newScreenSize = screenSize(config); const QSize currentScreenSize = m_screen->currentSize(); // When the current screen configuration is bigger than the new size (like // when rotating an output), the XSetScreenSize can fail or apply the smaller // size only partially, because we apply the size (we have to) before the // output changes. To prevent all kinds of weird screen sizes from happening, // we initially set such screen size, that it can take the current as well // as the new configuration, then we apply the output changes, and finally then // (if necessary) we reduce the screen size to fix the new configuration precisely. const QSize intermediateScreenSize = QSize(qMax(newScreenSize.width(), currentScreenSize.width()), qMax(newScreenSize.height(), currentScreenSize.height())); int neededCrtcs = 0; // pairs of before/after QMap<xcb_randr_output_t, std::pair<std::optional<uint32_t>, std::optional<uint32_t>>> prioritiesChange; for (const XRandROutput *xrandrOutput : std::as_const(m_outputs)) { prioritiesChange[xrandrOutput->id()].first = std::optional(xrandrOutput->priority()); } for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) { prioritiesChange[kscreenOutput->id()].second = std::optional(kscreenOutput->priority()); } const bool prioritiesDiffer = std::any_of(prioritiesChange.cbegin(), prioritiesChange.cend(), [](const auto &pair) { const auto &[before, after] = pair; return !before.has_value() || !after.has_value() || before.value() != after.value(); }); KScreen::OutputList toDisable, toEnable, toChange; for (const KScreen::OutputPtr &kscreenOutput : kscreenOutputs) { xcb_randr_output_t outputId = kscreenOutput->id(); XRandROutput *currentOutput = output(outputId); const bool currentEnabled = currentOutput->isEnabled(); if (!kscreenOutput->isEnabled() && currentEnabled) { toDisable.insert(outputId, kscreenOutput); continue; } else if (kscreenOutput->isEnabled() && !currentEnabled) { toEnable.insert(outputId, kscreenOutput); ++neededCrtcs; continue; } else if (!kscreenOutput->isEnabled() && !currentEnabled) { continue; } ++neededCrtcs; if (kscreenOutput->currentModeId() != currentOutput->currentModeId()) { if (!toChange.contains(outputId)) { toChange.insert(outputId, kscreenOutput); } } if (kscreenOutput->pos() != currentOutput->position()) { if (!toChange.contains(outputId)) { toChange.insert(outputId, kscreenOutput); } } if (kscreenOutput->rotation() != currentOutput->rotation()) { if (!toChange.contains(outputId)) { toChange.insert(outputId, kscreenOutput); } } if (kscreenOutput->explicitLogicalSize() != currentOutput->logicalSize()) { if (!toChange.contains(outputId)) { toChange.insert(outputId, kscreenOutput); } } XRandRMode *currentMode = currentOutput->modes().value(kscreenOutput->currentModeId().toInt()); // For some reason, in some environments currentMode is null // which doesn't make sense because it is the *current* mode... // Since we haven't been able to figure out the reason why // this happens, we are adding this debug code to try to // figure out how this happened. if (!currentMode) { qWarning() << "Current mode is null:" << "ModeId:" << currentOutput->currentModeId() << "Mode: " << currentOutput->currentMode() << "Output: " << currentOutput->id(); printConfig(config); printInternalCond(); continue; } // When the output would not fit into new screen size, we need to disable and reposition it. const QRect geom = kscreenOutput->geometry(); if (geom.right() > newScreenSize.width() || geom.bottom() > newScreenSize.height()) { if (!toDisable.contains(outputId)) { qCDebug(KSCREEN_XRANDR) << "The new output would not fit into screen - new geometry: " << geom << ", new screen size:" << newScreenSize; toDisable.insert(outputId, kscreenOutput); } } } const KScreen::ScreenPtr kscreenScreen = config->screen(); if (newScreenSize.width() > kscreenScreen->maxSize().width() || newScreenSize.height() > kscreenScreen->maxSize().height()) { qCDebug(KSCREEN_XRANDR) << "The new screen size is too big - requested: " << newScreenSize << ", maximum: " << kscreenScreen->maxSize(); return; } qCDebug(KSCREEN_XRANDR) << "Needed CRTCs: " << neededCrtcs; XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> screenResources(XRandR::screenResources()); if (neededCrtcs > screenResources->num_crtcs) { qCDebug(KSCREEN_XRANDR) << "We need more CRTCs than we have available - requested: " << neededCrtcs << ", available: " << screenResources->num_crtcs; return; } qCDebug(KSCREEN_XRANDR) << "Actions to perform:\n" << "\tPriorities:" << prioritiesDiffer; for (auto it = prioritiesChange.constBegin(); it != prioritiesChange.constEnd(); it++) { const auto &[before, after] = it.value(); if (before != after) { qCDebug(KSCREEN_XRANDR) << "\tOutput" << it.key() << "\n" << "\t\tOld:" << (before.has_value() ? QString::number(before.value()) : QStringLiteral("none")) << "\n" << "\t\tNew:" << (after.has_value() ? QString::number(after.value()) : QStringLiteral("none")); } } qCDebug(KSCREEN_XRANDR) << "\tChange Screen Size:" << (newScreenSize != currentScreenSize); if (newScreenSize != currentScreenSize) { qCDebug(KSCREEN_XRANDR) << "\t\tOld:" << currentScreenSize << "\n" << "\t\tIntermediate:" << intermediateScreenSize << "\n" << "\t\tNew:" << newScreenSize; } qCDebug(KSCREEN_XRANDR) << "\tDisable outputs:" << !toDisable.isEmpty(); if (!toDisable.isEmpty()) { qCDebug(KSCREEN_XRANDR) << "\t\t" << toDisable.keys(); } qCDebug(KSCREEN_XRANDR) << "\tChange outputs:" << !toChange.isEmpty(); if (!toChange.isEmpty()) { qCDebug(KSCREEN_XRANDR) << "\t\t" << toChange.keys(); } qCDebug(KSCREEN_XRANDR) << "\tEnable outputs:" << !toEnable.isEmpty(); if (!toEnable.isEmpty()) { qCDebug(KSCREEN_XRANDR) << "\t\t" << toEnable.keys(); } // Grab the server so that no-one else can do changes to XRandR and to block // change notifications until we are done XCB::GrabServer grabber; // If there is nothing to do, not even bother if (!prioritiesDiffer && toDisable.isEmpty() && toEnable.isEmpty() && toChange.isEmpty()) { if (newScreenSize != currentScreenSize) { setScreenSize(newScreenSize); } return; } for (const KScreen::OutputPtr &output : toDisable) { disableOutput(output); } if (intermediateScreenSize != currentScreenSize) { setScreenSize(intermediateScreenSize); } bool forceScreenSizeUpdate = false; for (const KScreen::OutputPtr &output : toChange) { if (!changeOutput(output)) { /* If we disabled the output before changing it and XRandR failed * to re-enable it, then update screen size too */ if (toDisable.contains(output->id())) { output->setEnabled(false); qCDebug(KSCREEN_XRANDR) << "Output failed to change: " << output->name(); forceScreenSizeUpdate = true; } } } for (const KScreen::OutputPtr &output : toEnable) { if (!enableOutput(output)) { qCDebug(KSCREEN_XRANDR) << "Output failed to be Enabled: " << output->name(); forceScreenSizeUpdate = true; } } for (auto it = prioritiesChange.constBegin(); it != prioritiesChange.constEnd(); it++) { const xcb_randr_output_t outputId = it.key(); const auto &[before, after] = it.value(); const uint32_t priority = after.value_or(0); setOutputPriority(outputId, priority); } if (forceScreenSizeUpdate || intermediateScreenSize != newScreenSize) { QSize newSize = newScreenSize; if (forceScreenSizeUpdate) { newSize = screenSize(config); qCDebug(KSCREEN_XRANDR) << "Forced to change screen size: " << newSize; } setScreenSize(newSize); } } void XRandRConfig::printConfig(const ConfigPtr &config) const { qCDebug(KSCREEN_XRANDR) << "KScreen version:" /*<< LIBKSCREEN_VERSION*/; if (!config) { qCDebug(KSCREEN_XRANDR) << "Config is invalid"; return; } if (!config->screen()) { qCDebug(KSCREEN_XRANDR) << "No screen in the configuration, broken backend"; return; } qCDebug(KSCREEN_XRANDR) << "Screen:" << "\n" << "\tmaxSize:" << config->screen()->maxSize() << "\n" << "\tminSize:" << config->screen()->minSize() << "\n" << "\tcurrentSize:" << config->screen()->currentSize(); const OutputList outputs = config->outputs(); for (const OutputPtr &output : outputs) { qCDebug(KSCREEN_XRANDR) << "\n-----------------------------------------------------\n" << "\n" << "Id: " << output->id() << "\n" << "Name: " << output->name() << "\n" << "Type: " << output->type() << "\n" << "Connected: " << output->isConnected(); if (!output->isConnected()) { continue; } qCDebug(KSCREEN_XRANDR) << "Enabled: " << output->isEnabled() << "\n" << "Priority: " << output->priority() << "\n" << "Rotation: " << output->rotation() << "\n" << "Pos: " << output->pos() << "\n" << "MMSize: " << output->sizeMm(); if (output->currentMode()) { qCDebug(KSCREEN_XRANDR) << "Size: " << output->currentMode()->size(); } qCDebug(KSCREEN_XRANDR) << "Clones: " << (output->clones().isEmpty() ? QStringLiteral("None") : QString::number(output->clones().count())) << "\n" << "Mode: " << output->currentModeId() << "\n" << "Preferred Mode: " << output->preferredModeId() << "\n" << "Preferred modes: " << output->preferredModes() << "\n" << "Modes: "; ModeList modes = output->modes(); for (const ModePtr &mode : modes) { qCDebug(KSCREEN_XRANDR) << "\t" << mode->id() << " " << mode->name() << " " << mode->size() << " " << mode->refreshRate(); } Edid *edid = output->edid(); qCDebug(KSCREEN_XRANDR) << "EDID Info: "; if (edid && edid->isValid()) { qCDebug(KSCREEN_XRANDR) << "\tDevice ID: " << edid->deviceId() << "\n" << "\tName: " << edid->name() << "\n" << "\tVendor: " << edid->vendor() << "\n" << "\tSerial: " << edid->serial() << "\n" << "\tEISA ID: " << edid->eisaId() << "\n" << "\tHash: " << edid->hash() << "\n" << "\tWidth: " << edid->width() << "\n" << "\tHeight: " << edid->height() << "\n" << "\tGamma: " << edid->gamma() << "\n" << "\tRed: " << edid->red() << "\n" << "\tGreen: " << edid->green() << "\n" << "\tBlue: " << edid->blue() << "\n" << "\tWhite: " << edid->white(); } else { qCDebug(KSCREEN_XRANDR) << "\tUnavailable"; } } } void XRandRConfig::printInternalCond() const { qCDebug(KSCREEN_XRANDR) << "Internal config in xrandr"; for (const XRandROutput *output : m_outputs) { qCDebug(KSCREEN_XRANDR) << "Id: " << output->id() << "\n" << "Current Mode: " << output->currentMode() << "\n" << "Current mode id: " << output->currentModeId() << "\n" << "Connected: " << output->isConnected() << "\n" << "Enabled: " << output->isEnabled() << "\n" << "Priority: " << output->priority(); if (!output->isEnabled()) { continue; } XRandRMode::Map modes = output->modes(); for (const XRandRMode *mode : modes) { qCDebug(KSCREEN_XRANDR) << "\t" << mode->id() << "\n" << "\t" << mode->name() << "\n" << "\t" << mode->size() << mode->refreshRate(); } } } QSize XRandRConfig::screenSize(const KScreen::ConfigPtr &config) const { QRect rect; for (const KScreen::OutputPtr &output : config->outputs()) { if (!output->isConnected() || !output->isEnabled()) { continue; } const ModePtr currentMode = output->currentMode(); if (!currentMode) { qCDebug(KSCREEN_XRANDR) << "Output: " << output->name() << " has no current Mode!"; continue; } const QRect outputGeom = output->geometry(); rect = rect.united(outputGeom); } const QSize size = QSize(rect.width(), rect.height()); qCDebug(KSCREEN_XRANDR) << "Requested screen size is" << size; return size; } bool XRandRConfig::setScreenSize(const QSize &size) const { const double dpi = 25.4 * XRandR::screen()->height_in_pixels / XRandR::screen()->height_in_millimeters; const int widthMM = (25.4 * size.width()) / dpi; const int heightMM = (25.4 * size.height()) / dpi; qCDebug(KSCREEN_XRANDR) << "RRSetScreenSize" << "\n" << "\tDPI:" << dpi << "\n" << "\tSize:" << size << "\n" << "\tSizeMM:" << QSize(widthMM, heightMM); xcb_randr_set_screen_size(XCB::connection(), XRandR::rootWindow(), size.width(), size.height(), widthMM, heightMM); m_screen->update(size); return true; } void XRandRConfig::setOutputPriority(xcb_randr_output_t outputId, uint32_t priority) const { qCDebug(KSCREEN_XRANDR) << "RRSetOutputPrimary" << "\n" << "\tNew priority:" << priority; if (m_outputs.contains(outputId)) { m_outputs[outputId]->setPriority(priority); } } bool XRandRConfig::disableOutput(const OutputPtr &kscreenOutput) const { XRandROutput *xOutput = output(kscreenOutput->id()); Q_ASSERT(xOutput); Q_ASSERT(xOutput->crtc()); if (!xOutput->crtc()) { qCWarning(KSCREEN_XRANDR) << "Attempting to disable output without CRTC, wth?"; return false; } const xcb_randr_crtc_t crtc = xOutput->crtc()->crtc(); qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (disable output)" << "\n" << "\tCRTC:" << crtc; xOutput->setPriority(0); auto cookie = xcb_randr_set_crtc_config(XCB::connection(), // crtc, XCB_CURRENT_TIME, XCB_CURRENT_TIME, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, 0, nullptr); XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); if (!reply) { qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)"; return false; } qCDebug(KSCREEN_XRANDR) << "\tResult:" << reply->status; // Update the cached output now, otherwise we get RRNotify_CrtcChange notification // for an outdated output, which can lead to a crash. if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) { xOutput->update(XCB_NONE, XCB_NONE, xOutput->isConnected() ? XCB_RANDR_CONNECTION_CONNECTED : XCB_RANDR_CONNECTION_DISCONNECTED); if (xOutput->crtc()) xOutput->crtc()->updateTimestamp(reply->timestamp); } return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); } bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const { XRandRCrtc *freeCrtc = nullptr; qCDebug(KSCREEN_XRANDR) << m_crtcs; for (XRandRCrtc *crtc : m_crtcs) { crtc->update(); qCDebug(KSCREEN_XRANDR) << "Testing CRTC" << crtc->crtc() << "\n" << "\tFree:" << crtc->isFree() << "\n" << "\tMode:" << crtc->mode() << "\n" << "\tPossible outputs:" << crtc->possibleOutputs() << "\n" << "\tConnected outputs:" << crtc->outputs() << "\n" << "\tGeometry:" << crtc->geometry(); if (crtc->isFree() && crtc->possibleOutputs().contains(kscreenOutput->id())) { freeCrtc = crtc; break; } } if (!freeCrtc) { qCWarning(KSCREEN_XRANDR) << "Failed to get free CRTC for output" << kscreenOutput->id(); return false; } XRandROutput *xOutput = output(kscreenOutput->id()); const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); xOutput->updateLogicalSize(kscreenOutput, freeCrtc); qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (enable output)" << "\n" << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")" << "\n" << "\tNew CRTC:" << freeCrtc->crtc() << "\n" << "\tPos:" << kscreenOutput->pos() << "\n" << "\tMode:" << kscreenOutput->currentMode() << "Preferred:" << kscreenOutput->preferredModeId() << "\n" << "\tRotation:" << kscreenOutput->rotation(); if (!sendConfig(kscreenOutput, freeCrtc)) { return false; } xOutput->update(freeCrtc->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED); xOutput->setPriority(kscreenOutput->priority()); return true; } bool XRandRConfig::changeOutput(const KScreen::OutputPtr &kscreenOutput) const { XRandROutput *xOutput = output(kscreenOutput->id()); Q_ASSERT(xOutput); if (!xOutput->crtc()) { qCDebug(KSCREEN_XRANDR) << "Output" << kscreenOutput->id() << "has no CRTC, falling back to enableOutput()"; return enableOutput(kscreenOutput); } int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); xOutput->updateLogicalSize(kscreenOutput); qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)" << "\n" << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")" << "\n" << "\tCRTC:" << xOutput->crtc()->crtc() << "\n" << "\tPos:" << kscreenOutput->pos() << "\n" << "\tMode:" << kscreenOutput->currentMode() << "Preferred:" << kscreenOutput->preferredModeId() << "\n" << "\tRotation:" << kscreenOutput->rotation(); if (!sendConfig(kscreenOutput, xOutput->crtc())) { return false; } xOutput->update(xOutput->crtc()->crtc(), modeId, XCB_RANDR_CONNECTION_CONNECTED); xOutput->setPriority(kscreenOutput->priority()); return true; } bool XRandRConfig::sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const { xcb_randr_output_t outputs[1]{static_cast<xcb_randr_output_t>(kscreenOutput->id())}; const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); auto cookie = xcb_randr_set_crtc_config(XCB::connection(), crtc->crtc(), XCB_CURRENT_TIME, XCB_CURRENT_TIME, kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), modeId, kscreenOutput->rotation(), 1, outputs); XCB::ScopedPointer<xcb_randr_set_crtc_config_reply_t> reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); if (!reply) { qCDebug(KSCREEN_XRANDR) << "\tResult: unknown (error)"; return false; } crtc->updateTimestamp(reply->timestamp); qCDebug(KSCREEN_XRANDR) << "\tResult: " << reply->status << " timestamp: " << reply->timestamp; return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); } 07070100000055000081A40000000000000000000000016638555E00000674000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrconfig.h/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <QObject> #include <cstdint> #include "xrandrcrtc.h" #include "xrandroutput.h" class XRandRScreen; namespace KScreen { class Config; } class XRandRConfig : public QObject { Q_OBJECT public: XRandRConfig(); ~XRandRConfig() override; XRandROutput::Map outputs() const; XRandROutput *output(xcb_randr_output_t output) const; XRandRCrtc::Map crtcs() const; XRandRCrtc *crtc(xcb_randr_crtc_t crtc) const; XRandRScreen *screen() const; void addNewOutput(xcb_randr_output_t id); void addNewCrtc(xcb_randr_crtc_t crtc); void removeOutput(xcb_randr_output_t id); KScreen::ConfigPtr toKScreenConfig() const; void applyKScreenConfig(const KScreen::ConfigPtr &config); private: QSize screenSize(const KScreen::ConfigPtr &config) const; bool setScreenSize(const QSize &size) const; void setOutputPriority(xcb_randr_output_t outputId, uint32_t priority) const; bool disableOutput(const KScreen::OutputPtr &output) const; bool enableOutput(const KScreen::OutputPtr &output) const; bool changeOutput(const KScreen::OutputPtr &output) const; bool sendConfig(const KScreen::OutputPtr &kscreenOutput, XRandRCrtc *crtc) const; /** * We need to print stuff to discover the damn bug * where currentMode is null */ void printConfig(const KScreen::ConfigPtr &config) const; void printInternalCond() const; XRandROutput::Map m_outputs; XRandRCrtc::Map m_crtcs; XRandRScreen *m_screen; }; 07070100000056000081A40000000000000000000000016638555E00000D0E000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrcrtc.cpp/* * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL * */ #include "xrandrcrtc.h" #include "xrandr.h" #include "xrandrconfig.h" #include "../xcbwrapper.h" XRandRCrtc::XRandRCrtc(xcb_randr_crtc_t crtc, XRandRConfig *config) : QObject(config) , m_crtc(crtc) , m_mode(0) , m_rotation(XCB_RANDR_ROTATION_ROTATE_0) , m_timestamp(XCB_CURRENT_TIME) , m_configTimestamp(XCB_CURRENT_TIME) { update(); } xcb_randr_crtc_t XRandRCrtc::crtc() const { return m_crtc; } xcb_randr_mode_t XRandRCrtc::mode() const { return m_mode; } QRect XRandRCrtc::geometry() const { return m_geometry; } xcb_randr_rotation_t XRandRCrtc::rotation() const { return m_rotation; } QVector<xcb_randr_output_t> XRandRCrtc::possibleOutputs() { return m_possibleOutputs; } QVector<xcb_randr_output_t> XRandRCrtc::outputs() const { return m_outputs; } bool XRandRCrtc::connectOutput(xcb_randr_output_t output) { update(); qCDebug(KSCREEN_XRANDR) << "Connected output" << output << "to CRTC" << m_crtc; if (!m_possibleOutputs.contains(output)) { qCDebug(KSCREEN_XRANDR) << "Output" << output << "is not an allowed output for CRTC" << m_crtc; return false; } if (!m_outputs.contains(output)) { m_outputs.append(output); } return true; } void XRandRCrtc::disconectOutput(xcb_randr_output_t output) { update(); qCDebug(KSCREEN_XRANDR) << "Disconnected output" << output << "from CRTC" << m_crtc; const int index = m_outputs.indexOf(output); if (index > -1) { m_outputs.remove(index); } } bool XRandRCrtc::isFree() const { return m_outputs.isEmpty(); } void XRandRCrtc::update() { XCB::CRTCInfo crtcInfo(m_crtc, XCB_TIME_CURRENT_TIME); m_mode = crtcInfo->mode; m_geometry = QRect(crtcInfo->x, crtcInfo->y, crtcInfo->width, crtcInfo->height); m_rotation = (xcb_randr_rotation_t)crtcInfo->rotation; m_possibleOutputs.clear(); m_possibleOutputs.reserve(crtcInfo->num_possible_outputs); xcb_randr_output_t *possible = xcb_randr_get_crtc_info_possible(crtcInfo); for (int i = 0; i < crtcInfo->num_possible_outputs; ++i) { m_possibleOutputs.append(possible[i]); } m_outputs.clear(); xcb_randr_output_t *outputs = xcb_randr_get_crtc_info_outputs(crtcInfo); for (int i = 0; i < crtcInfo->num_outputs; ++i) { m_outputs.append(outputs[i]); } } void XRandRCrtc::update(xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, const QRect &geom) { m_mode = mode; m_geometry = geom; m_rotation = rotation; } void XRandRCrtc::updateTimestamp(const xcb_timestamp_t tmstamp) { if (tmstamp > m_timestamp) { qCDebug(KSCREEN_XRANDR) << "XRandRCrtc " << m_crtc << " m_timestamp update " << m_timestamp << " => " << tmstamp; m_timestamp = tmstamp; } } void XRandRCrtc::updateConfigTimestamp(const xcb_timestamp_t tmstamp) { if (tmstamp > m_configTimestamp) { qCDebug(KSCREEN_XRANDR) << "XRandRCrtc " << m_crtc << " m_configTimestamp update" << m_configTimestamp << " => " << tmstamp; m_configTimestamp = tmstamp; } } bool XRandRCrtc::isChangedFromOutside() const { return m_configTimestamp > m_timestamp; } 07070100000057000081A40000000000000000000000016638555E0000058A000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrcrtc.h/* * SPDX-FileCopyrightText: 2015 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL * */ #pragma once #include <QMap> #include <QObject> #include <QRect> #include <QVector> #include <xcb/randr.h> class XRandRConfig; class XRandRCrtc : public QObject { Q_OBJECT public: typedef QMap<xcb_randr_crtc_t, XRandRCrtc *> Map; XRandRCrtc(xcb_randr_crtc_t crtc, XRandRConfig *config); xcb_randr_crtc_t crtc() const; xcb_randr_mode_t mode() const; QRect geometry() const; xcb_randr_rotation_t rotation() const; QVector<xcb_randr_output_t> possibleOutputs(); QVector<xcb_randr_output_t> outputs() const; bool connectOutput(xcb_randr_output_t output); void disconectOutput(xcb_randr_output_t output); bool isFree() const; void update(); void update(xcb_randr_crtc_t mode, xcb_randr_rotation_t rotation, const QRect &geom); void updateTimestamp(const xcb_timestamp_t tmstamp); void updateConfigTimestamp(const xcb_timestamp_t tmstamp); bool isChangedFromOutside() const; private: xcb_randr_crtc_t m_crtc; xcb_randr_mode_t m_mode; QRect m_geometry; xcb_randr_rotation_t m_rotation; QVector<xcb_randr_output_t> m_possibleOutputs; QVector<xcb_randr_output_t> m_outputs; xcb_timestamp_t m_timestamp; xcb_timestamp_t m_configTimestamp; }; 07070100000058000081A40000000000000000000000016638555E000005E6000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrmode.cpp/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandrmode.h" #include "xrandroutput.h" #include "mode.h" XRandRMode::XRandRMode(const xcb_randr_mode_info_t &modeInfo, XRandROutput *output) : QObject(output) { m_id = modeInfo.id; // FIXME XCB // m_name = QString::fromUtf8(modeInfo->name); m_size = QSize(modeInfo.width, modeInfo.height); double vTotal = modeInfo.vtotal; if (modeInfo.mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN) { /* doublescan doubles the number of lines */ vTotal *= 2; } if (modeInfo.mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE) { /* interlace splits the frame into two fields */ /* the field rate is what is typically reported by monitors */ vTotal /= 2; } m_refreshRate = (float)modeInfo.dot_clock / ((float)modeInfo.htotal * vTotal); } XRandRMode::~XRandRMode() { } KScreen::ModePtr XRandRMode::toKScreenMode() { KScreen::ModePtr kscreenMode(new KScreen::Mode); kscreenMode->setId(QString::number(m_id)); kscreenMode->setName(m_name); kscreenMode->setSize(m_size); kscreenMode->setRefreshRate(m_refreshRate); return kscreenMode; } xcb_randr_mode_t XRandRMode::id() const { return m_id; } QSize XRandRMode::size() const { return m_size; } float XRandRMode::refreshRate() const { return m_refreshRate; } QString XRandRMode::name() const { return m_name; } 07070100000059000081A40000000000000000000000016638555E0000036A000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrmode.h/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include <QMap> #include <QObject> #include <QSize> #include <QVariant> #include <xcb/randr.h> #include <xcb/xcb.h> #include "types.h" class XRandROutput; namespace KScreen { class Output; class Mode; } class XRandRMode : public QObject { Q_OBJECT public: typedef QMap<xcb_randr_mode_t, XRandRMode *> Map; explicit XRandRMode(const xcb_randr_mode_info_t &modeInfo, XRandROutput *output); ~XRandRMode() override; KScreen::ModePtr toKScreenMode(); xcb_randr_mode_t id() const; QSize size() const; float refreshRate() const; QString name() const; private: xcb_randr_mode_t m_id; QString m_name; QSize m_size; float m_refreshRate; }; Q_DECLARE_METATYPE(XRandRMode::Map) 0707010000005A000081A40000000000000000000000016638555E00003E7B000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandroutput.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandroutput.h" #include "xrandr.h" #include "xrandrconfig.h" #include "../utils.h" #include "mode.h" #include <array> #include <cstring> #include <qglobal.h> #include <utility> #include <xcb/randr.h> #include <xcb/render.h> Q_DECLARE_METATYPE(QList<int>) #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t)((d)*65536)) #define FIXED_TO_DOUBLE(f) ((double)((f) / 65536.0)) xcb_render_fixed_t fOne = DOUBLE_TO_FIXED(1); xcb_render_fixed_t fZero = DOUBLE_TO_FIXED(0); XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config) : QObject(config) , m_config(config) , m_id(id) , m_type(KScreen::Output::Unknown) , m_crtc(nullptr) { init(); } XRandROutput::~XRandROutput() { } xcb_randr_output_t XRandROutput::id() const { return m_id; } bool XRandROutput::isConnected() const { return m_connected == XCB_RANDR_CONNECTION_CONNECTED; } bool XRandROutput::isEnabled() const { return m_crtc != nullptr && m_crtc->mode() != XCB_NONE; } bool XRandROutput::isPrimary() const { return priority() == 1; } uint32_t XRandROutput::priority() const { if (isConnected() && isEnabled()) { return outputPriorityFromProperty(); } else { return 0; } } void XRandROutput::setPriority(XRandROutput::Priority newPriority) { if (priority() != newPriority) { setOutputPriorityToProperty(newPriority); } // Always update the primary regardless of it having changed. If a primary gets unplugged and plugged back in we'd // otherwise end up with priorities not being in sync with xrandr primary because the effective priorities in the // atoms haven't changed; also see priority(). if (newPriority == 1) { setAsPrimary(); } } QPoint XRandROutput::position() const { return m_crtc ? m_crtc->geometry().topLeft() : QPoint(); } QSize XRandROutput::size() const { return m_crtc ? m_crtc->geometry().size() : QSize(); } XRandRMode::Map XRandROutput::modes() const { return m_modes; } QString XRandROutput::currentModeId() const { return m_crtc ? QString::number(m_crtc->mode()) : QString(); } XRandRMode *XRandROutput::currentMode() const { if (!m_crtc) { return nullptr; } unsigned int modeId = m_crtc->mode(); if (!m_modes.contains(modeId)) { return nullptr; } return m_modes[modeId]; } KScreen::Output::Rotation XRandROutput::rotation() const { return static_cast<KScreen::Output::Rotation>(m_crtc ? m_crtc->rotation() : XCB_RANDR_ROTATION_ROTATE_0); } bool XRandROutput::isHorizontal() const { const auto rot = rotation(); return rot == KScreen::Output::Rotation::None || rot == KScreen::Output::Rotation::Inverted; } QByteArray XRandROutput::edid() const { if (m_edid.isNull()) { m_edid = XRandR::outputEdid(m_id); } return m_edid; } XRandRCrtc *XRandROutput::crtc() const { return m_crtc; } void XRandROutput::update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn) { qCDebug(KSCREEN_XRANDR) << "XRandROutput" << m_id << "update" << "\n" << "\tm_connected:" << m_connected << "\n" << "\tm_crtc" << m_crtc << "\n" << "\tCRTC:" << crtc << "\n" << "\tMODE:" << mode << "\n" << "\tConnection:" << conn; // Connected or disconnected if (isConnected() != (conn == XCB_RANDR_CONNECTION_CONNECTED)) { if (conn == XCB_RANDR_CONNECTION_CONNECTED) { // New monitor has been connected, refresh everything init(); } else { // Disconnected m_connected = conn; m_clones.clear(); m_heightMm = 0; m_widthMm = 0; m_type = KScreen::Output::Unknown; qDeleteAll(m_modes); m_modes.clear(); m_preferredModes.clear(); m_edid.clear(); } } else if (conn == XCB_RANDR_CONNECTION_CONNECTED) { // the output changed in some way, let's update the internal // list of modes, as it may have changed XCB::OutputInfo outputInfo(m_id, XCB_TIME_CURRENT_TIME); if (outputInfo) { updateModes(outputInfo); } m_hotplugModeUpdate = XRandR::hasProperty(m_id, "hotplug_mode_update"); m_edid.clear(); } // A monitor has been enabled or disabled // We don't use isEnabled(), because it checks for crtc && crtc->mode(), however // crtc->mode may already be unset due to xcb_randr_crtc_tChangeNotify coming before // xcb_randr_output_tChangeNotify and reseting the CRTC mode if ((m_crtc == nullptr) != (crtc == XCB_NONE)) { if (crtc == XCB_NONE && mode == XCB_NONE) { // Monitor has been disabled m_crtc->disconectOutput(m_id); m_crtc = nullptr; } else { m_crtc = m_config->crtc(crtc); m_crtc->connectOutput(m_id); } } } static constexpr const char *KDE_SCREEN_INDEX = "_KDE_SCREEN_INDEX"; XRandROutput::Priority XRandROutput::outputPriorityFromProperty() const { if (!isConnected()) { return 0; } xcb_atom_t screen_index_atom = XCB::InternAtom(/* only_if_exists */ false, strlen(KDE_SCREEN_INDEX), KDE_SCREEN_INDEX)->atom; auto cookie = xcb_randr_get_output_property(XCB::connection(), m_id, screen_index_atom, XCB_ATOM_INTEGER, /*offset*/ 0, /*length*/ 1, /*delete*/ false, /*pending*/ false); XCB::ScopedPointer<xcb_randr_get_output_property_reply_t> reply(xcb_randr_get_output_property_reply(XCB::connection(), cookie, nullptr)); if (!reply) { return 0; } if (!(reply->type == XCB_ATOM_INTEGER && reply->format == PRIORITY_FORMAT && reply->num_items == 1)) { return 0; } const uint8_t *prop = xcb_randr_get_output_property_data(reply.data()); const Priority priority = *reinterpret_cast<const Priority *>(prop); return priority; } void XRandROutput::setOutputPriorityToProperty(Priority priority) { if (!isConnected()) { return; } const std::array<Priority, 1> data = {priority}; xcb_atom_t screen_index_atom = XCB::InternAtom(/* only_if_exists */ false, strlen(KDE_SCREEN_INDEX), KDE_SCREEN_INDEX)->atom; xcb_randr_change_output_property(XCB::connection(), // m_id, screen_index_atom, XCB_ATOM_INTEGER, PRIORITY_FORMAT, XCB_PROP_MODE_REPLACE, data.size(), data.data()); } void XRandROutput::setAsPrimary() { if (isConnected() && isEnabled()) { xcb_randr_set_output_primary(XCB::connection(), XRandR::rootWindow(), m_id); } } void XRandROutput::init() { XCB::OutputInfo outputInfo(m_id, XCB_TIME_CURRENT_TIME); Q_ASSERT(outputInfo); if (!outputInfo) { return; } m_name = QString::fromUtf8((const char *)xcb_randr_get_output_info_name(outputInfo.data()), outputInfo->name_len); m_type = fetchOutputType(m_id, m_name); m_icon = QString(); m_connected = (xcb_randr_connection_t)outputInfo->connection; xcb_randr_output_t *clones = xcb_randr_get_output_info_clones(outputInfo.data()); for (int i = 0; i < outputInfo->num_clones; ++i) { m_clones.append(clones[i]); } m_widthMm = outputInfo->mm_width; m_heightMm = outputInfo->mm_height; m_crtc = m_config->crtc(outputInfo->crtc); if (m_crtc) { m_crtc->connectOutput(m_id); } m_hotplugModeUpdate = XRandR::hasProperty(m_id, "hotplug_mode_update"); updateModes(outputInfo); } void XRandROutput::updateModes(const XCB::OutputInfo &outputInfo) { /* Init modes */ XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> screenResources(XRandR::screenResources()); Q_ASSERT(screenResources); if (!screenResources) { return; } xcb_randr_mode_info_t *modes = xcb_randr_get_screen_resources_modes(screenResources.data()); xcb_randr_mode_t *outputModes = xcb_randr_get_output_info_modes(outputInfo.data()); m_preferredModes.clear(); qDeleteAll(m_modes); m_modes.clear(); for (int i = 0; i < outputInfo->num_modes; ++i) { /* Resources->modes contains all possible modes, we are only interested * in those listed in outputInfo->modes. */ for (int j = 0; j < screenResources->num_modes; ++j) { if (modes[j].id != outputModes[i]) { continue; } XRandRMode *mode = new XRandRMode(modes[j], this); m_modes.insert(mode->id(), mode); if (i < outputInfo->num_preferred) { m_preferredModes.append(QString::number(mode->id())); } break; } } } KScreen::Output::Type XRandROutput::fetchOutputType(xcb_randr_output_t outputId, const QString &name) { QString type = QString::fromUtf8(typeFromProperty(outputId)); if (type.isEmpty()) { type = name; } return Utils::guessOutputType(type, name); } QByteArray XRandROutput::typeFromProperty(xcb_randr_output_t outputId) { QByteArray type; XCB::InternAtom atomType(true, 13, "ConnectorType"); if (!atomType) { return type; } auto cookie = xcb_randr_get_output_property(XCB::connection(), outputId, atomType->atom, XCB_ATOM_ANY, 0, 100, false, false); XCB::ScopedPointer<xcb_randr_get_output_property_reply_t> reply(xcb_randr_get_output_property_reply(XCB::connection(), cookie, nullptr)); if (!reply) { return type; } if (!(reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->num_items == 1)) { return type; } const uint8_t *prop = xcb_randr_get_output_property_data(reply.data()); XCB::AtomName atomName(*reinterpret_cast<const xcb_atom_t *>(prop)); if (!atomName) { return type; } return QByteArray(xcb_get_atom_name_name(atomName), xcb_get_atom_name_name_length(atomName)); } bool isScaling(const xcb_render_transform_t &tr) { return tr.matrix11 != fZero && tr.matrix12 == fZero && tr.matrix13 == fZero && tr.matrix21 == fZero && tr.matrix22 != fZero && tr.matrix23 == fZero && tr.matrix31 == fZero && tr.matrix32 == fZero && tr.matrix33 == fOne; } xcb_render_transform_t zeroTransform() { return {DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0)}; } xcb_render_transform_t unityTransform() { return {DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1)}; } xcb_render_transform_t XRandROutput::currentTransform() const { auto cookie = xcb_randr_get_crtc_transform(XCB::connection(), m_crtc->crtc()); xcb_generic_error_t *error = nullptr; auto *reply = xcb_randr_get_crtc_transform_reply(XCB::connection(), cookie, &error); if (error) { return zeroTransform(); } const xcb_render_transform_t transform = reply->pending_transform; free(reply); return transform; } QSizeF XRandROutput::logicalSize() const { const QSize modeSize = size(); if (!modeSize.isValid()) { return QSize(); } const xcb_render_transform_t transform = currentTransform(); const qreal width = FIXED_TO_DOUBLE(transform.matrix11) * modeSize.width(); const qreal height = FIXED_TO_DOUBLE(transform.matrix22) * modeSize.height(); return QSizeF(width, height); } void XRandROutput::updateLogicalSize(const KScreen::OutputPtr &output, XRandRCrtc *crtc) { if (!crtc) { // TODO: This is a workaround for now when updateLogicalSize is called on enabling the // output. At this point m_crtc is not yet set. Change the order in the future so // that the additional argument is not necessary anymore. crtc = m_crtc; } const QSizeF logicalSize = output->explicitLogicalSize(); xcb_render_transform_t transform = unityTransform(); KScreen::ModePtr mode = output->currentMode() ? output->currentMode() : output->preferredMode(); if (mode && logicalSize.isValid()) { QSize modeSize = mode->size(); if (!output->isHorizontal()) { modeSize.transpose(); } const qreal widthFactor = logicalSize.width() / (qreal)modeSize.width(); const qreal heightFactor = logicalSize.height() / (qreal)modeSize.height(); transform.matrix11 = DOUBLE_TO_FIXED(widthFactor); transform.matrix22 = DOUBLE_TO_FIXED(heightFactor); } QByteArray filterName(isScaling(transform) ? "bilinear" : "nearest"); auto cookie = xcb_randr_set_crtc_transform_checked(XCB::connection(), crtc->crtc(), transform, filterName.size(), filterName.data(), 0, nullptr); xcb_generic_error_t *error = xcb_request_check(XCB::connection(), cookie); if (error) { qCDebug(KSCREEN_XRANDR) << "Error on logical size transformation!"; free(error); } } KScreen::OutputPtr XRandROutput::toKScreenOutput() const { KScreen::OutputPtr kscreenOutput(new KScreen::Output); const bool signalsBlocked = kscreenOutput->signalsBlocked(); kscreenOutput->blockSignals(true); kscreenOutput->setId(m_id); kscreenOutput->setType(m_type); kscreenOutput->setSizeMm(QSize(m_widthMm, m_heightMm)); kscreenOutput->setName(m_name); kscreenOutput->setIcon(m_icon); kscreenOutput->setPriority(priority()); // See https://bugzilla.redhat.com/show_bug.cgi?id=1290586 // QXL will be creating a new mode we need to jump to every time the display is resized kscreenOutput->setFollowPreferredMode(m_hotplugModeUpdate && m_crtc && m_crtc->isChangedFromOutside()); kscreenOutput->setConnected(isConnected()); if (isConnected()) { KScreen::ModeList kscreenModes; for (auto iter = m_modes.constBegin(), end = m_modes.constEnd(); iter != end; ++iter) { XRandRMode *mode = iter.value(); kscreenModes.insert(QString::number(iter.key()), mode->toKScreenMode()); } kscreenOutput->setModes(kscreenModes); kscreenOutput->setPreferredModes(m_preferredModes); kscreenOutput->setClones([](const QList<xcb_randr_output_t> &clones) { QList<int> kclones; kclones.reserve(clones.size()); for (xcb_randr_output_t o : clones) { kclones.append(static_cast<int>(o)); } return kclones; }(m_clones)); kscreenOutput->setEnabled(isEnabled()); if (isEnabled()) { kscreenOutput->setSize(size()); kscreenOutput->setPos(position()); kscreenOutput->setRotation(rotation()); kscreenOutput->setCurrentModeId(currentModeId()); } // TODO: set logical size? } kscreenOutput->blockSignals(signalsBlocked); return kscreenOutput; } 0707010000005B000081A40000000000000000000000016638555E0000099D000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandroutput.h/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "xrandrmode.h" #include "../xcbwrapper.h" #include "output.h" #include <QMap> #include <QObject> #include <QVariant> #include <cstdint> #include <limits> class XRandRConfig; class XRandRCrtc; namespace KScreen { class Config; class Output; } class XRandROutput : public QObject { Q_OBJECT public: typedef QMap<xcb_randr_output_t, XRandROutput *> Map; using Priority = uint32_t; static constexpr size_t PRIORITY_FORMAT = std::numeric_limits<Priority>::digits; explicit XRandROutput(xcb_randr_output_t id, XRandRConfig *config); ~XRandROutput() override; void disabled(); void disconnected(); void update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn); xcb_randr_output_t id() const; bool isEnabled() const; bool isConnected() const; bool isPrimary() const; Priority priority() const; void setPriority(Priority priority); QPoint position() const; QSize size() const; QSizeF logicalSize() const; QString currentModeId() const; XRandRMode::Map modes() const; XRandRMode *currentMode() const; KScreen::Output::Rotation rotation() const; bool isHorizontal() const; QByteArray edid() const; XRandRCrtc *crtc() const; KScreen::OutputPtr toKScreenOutput() const; void updateLogicalSize(const KScreen::OutputPtr &output, XRandRCrtc *crtc = nullptr); private: void init(); void updateModes(const XCB::OutputInfo &outputInfo); Priority outputPriorityFromProperty() const; void setOutputPriorityToProperty(Priority priority); void setAsPrimary(); static KScreen::Output::Type fetchOutputType(xcb_randr_output_t outputId, const QString &name); static QByteArray typeFromProperty(xcb_randr_output_t outputId); xcb_render_transform_t currentTransform() const; XRandRConfig *m_config; xcb_randr_output_t m_id; QString m_name; QString m_icon; mutable QByteArray m_edid; xcb_randr_connection_t m_connected; KScreen::Output::Type m_type; XRandRMode::Map m_modes; QStringList m_preferredModes; QList<xcb_randr_output_t> m_clones; unsigned int m_widthMm; unsigned int m_heightMm; bool m_hotplugModeUpdate = false; XRandRCrtc *m_crtc; }; Q_DECLARE_METATYPE(XRandROutput::Map) 0707010000005C000081A40000000000000000000000016638555E000006D9000000000000000000000000000000000000004F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrscreen.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandrscreen.h" #include "xrandr.h" #include "xrandrconfig.h" #include "../xcbwrapper.h" #include "screen.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif XRandRScreen::XRandRScreen(XRandRConfig *config) : QObject(config) { XCB::ScreenSize size(XRandR::rootWindow()); m_maxSize = QSize(size->max_width, size->max_height); m_minSize = QSize(size->min_width, size->min_height); update(); } XRandRScreen::~XRandRScreen() { } void XRandRScreen::update() { const int appScreen = QX11Info::appScreen(); m_id = appScreen; xcb_screen_t *screen = XCB::screenOfDisplay(XCB::connection(), appScreen); m_currentSize = QSize(screen->width_in_pixels, screen->height_in_pixels); } void XRandRScreen::update(const QSize &size) { m_currentSize = size; } QSize XRandRScreen::currentSize() { return m_currentSize; } KScreen::ScreenPtr XRandRScreen::toKScreenScreen() const { KScreen::ScreenPtr kscreenScreen(new KScreen::Screen); kscreenScreen->setId(m_id); kscreenScreen->setMaxSize(m_maxSize); kscreenScreen->setMinSize(m_minSize); kscreenScreen->setCurrentSize(m_currentSize); XCB::ScopedPointer<xcb_randr_get_screen_resources_reply_t> screenResources(XRandR::screenResources()); kscreenScreen->setMaxActiveOutputsCount(screenResources->num_crtcs); return kscreenScreen; } void XRandRScreen::updateKScreenScreen(KScreen::ScreenPtr &screen) const { screen->setCurrentSize(m_currentSize); } 0707010000005D000081A40000000000000000000000016638555E000002C7000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr/xrandrscreen.h/* * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include "types.h" #include <QObject> #include <QSize> class XRandRConfig; namespace KScreen { class Screen; class Config; } class XRandRScreen : public QObject { Q_OBJECT public: explicit XRandRScreen(XRandRConfig *config = nullptr); ~XRandRScreen() override; KScreen::ScreenPtr toKScreenScreen() const; void updateKScreenScreen(KScreen::ScreenPtr &screen) const; void update(); void update(const QSize &size); QSize currentSize(); private: int m_id; QSize m_minSize; QSize m_maxSize; QSize m_currentSize; }; 0707010000005E000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000004100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr1.10707010000005F000081A40000000000000000000000016638555E000003AA000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr1.1/CMakeLists.txtfind_package(XCB REQUIRED COMPONENTS XCB RANDR) include_directories(${CMAKE_SOURCE_DIR}/src ${CMAKE_BUILD_DIR} ${QT_INCLUDES} ) add_library(KSC_XRandR11 MODULE) target_sources(KSC_XRandR11 PRIVATE xrandr11.cpp ../xcbeventlistener.cpp ../xcbwrapper.cpp ) set_target_properties(KSC_XRandR11 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf${QT_MAJOR_VERSION}/kscreen") set_target_properties(KSC_XRandR11 PROPERTIES PREFIX "") target_link_libraries(KSC_XRandR11 Qt::Core Qt::Gui ${XCB_LIBRARIES} KF5::Screen ) if (QT_MAJOR_VERSION EQUAL "5") target_link_libraries(KSC_XRandR11 Qt::X11Extras) else() target_link_libraries(KSC_XRandR11 Qt::GuiPrivate) endif() install(TARGETS KSC_XRandR11 DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf${QT_MAJOR_VERSION}/kscreen/) 07070100000060000081A40000000000000000000000016638555E00001704000000000000000000000000000000000000004E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr1.1/xrandr11.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "xrandr11.h" #include "../xcbeventlistener.h" #include "../xcbwrapper.h" #include "config.h" #include "mode.h" #include "output.h" #include "screen.h" #include <xcb/randr.h> #include <QDebug> #include <QString> Q_LOGGING_CATEGORY(KSCREEN_XRANDR11, "kscreen.xrandr11") XRandR11::XRandR11() : KScreen::AbstractBackend() , m_valid(false) , m_x11Helper(nullptr) , m_currentConfig(new KScreen::Config) , m_currentTimestamp(0) { xcb_generic_error_t *error = nullptr; xcb_randr_query_version_reply_t *version; version = xcb_randr_query_version_reply(XCB::connection(), // xcb_randr_query_version(XCB::connection(), XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), &error); if (!version || error) { free(error); XCB::closeConnection(); qCDebug(KSCREEN_XRANDR11) << "Can't get XRandR version"; return; } if (version->major_version != 1 || version->minor_version != 1) { XCB::closeConnection(); qCDebug(KSCREEN_XRANDR11) << "This backend is only for XRandR 1.1, your version is: " << version->major_version << "." << version->minor_version; return; } m_x11Helper = new XCBEventListener(); connect(m_x11Helper, &XCBEventListener::outputsChanged, this, &XRandR11::updateConfig); m_valid = true; } XRandR11::~XRandR11() { XCB::closeConnection(); delete m_x11Helper; } QString XRandR11::name() const { return QStringLiteral("XRandR 1.1"); } QString XRandR11::serviceName() const { return QStringLiteral("org.kde.KScreen.Backend.XRandR11"); } bool XRandR11::isValid() const { return m_valid; } KScreen::ConfigPtr XRandR11::config() const { KScreen::ConfigPtr config(new KScreen::Config); auto features = KScreen::Config::Feature::Writable | KScreen::Config::Feature::PrimaryDisplay; config->setSupportedFeatures(features); const int screenId = QX11Info::appScreen(); xcb_screen_t *xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId); const XCB::ScreenInfo info(xcbScreen->root); const XCB::ScreenSize size(xcbScreen->root); if (info->config_timestamp == m_currentTimestamp) { return m_currentConfig; } KScreen::ScreenPtr screen(new KScreen::Screen); screen->setId(screenId); screen->setCurrentSize(QSize(xcbScreen->width_in_pixels, xcbScreen->height_in_pixels)); if (size) { // RRGetScreenSize may file on VNC/RDP connections screen->setMaxSize(QSize(size->max_width, size->max_height)); screen->setMinSize(QSize(size->min_width, size->min_height)); } else { screen->setMaxSize(screen->currentSize()); screen->setMinSize(screen->currentSize()); } screen->setMaxActiveOutputsCount(1); config->setScreen(screen); KScreen::OutputList outputs; KScreen::OutputPtr output(new KScreen::Output); output->setId(1); output->setConnected(true); output->setEnabled(true); output->setName(QStringLiteral("Default")); output->setPos(QPoint(0, 0)); output->setPriority(1); output->setRotation((KScreen::Output::Rotation)info->rotation); output->setSizeMm(QSize(xcbScreen->width_in_millimeters, xcbScreen->height_in_millimeters)); outputs.insert(1, output); config->setOutputs(outputs); KScreen::ModePtr mode; KScreen::ModeList modes; auto iter = xcb_randr_get_screen_info_rates_iterator(info); xcb_randr_screen_size_t *sizes = xcb_randr_get_screen_info_sizes(info); for (int x = 0; x < info->nSizes; x++) { const xcb_randr_screen_size_t size = sizes[x]; const uint16_t *rates = xcb_randr_refresh_rates_rates(iter.data); const int nrates = xcb_randr_refresh_rates_rates_length(iter.data); for (int j = 0; j < nrates; j++) { float rate = rates[j]; mode = KScreen::ModePtr(new KScreen::Mode); mode->setId(QStringLiteral("%1-%2").arg(x).arg(j)); mode->setSize(QSize(size.width, size.height)); mode->setRefreshRate(rate); mode->setName(QStringLiteral("%1x%2").arg(size.width).arg(size.height)); if (x == info->sizeID && rate == info->rate) { output->setCurrentModeId(mode->id()); output->setSize(mode->size()); } modes.insert(mode->id(), mode); } xcb_randr_refresh_rates_next(&iter); } output->setModes(modes); return config; } void XRandR11::setConfig(const KScreen::ConfigPtr &config) { const KScreen::OutputPtr output = config->outputs().take(1); const KScreen::ModePtr mode = output->currentMode(); const int screenId = QX11Info::appScreen(); xcb_screen_t *xcbScreen = XCB::screenOfDisplay(XCB::connection(), screenId); const XCB::ScreenInfo info(xcbScreen->root); xcb_generic_error_t *err; const int sizeId = mode->id().split(QLatin1Char('-')).first().toInt(); auto cookie = xcb_randr_set_screen_config(XCB::connection(), xcbScreen->root, XCB_CURRENT_TIME, info->config_timestamp, sizeId, (short)output->rotation(), mode->refreshRate()); XCB::ScopedPointer<xcb_randr_set_screen_config_reply_t> reply(xcb_randr_set_screen_config_reply(XCB::connection(), cookie, &err)); if (err) { free(err); } } void XRandR11::updateConfig() { m_currentConfig = config(); Q_EMIT configChanged(m_currentConfig); } 07070100000061000081A40000000000000000000000016638555E000003DD000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/backends/xrandr1.1/xrandr11.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef XRANDR11_BACKEND_H #define XRANDR11_BACKEND_H #include "abstractbackend.h" #include <QLoggingCategory> #include <QObject> #include <xcb/xcb.h> class XCBEventListener; class XRandR11 : public KScreen::AbstractBackend { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kf5.kscreen.backends.xrandr11") public: explicit XRandR11(); ~XRandR11() override; QString name() const override; QString serviceName() const override; KScreen::ConfigPtr config() const override; void setConfig(const KScreen::ConfigPtr &config) override; bool isValid() const override; private Q_SLOTS: void updateConfig(); private: bool m_valid; XCBEventListener *m_x11Helper; KScreen::ConfigPtr m_currentConfig; xcb_timestamp_t m_currentTimestamp; }; Q_DECLARE_LOGGING_CATEGORY(KSCREEN_XRANDR11) #endif // FAKE_BACKEND_H 07070100000062000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/interfaces07070100000063000081A40000000000000000000000016638555E000003DA000000000000000000000000000000000000005500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/interfaces/org.kde.KScreen.Backend.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.kde.kscreen.Backend"> <method name="getConfig"> <arg type="a{sv}" direction="out" /> <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap" /> </method> <method name="setConfig"> <arg type="a{sv}" direction="in" /> <arg type="a{sv}" direction="out" /> <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap" /> <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap" /> </method> <signal name="configChanged"> <arg type="a{sv}" direction="out" /> <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap" /> </signal> <method name="getEdid"> <arg type="i" direction="in" /> <arg type="ay" direction="out" /> </method> </interface> </node> 07070100000064000081A40000000000000000000000016638555E000004E1000000000000000000000000000000000000005900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/interfaces/org.kde.KScreen.FakeBackend.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.kde.kscreen.FakeBackend"> <method name="setConnected"> <arg type="i" name="outputId" direction="in" /> <arg type="b" name="connected" direction="in" /> </method> <method name="setEnabled"> <arg type="i" name="outputId" direction="in" /> <arg type="b" name="enabled" direction="in" /> </method> <method name="setPrimary"> <arg type="i" name="outputId" direction="in" /> <arg type="b" name="primary" direction="in" /> </method> <method name="setCurrentModeId"> <arg type="i" name="outputId" direction="in" /> <arg type="s" name="currentModeId" direction="in" /> </method> <method name="setRotation"> <arg type="i" name="outputId" direction="in" /> <arg type="i" name="rotation" direction="in" /> </method> <method name="addOutput"> <arg type="i" name="outputId" direction="in" /> <arg type="s" name="name" direction="in" /> </method> <method name="removeOutput"> <arg type="i" name="outputId" direction="in" /> </method> </interface> </node> 07070100000065000081A40000000000000000000000016638555E0000023A000000000000000000000000000000000000004D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/interfaces/org.kde.KScreen.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.kde.KScreen"> <method name="backend"> <arg type="s" direction="out" /> </method> <method name="requestBackend"> <arg type="s" direction="in" /> <arg type="a{sv}" direction="in" /> <arg type="b" direction="out" /> <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/> </method> <method name="quit" /> </interface> </node> 07070100000066000041ED0000000000000000000000216638555E00000000000000000000000000000000000000000000003300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm07070100000067000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ca07070100000068000081A40000000000000000000000016638555E00000679000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ca/libkscreen5_qt.po# Translation of libkscreen5_qt.po to Catalan # Copyright (C) 2022 This_file_is_part_of_KDE # This file is distributed under the license LGPL version 2.1 or # version 3 or later versions approved by the membership of KDE e.V. # # Josep M. Ferrer <txemaq@gmail.com>, 2022. msgid "" msgstr "" "Project-Id-Version: libkscreen\n" "PO-Revision-Date: 2022-10-21 13:09+0200\n" "Last-Translator: Josep M. Ferrer <txemaq@gmail.com>\n" "Language-Team: Catalan <kde-i18n-ca@kde.org>\n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 20.12.0\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Ha fallat en preparar el dorsal" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Ha fallat en desserialitzar la resposta del dorsal" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Dorsal invalidat" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Ha fallat en preparar el dorsal" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Ha fallat en serialitzar la sol·licitud" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Ha fallat en desserialitzar la resposta del dorsal" 07070100000069000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ca@valencia0707010000006A000081A40000000000000000000000016638555E00000693000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ca@valencia/libkscreen5_qt.po# Translation of libkscreen5_qt.po to Catalan (Valencian) # Copyright (C) 2022 This_file_is_part_of_KDE # This file is distributed under the license LGPL version 2.1 or # version 3 or later versions approved by the membership of KDE e.V. # # Josep M. Ferrer <txemaq@gmail.com>, 2022. msgid "" msgstr "" "Project-Id-Version: libkscreen\n" "PO-Revision-Date: 2022-10-21 13:09+0200\n" "Last-Translator: Josep M. Ferrer <txemaq@gmail.com>\n" "Language-Team: Catalan <kde-i18n-ca@kde.org>\n" "Language: ca@valencia\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 20.12.0\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "No s'ha pogut preparar el dorsal" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "No s'ha pogut desserialitzar la resposta del dorsal" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Dorsal invalidat" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "No s'ha pogut preparar el dorsal" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "No s'ha pogut serialitzar la sol·licitud" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "No s'ha pogut desserialitzar la resposta del dorsal" 0707010000006B000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/cs0707010000006C000081A40000000000000000000000016638555E00000511000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/cs/libkscreen5_qt.po# Vit Pelcak <vit@pelcak.org>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-02-10 14:48+0100\n" "Last-Translator: Vit Pelcak <vit@pelcak.org>\n" "Language-Team: Czech <kde-i18n-doc@kde.org>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Lokalize 22.12.2\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Nepovedlo se připravit podpůrnou vrstvu" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Nepovedlo se připravit podpůrnou vrstvu" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" 0707010000006D000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/de0707010000006E000081A40000000000000000000000016638555E00000647000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/de/libkscreen5_qt.po# Frederik Schwarzer <schwarzer@kde.org>, 2023. # Jannick Kuhr <opensource@kuhr.org>, 2023. msgid "" msgstr "" "Project-Id-Version: l 10n-support\n" "PO-Revision-Date: 2023-04-04 21:04+0200\n" "Last-Translator: Jannick Kuhr <opensource@kuhr.org>\n" "Language-Team: German <kde-i18n-de@kde.org>\n" "Language: de\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 22.12.3\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Vorbereitung des Hintergrundprogramms ist fehlgeschlagen" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" "Deserialisierung der Antwort des Hintergrundprogramms ist fehlgeschlagen" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Hintergrundprogramm ungültig gemacht" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Vorbereitung des Hintergrundprogramms ist fehlgeschlagen" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Serialisierung der Anfrage ist fehlgeschlagen" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" "Deserialisierung der Antwort des Hintergrundprogramms ist fehlgeschlagen" 0707010000006F000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/en_GB07070100000070000081A40000000000000000000000016638555E00000577000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/en_GB/libkscreen5_qt.po# Steve Allewell <steve.allewell@gmail.com>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-12-31 15:42+0000\n" "Last-Translator: Steve Allewell <steve.allewell@gmail.com>\n" "Language-Team: British English <kde-i18n-doc@kde.org>\n" "Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 21.12.3\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Failed to prepare backend" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Failed to deserialise backend response" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Backend invalidated" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Failed to prepare backend" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Failed to serialise request" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Failed to deserialise backend response" 07070100000071000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/eo07070100000072000081A40000000000000000000000016638555E000006C2000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/eo/libkscreen5_qt.po# translation of libkscreen5_qt.pot to Esperanto # Copyright (C) 2024 Free Software Foundation, Inc. # This file is distributed under the same license as the libkscreen package. # Oliver Kellogg <okellogg@users.sourceforge.net>, 2024. # msgid "" msgstr "" "Project-Id-Version: libkscreen\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-10-21 02:51+0200\n" "PO-Revision-Date: 2024-03-28 09:21+0100\n" "Last-Translator: Oliver Kellogg <okellogg@users.sourceforge.net>\n" "Language-Team: Esperanto <kde-i18n-eo.org>\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "X-Generator: translate-po (https://github.com/zcribe/translate-po)\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Malsukcesis prepari backend" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Malsukcesis malseriigi la backend-respondon" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Backend malvalidigita" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Malsukcesis prepari backend" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Malsukcesis seriigi peton" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Malsukcesis malseriigi la backend-respondon" 07070100000073000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/es07070100000074000081A40000000000000000000000016638555E000005AF000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/es/libkscreen5_qt.po# Eloy Cuadra <ecuadra@eloihr.net>, 2022. msgid "" msgstr "" "Project-Id-Version: libkscreen5_qt\n" "PO-Revision-Date: 2022-10-21 16:10+0200\n" "Last-Translator: Eloy Cuadra <ecuadra@eloihr.net>\n" "Language-Team: Spanish <kde-l10n-es@kde.org>\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 22.08.2\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "La preparación del motor ha fallado" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "La deserialización de la respuesta del motor ha fallado" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Motor invalidado" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "La preparación del motor ha fallado" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "La serialización de la petición ha fallado" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "La deserialización de la respuesta del motor ha fallado" 07070100000075000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/eu07070100000076000081A40000000000000000000000016638555E000006B4000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/eu/libkscreen5_qt.po# Translation for libkscreen5_qt.po to Euskara/Basque (eu). # Copyright (C) 2022 This file is copyright: # This file is distributed under the same license as the original file. # KDE euskaratzeko proiektuko arduraduna <xalba@ni.eus>. # # Translators: # Iñigo Salvador Azurmendi <xalba@ni.eus>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-21 20:20+0200\n" "Last-Translator: Iñigo Salvador Azurmendi <xalba@ni.eus>\n" "Language-Team: Basque <kde-i18n-eu@kde.org>\n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 22.08.1\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Bizkarraldekoa prestatzea huts egin du" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Bizkarraldekoaren erantzuna deserializatzea huts egin du" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Bizkarraldekoa baliogabetuta" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Bizkarraldekoa prestatzea huts egin du" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Eskaerak serializatzea huts egin du" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Bizkarraldekoaren erantzuna deserializatzea huts egin du" 07070100000077000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/fi07070100000078000081A40000000000000000000000016638555E000005E7000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/fi/libkscreen5_qt.po# Tommi Nieminen <translator@legisign.org>, 2022, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-01-18 15:11+0200\n" "Last-Translator: Tommi Nieminen <translator@legisign.org>\n" "Language-Team: Finnish <kde-i18n-doc@kde.org>\n" "Language: fi\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 20.12.0\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Taustajärjestelmän valmistelu epäonnistui" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Taustajärjestelmän vastauksen serialisoinnin purku epäonnistui" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Taustajärjestelmä on mitätöity" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Taustajärjestelmän valmistelu epäonnistui" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Vastauksen serialisointi epäonnistui" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Taustajärjestelmän vastauksen serialisoinnin purku epäonnistui" 07070100000079000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/fr0707010000007A000081A40000000000000000000000016638555E000005AD000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/fr/libkscreen5_qt.po# Xavier Besnard <xavier.besnard@kde.org>, 2022. msgid "" msgstr "" "Project-Id-Version: libkscreen5_qt\n" "PO-Revision-Date: 2022-10-22 11:30+0200\n" "Last-Translator: Xavier Besnard <xavier.besnard@neuf.fr>\n" "Language-Team: French <kde-francophone@kde.org>\n" "Language: fr\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 22.08.2\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Impossible de préparer un moteur" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Impossible de dé-sérialiser une réponse du moteur" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Moteur invalidé" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Impossible de préparer un moteur" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Impossible de sérialiser une requête" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Impossible de dé-sérialiser une réponse du moteur" 0707010000007B000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/gl0707010000007C000081A40000000000000000000000016638555E000005A7000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/gl/libkscreen5_qt.po# Adrián Chaves (Gallaecio) <adrian@chaves.io>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-04-28 17:45+0200\n" "Last-Translator: Adrián Chaves (Gallaecio) <adrian@chaves.io>\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" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Non foi posíbel preparar o motor" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Non foi posíbel deserializar a resposta do motor" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Invalidouse o motor" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Non foi posíbel preparar o motor" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Non foi posíbel serializar a solicitude" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Non foi posíbel deserializar a resposta do motor" 0707010000007D000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ia0707010000007E000081A40000000000000000000000016638555E000005B0000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ia/libkscreen5_qt.po# giovanni <g.sora@tiscali.it>, 2024. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2024-04-30 12:07+0200\n" "Last-Translator: giovanni <g.sora@tiscali.it>\n" "Language-Team: Interlingua <kde-i18n-doc@kde.org>\n" "Language: ia\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 22.12.3\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Il falleva a preparar retroadministration" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Il falleva a deserializar le responsa de retroadministration" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Retroadministration invalidate " #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Il falleva a preparar retroadministration" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Falleva a serializar requesta" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Il falleva a deserializar le responsa de retroadministration" 0707010000007F000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/id07070100000080000081A40000000000000000000000016638555E00000556000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/id/libkscreen5_qt.po# Wantoyèk <wantoyek@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-05-30 20:40+0700\n" "Last-Translator: Wantoyèk <wantoyek@gmail.com>\n" "Language-Team: Indonesian <kde-i18n-doc@kde.org>\n" "Language: id\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" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Gagal mempersiapkan backend" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Gagal mendeserialkan respons backend" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Backend tidak berlaku" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Gagal mempersiapkan backend" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Gagal meyerialkan permintaan" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Gagal mendeserialkan backend" 07070100000081000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/it07070100000082000081A40000000000000000000000016638555E000005B2000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/it/libkscreen5_qt.po# Vincenzo Reale <smart2128vr@gmail.com>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-23 02:06+0200\n" "Last-Translator: Vincenzo Reale <smart2128vr@gmail.com>\n" "Language-Team: Italian <kde-i18n-it@kde.org>\n" "Language: it\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 22.08.2\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Preparazione del motore non riuscita" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "De-serializzazione della risposta del motore non riuscita" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Motore invalidato" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Preparazione del motore non riuscita" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Serializzazione della richiesta non riuscita" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "De-serializzazione della risposta del motore non riuscita" 07070100000083000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ja07070100000084000081A40000000000000000000000016638555E000004A5000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ja/libkscreen5_qt.pomsgid "" msgstr "" "Project-Id-Version: libkscreen5_qt\n" "PO-Revision-Date: 2023-05-26 23:55-0700\n" "Last-Translator: Fumiaki Okushi <fumiaki.okushi@gmail.com>\n" "Language-Team: Japanese <kde-jp@kde.org>\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Accelerator-Marker: &\n" "X-Text-Markup: qtrich\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "" 07070100000085000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ka07070100000086000081A40000000000000000000000016638555E0000069A000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ka/libkscreen5_qt.pomsgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n" "Language-Team: \n" "Language: ka\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-Qt-Contexts: true\n" "X-Generator: Poedit 3.2\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "უკანაბოლოს მომზადების შეცდომა" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "უკანაბოლოს პასუხის დესერიალიზაციის შეცდომა" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "უკანაბოლო არასწორია" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "უკანაბოლოს მომზადების შეცდომა" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "მოთხოვნის სერიალიზაციის შეცდომა" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "უკანაბოლოს პასუხის დესერიალიზაციის შეცდომა" 07070100000087000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ko07070100000088000081A40000000000000000000000016638555E00000584000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ko/libkscreen5_qt.po# Shinjo Park <kde@peremen.name>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-04-22 00:57+0200\n" "Last-Translator: Shinjo Park <kde@peremen.name>\n" "Language-Team: Korean <kde-kr@kde.org>\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Lokalize 22.12.3\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "백엔드를 준비할 수 없음" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "백엔드 응답의 직렬화를 해석할 수 없음" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "백엔드가 무효화됨" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "백엔드를 준비할 수 없음" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "요청을 직렬화할 수 없음" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "백엔드 응답의 직렬화를 해석할 수 없음" 07070100000089000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/nl0707010000008A000081A40000000000000000000000016638555E00000595000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/nl/libkscreen5_qt.po# Freek de Kruijf <freekdekruijf@kde.nl>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-21 12:41+0200\n" "Last-Translator: Freek de Kruijf <freekdekruijf@kde.nl>\n" "Language-Team: \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 22.08.2\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Voorbereiden van de backend is mislukt" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Ontdoen van serialisatie van respons van backend is mislukt" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Backend ongeldig gemaakt" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Voorbereiden van de backend is mislukt" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Verzoek serialiseren is mislukt" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Ontdoen van serialisatie van respons van backend is mislukt" 0707010000008B000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/nn0707010000008C000081A40000000000000000000000016638555E00000612000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/nn/libkscreen5_qt.po# Translation of libkscreen6_qt to Norwegian Nynorsk # # Karl Ove Hufthammer <karl@huftis.org>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-03-05 12:32+0100\n" "Last-Translator: Karl Ove Hufthammer <karl@huftis.org>\n" "Language-Team: Norwegian Nynorsk <l10n-no@lister.huftis.org>\n" "Language: nn\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 22.12.3\n" "X-Environment: kde\n" "X-Accelerator-Marker: &\n" "X-Text-Markup: qtrich\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Klarte ikkje gjera klar motor" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Klarte ikkje avseralisera svar frå motor" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Motor er gjort ugyldig" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Klarte ikkje gjera klar motor" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Klarte ikkje serialisera førespurnad" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Klarte ikkje avseralisera svar frå motor" 0707010000008D000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pl0707010000008E000081A40000000000000000000000016638555E000005F4000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pl/libkscreen5_qt.po# Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-01-04 20:52+0100\n" "Last-Translator: Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>\n" "Language-Team: Polish <kde-i18n-doc@kde.org>\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Lokalize 22.12.0\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Nie udało się przygotować silnika" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Nie udało się odszeregować odpowiedzi silnika" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Unieważniono silnik" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Nie udało się przygotować silnika" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Nie udało się zszeregować żądanie" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Nie udało się odszeregować odpowiedzi silnika" 0707010000008F000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pt07070100000090000081A40000000000000000000000016638555E000005EA000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pt/libkscreen5_qt.po# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # msgid "" msgstr "" "Project-Id-Version: libkscreen5_qt\n" "PO-Revision-Date: 2022-10-21 23:41+0100\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: Portuguese <LL@li.org>\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Não foi possível preparar a infra-estrutura" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Não foi possível descodificar a resposta da infra-estrutura" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Infra-estrutura invalidada" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Não foi possível preparar a infra-estrutura" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Não foi possível codificar o pedido" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Não foi possível descodificar a resposta da infra-estrutura" 07070100000091000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pt_BR07070100000092000081A40000000000000000000000016638555E000005BD000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/pt_BR/libkscreen5_qt.po# Luiz Fernando Ranghetti <elchevive@opensuse.org>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-02-14 09:17-0300\n" "Last-Translator: Luiz Fernando Ranghetti <elchevive@opensuse.org>\n" "Language-Team: Portuguese <kde-i18n-pt_BR@kde.org>\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Lokalize 21.12.3\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Falha ao preparar a infraestrutura" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Falha ao desserializar a resposta da infraestrutura" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Infraestrutura invalidada" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Falha ao preparar a infraestrutura" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Falha ao serializar a solicitação" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Falha ao desserializar a resposta da infraestrutura" 07070100000093000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ro07070100000094000081A40000000000000000000000016638555E000005A8000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ro/libkscreen5_qt.po# Sergiu Bivol <sergiu@cip.md>, 2024. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2024-02-18 16:09+0000\n" "Last-Translator: Sergiu Bivol <sergiu@cip.md>\n" "Language-Team: Romanian <kde-i18n-ro@kde.org>\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Lokalize 21.12.3\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Pregătirea platformei a eșuat" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Deserializarea răspunsului platformei a eșuat" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Platformă invalidată" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Pregătirea platformei a eșuat" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Serializarea cererii a eșuat" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Deserializarea răspunsului platformei a eșuat" 07070100000095000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ru07070100000096000081A40000000000000000000000016638555E00000700000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/ru/libkscreen5_qt.po# Olesya Gerasimenko <translation-team@basealt.ru>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-28 09:04+0300\n" "Last-Translator: Olesya Gerasimenko <translation-team@basealt.ru>\n" "Language-Team: Basealt Translation Team\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Lokalize 22.04.3\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Не удалось подготовить внутреннюю службу" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Не удалось десериализировать ответ внутренней службы" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Внутренняя служба более не действительна" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Не удалось подготовить внутреннюю службу" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Не удалось сериализировать запрос" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Не удалось десериализировать ответ внутренней службы" 07070100000097000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sk07070100000098000081A40000000000000000000000016638555E00000625000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sk/libkscreen5_qt.po# translation of libkscreen5_qt.po to Slovak # Roman Paholík <wizzardsk@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: libkscreen5_qt\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2022-12-17 00:51+0000\n" "PO-Revision-Date: 2023-03-20 17:06+0100\n" "Last-Translator: Roman Paholik <wizzardsk@gmail.com>\n" "Language-Team: Slovak <kde-sk@linux.sk>\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Lokalize 22.12.3\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Qt-Contexts: true\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Zlyhalo pripravenie backendu" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Zlyhalo deserializovanie odpovede backendu" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Backend zneplatnený" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Zlyhalo pripravenie backendu" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Zlyhalo serializovanie požiadavky" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Zlyhalo deserializovanie odpovede backendu" 07070100000099000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sl0707010000009A000081A40000000000000000000000016638555E000005BB000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sl/libkscreen5_qt.po# Matjaž Jeran <matjaz.jeran@amis.net>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: 2022-10-22 08:31+0200\n" "Last-Translator: Matjaž Jeran <matjaz.jeran@amis.net>\n" "Language-Team: Slovenian <lugos-slo@lugos.si>\n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100>=3 && n" "%100<=4 ? 2 : 3);\n" "X-Qt-Contexts: true\n" "X-Generator: Lokalize 22.08.1\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Ni bilo pripravljeno zaledje" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Ni uspela razmestitev odziva zaledja" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Zaledje razveljavljeno" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Priprava zaledja ni uspela" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Neuspelo razvrščanje zahtevka" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Neuspela razmestitev odziva zaledja" 0707010000009B000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sv0707010000009C000081A40000000000000000000000016638555E000005A4000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/sv/libkscreen5_qt.po# Stefan Asserhäll <stefan.asserhall@gmail.com>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-07-25 11:14+0200\n" "Last-Translator: Stefan Asserhäll <stefan.asserhall@gmail.com>\n" "Language-Team: Swedish <kde-i18n-doc@kde.org>\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 20.08.1\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Misslyckades förbereda gränssnitt" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Misslyckades deserialisera gränssnittssvar" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Gränssnitt ogiltigförklarat" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Misslyckades förbereda gränssnitt" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Misslyckades serialisera begäran" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Misslyckades deserialisera gränssnittssvar" 0707010000009D000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/tr0707010000009E000081A40000000000000000000000016638555E0000055A000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/tr/libkscreen5_qt.po# Emir SARI <emir_sari@icloud.com>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-21 10:51+0300\n" "Last-Translator: Emir SARI <emir_sari@icloud.com>\n" "Language-Team: Turkish <kde-l10n-tr@kde.org>\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Lokalize 22.08.1\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Arka uç hazırlanamadı" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Arka uç yanıtı paralelleştirilemedi" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Arka uç geçersizleştirildi" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Arka uç hazırlanamadı" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "İstek dizilemedi" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Arka uç yanıtı paralelleştirilemedi" 0707010000009F000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/uk070701000000A0000081A40000000000000000000000016638555E00000728000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/uk/libkscreen5_qt.po# Yuri Chornoivan <yurchor@ukr.net>, 2022. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2022-10-22 13:40+0300\n" "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n" "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Lokalize 20.12.0\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Не вдалося приготувати модуль обробки" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Не вдалося перетворити з послідовної форми відповідь модуля обробки" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "Чинність модуля обробки скасовано" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "Не вдалося приготувати модуль обробки" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "Не вдалося перетворити запит у послідовну форму" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "Не вдалося перетворити з послідовної форми відповідь модуля обробки" 070701000000A1000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/zh_CN070701000000A2000081A40000000000000000000000016638555E0000057F000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/zh_CN/libkscreen5_qt.pomsgid "" msgstr "" "Project-Id-Version: kdeorg\n" "PO-Revision-Date: 2024-03-30 19:46\n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: kdeorg\n" "X-Crowdin-Project-ID: 269464\n" "X-Crowdin-Language: zh-CN\n" "X-Crowdin-File: /kf5-stable/messages/libkscreen/libkscreen5_qt.pot\n" "X-Crowdin-File-ID: 41112\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "准备后端程序失败" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "反序列化后端程序响应失败" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "后端程序无效" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "准备后端程序失败" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "序列化请求失败" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "反序列化后端程序响应失败" 070701000000A3000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/zh_TW070701000000A4000081A40000000000000000000000016638555E0000054A000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/poqm/zh_TW/libkscreen5_qt.po# Kisaragi Hiu <mail@kisaragi-hiu.com>, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "PO-Revision-Date: 2023-01-15 20:36+0800\n" "Last-Translator: Kisaragi Hiu <mail@kisaragi-hiu.com>\n" "Language-Team: Traditional Chinese <zh-l10n@linux.org.tw>\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Qt-Contexts: true\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Lokalize 22.12.0\n" #: getconfigoperation.cpp:62 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "準備後端失敗" #: getconfigoperation.cpp:87 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "反序列化後端回應失敗" #: getconfigoperation.cpp:99 msgctxt "KScreen::GetConfigOperationPrivate|" msgid "Backend invalidated" msgstr "已將後端無效化" #: setconfigoperation.cpp:58 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to prepare backend" msgstr "準備後端失敗" #: setconfigoperation.cpp:65 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to serialize request" msgstr "序列化要求失敗" #: setconfigoperation.cpp:89 msgctxt "KScreen::SetConfigOperationPrivate|" msgid "Failed to deserialize backend response" msgstr "反序列化後端回應失敗" 070701000000A5000041ED0000000000000000000000056638555E00000000000000000000000000000000000000000000003200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src070701000000A6000081A40000000000000000000000016638555E00000D9E000000000000000000000000000000000000004100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/CMakeLists.txtadd_subdirectory(backendlauncher) add_subdirectory(doctor) add_subdirectory(libdpms) set(libkscreen_SRCS abstractbackend.cpp backendmanager.cpp config.cpp configoperation.cpp getconfigoperation.cpp setconfigoperation.cpp configmonitor.cpp configserializer.cpp screen.cpp output.cpp edid.cpp mode.cpp log.cpp ) qt_add_dbus_interface(libkscreen_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.Backend.xml backendinterface) ecm_qt_declare_logging_category(libkscreen_SRCS HEADER kscreen_debug.h IDENTIFIER KSCREEN CATEGORY_NAME org.kde.kscreen ) ecm_qt_declare_logging_category(libkscreen_SRCS HEADER kscreen_debug_edid.h IDENTIFIER KSCREEN_EDID CATEGORY_NAME org.kde.kscreen.edid ) add_library(KF5Screen SHARED ${libkscreen_SRCS}) generate_export_header(KF5Screen BASE_NAME KScreen) ecm_create_qm_loader(KF5Screen libkscreen5_qt) target_link_libraries(KF5Screen PUBLIC Qt::Core Qt::Gui PRIVATE Qt::DBus ) if (QT_MAJOR_VERSION EQUAL "5") target_link_libraries(KF5Screen PRIVATE Qt::X11Extras) else() target_link_libraries(KF5Screen PRIVATE Qt::GuiPrivate) endif() set_target_properties(KF5Screen PROPERTIES VERSION "${KSCREEN_VERSION}" SOVERSION "${KSCREEN_SOVERSION}" EXPORT_NAME Screen ) target_include_directories(KF5Screen PUBLIC "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KScreen>") install(TARGETS KF5Screen EXPORT KF5ScreenTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) add_library(KF5::Screen ALIAS KF5Screen) ecm_generate_headers(KScreen_HEADERS HEADER_NAMES Log Mode Output EDID Screen Config ConfigMonitor ConfigOperation GetConfigOperation SetConfigOperation Types PREFIX KScreen REQUIRED_HEADERS KScreen_REQ_HEADERS ) install(FILES ${KScreen_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KScreen/KScreen COMPONENT Devel) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kscreen_export.h backendmanager_p.h # needed for unit-tests in KScreen ${KScreen_REQ_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KScreen/kscreen) if(NOT WIN32) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/kscreen2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/kscreen2.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kscreen2.pc DESTINATION ${KDE_INSTALL_LIBDIR}/pkgconfig) endif(NOT WIN32) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KScreen LIB_NAME KF5Screen DEPS "core" FILENAME_VAR PRI_FILENAME) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if(BUILD_QCH) ecm_add_qch( KF5Screen_QCH NAME KScreen BASE_NAME KF5Screen VERSION ${PROJECT_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KScreen_REQ_HEADERS} # MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" LINK_QCHS Qt5Core_QCH Qt5Gui_QCH BLANK_MACROS KSCREEN_EXPORT KSCREEN_DEPRECATED KSCREEN_DEPRECATED_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() 070701000000A7000081A40000000000000000000000016638555E00000105000000000000000000000000000000000000003E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/Messages.sh#!/bin/sh # Extract strings from all source files. # EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with # lconvert. $EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/libkscreen5_qt.pot 070701000000A8000081A40000000000000000000000016638555E00000170000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/abstractbackend.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "abstractbackend.h" void KScreen::AbstractBackend::init(const QVariantMap &arguments) { Q_UNUSED(arguments); } QByteArray KScreen::AbstractBackend::edid(int outputId) const { Q_UNUSED(outputId); return QByteArray(); } 070701000000A9000081A40000000000000000000000016638555E00000ABE000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/abstractbackend.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef ABSTRACT_BACKEND_H #define ABSTRACT_BACKEND_H #include "kscreen_export.h" #include "types.h" #include <QObject> #include <QString> namespace KScreen { class Config; /** * Abstract class for backends. */ class KSCREEN_EXPORT AbstractBackend : public QObject { Q_OBJECT public: ~AbstractBackend() override { } /** * This is where the backend should perform all initialization. This method * is always called right after the backend is created. * * Default implementation does nothing. * * @p arguments Optional arguments passed by caller. Used mostly for unit-testing. */ virtual void init(const QVariantMap &arguments); /** * Returns a user-friendly name of the backend. */ virtual QString name() const = 0; /** * Returns the name of the DBus service that should be used for this backend. * * Each backend must have an unique service name (usually something like * org.kde.KScreen.Backend.%backendName%) to allow multiple different backends * running concurrently. */ virtual QString serviceName() const = 0; /** * Returns a new Config object, holding Screen, Output objects, etc. * * @return Config object for the system. */ virtual KScreen::ConfigPtr config() const = 0; /** * Apply a config object to the system. * * @param config Configuration to apply */ virtual void setConfig(const KScreen::ConfigPtr &config) = 0; /** * Returns whether the backend is in valid state. * * Backends should use this to tell BackendLauncher whether they are capable * of operating on the current platform. */ virtual bool isValid() const = 0; /** * Returns encoded EDID data for given output * * Default implementation does nothing and returns null QByteArray. Backends * that don't support EDID don't have to reimplement this method. * * @param outputd ID of output to return EDID data for */ virtual QByteArray edid(int outputId) const; Q_SIGNALS: /** * Emitted when backend detects a change in configuration * * It's OK to emit this signal for every single change. The emissions are aggregated * in the backend launcher, so that the backend does not spam DBus and client * applications. * * @param config New configuration */ void configChanged(const KScreen::ConfigPtr &config); }; } // namespace KScreen #endif // ABSTRACT_BACKEND_H 070701000000AA000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000004200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher070701000000AB000081A40000000000000000000000016638555E00000685000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/CMakeLists.txtset(backendlauncher_SRCS main.cpp backendloader.cpp backenddbuswrapper.cpp ) ecm_qt_declare_logging_category(backendlauncher_SRCS HEADER kscreen_backendLauncher_debug.h IDENTIFIER KSCREEN_BACKEND_LAUNCHER CATEGORY_NAME org.kde.kscreen.backendLauncher ) qt_add_dbus_adaptor(backendlauncher_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.Backend.xml backenddbuswrapper.h BackendDBusWrapper backendadaptor BackendAdaptor) qt_add_dbus_adaptor(backendlauncher_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.xml backendloader.h BackendLoader backendloaderadaptor BackendLoaderAdaptor) add_executable(kscreen_backend_launcher ${backendlauncher_SRCS}) target_link_libraries(kscreen_backend_launcher KF5Screen Qt::Core Qt::Gui Qt::DBus ) if (QT_MAJOR_VERSION EQUAL "5") target_link_libraries(kscreen_backend_launcher Qt::X11Extras) else() target_link_libraries(kscreen_backend_launcher Qt::GuiPrivate) endif() install(TARGETS kscreen_backend_launcher DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR_KF5} ) configure_file(org.kde.kscreen.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kscreen.service @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kscreen.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR} ) configure_file(plasma-kscreen.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasma-kscreen.service @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasma-kscreen.service DESTINATION ${KDE_INSTALL_SYSTEMDUSERUNITDIR} ) 070701000000AC000081A40000000000000000000000016638555E00000D47000000000000000000000000000000000000005900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/backenddbuswrapper.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "backenddbuswrapper.h" #include "backendadaptor.h" #include "kscreen_backendLauncher_debug.h" #include "abstractbackend.h" #include "config.h" #include "configserializer_p.h" #include <QDBusConnection> #include <QDBusError> BackendDBusWrapper::BackendDBusWrapper(KScreen::AbstractBackend *backend) : QObject() , mBackend(backend) { connect(mBackend, &KScreen::AbstractBackend::configChanged, this, &BackendDBusWrapper::backendConfigChanged); mChangeCollector.setSingleShot(true); mChangeCollector.setInterval(200); // wait for 200 msecs without any change // before actually emitting configChanged connect(&mChangeCollector, &QTimer::timeout, this, &BackendDBusWrapper::doEmitConfigChanged); } BackendDBusWrapper::~BackendDBusWrapper() { } bool BackendDBusWrapper::init() { QDBusConnection dbus = QDBusConnection::sessionBus(); new BackendAdaptor(this); if (!dbus.registerObject(QStringLiteral("/backend"), this, QDBusConnection::ExportAdaptors)) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Failed to export backend to DBus: another launcher already running?"; qCWarning(KSCREEN_BACKEND_LAUNCHER) << dbus.lastError().message(); return false; } return true; } QVariantMap BackendDBusWrapper::getConfig() const { const KScreen::ConfigPtr config = mBackend->config(); Q_ASSERT(!config.isNull()); if (!config) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Backend provided an empty config!"; return QVariantMap(); } const QJsonObject obj = KScreen::ConfigSerializer::serializeConfig(mBackend->config()); Q_ASSERT(!obj.isEmpty()); return obj.toVariantMap(); } QVariantMap BackendDBusWrapper::setConfig(const QVariantMap &configMap) { if (configMap.isEmpty()) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Received an empty config map"; return QVariantMap(); } const KScreen::ConfigPtr config = KScreen::ConfigSerializer::deserializeConfig(configMap); mBackend->setConfig(config); mCurrentConfig = mBackend->config(); QMetaObject::invokeMethod(this, "doEmitConfigChanged", Qt::QueuedConnection); // TODO: setConfig should return adjusted config that was actually applied const QJsonObject obj = KScreen::ConfigSerializer::serializeConfig(mCurrentConfig); Q_ASSERT(!obj.isEmpty()); return obj.toVariantMap(); } QByteArray BackendDBusWrapper::getEdid(int output) const { const QByteArray edidData = mBackend->edid(output); if (edidData.isEmpty()) { return QByteArray(); } return edidData; } void BackendDBusWrapper::backendConfigChanged(const KScreen::ConfigPtr &config) { Q_ASSERT(!config.isNull()); if (!config) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Backend provided an empty config!"; return; } mCurrentConfig = config; mChangeCollector.start(); } void BackendDBusWrapper::doEmitConfigChanged() { Q_ASSERT(!mCurrentConfig.isNull()); if (mCurrentConfig.isNull()) { return; } const QJsonObject obj = KScreen::ConfigSerializer::serializeConfig(mCurrentConfig); Q_EMIT configChanged(obj.toVariantMap()); mCurrentConfig.clear(); mChangeCollector.stop(); } 070701000000AD000081A40000000000000000000000016638555E00000464000000000000000000000000000000000000005700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/backenddbuswrapper.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef BACKENDDBUSWRAPPER_H #define BACKENDDBUSWRAPPER_H #include <QObject> #include <QTimer> #include <QVariant> #include "types.h" namespace KScreen { class AbstractBackend; } class BackendDBusWrapper : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KScreen.Backend") public: explicit BackendDBusWrapper(KScreen::AbstractBackend *backend); ~BackendDBusWrapper() override; bool init(); QVariantMap getConfig() const; QVariantMap setConfig(const QVariantMap &config); QByteArray getEdid(int output) const; inline KScreen::AbstractBackend *backend() const { return mBackend; } Q_SIGNALS: void configChanged(const QVariantMap &config); private Q_SLOTS: void backendConfigChanged(const KScreen::ConfigPtr &config); void doEmitConfigChanged(); private: KScreen::AbstractBackend *mBackend = nullptr; QTimer mChangeCollector; KScreen::ConfigPtr mCurrentConfig; }; #endif // BACKENDDBUSWRAPPER_H 070701000000AE000081A40000000000000000000000016638555E00000C19000000000000000000000000000000000000005400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/backendloader.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "backendloader.h" #include "abstractbackend.h" #include "backenddbuswrapper.h" #include "backendloaderadaptor.h" #include "backendmanager_p.h" #include "kscreen_backendLauncher_debug.h" #include <QCoreApplication> #include <QDBusConnectionInterface> #include <QDir> #include <QPluginLoader> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif #include <memory> #include <QDBusConnection> #include <QDBusInterface> void pluginDeleter(QPluginLoader *p) { if (p) { qCDebug(KSCREEN_BACKEND_LAUNCHER) << "Unloading" << p->fileName(); p->unload(); delete p; } } BackendLoader::BackendLoader() : QObject() , QDBusContext() , mLoader(nullptr) , mBackend(nullptr) { } BackendLoader::~BackendLoader() { delete mBackend; pluginDeleter(mLoader); qCDebug(KSCREEN_BACKEND_LAUNCHER) << "Backend loader destroyed"; } bool BackendLoader::init() { QDBusConnection dbus = QDBusConnection::sessionBus(); new BackendLoaderAdaptor(this); if (!dbus.registerObject(QStringLiteral("/"), this, QDBusConnection::ExportAdaptors)) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Failed to export backend to DBus: another launcher already running?"; qCWarning(KSCREEN_BACKEND_LAUNCHER) << dbus.lastError().message(); return false; } return true; } QString BackendLoader::backend() const { if (mBackend) { return mBackend->backend()->name(); } return QString(); } bool BackendLoader::requestBackend(const QString &backendName, const QVariantMap &arguments) { if (mBackend) { // If an backend is already loaded, but it's not the same as the one // requested, then it's an error if (!backendName.isEmpty() && mBackend->backend()->name() != backendName) { sendErrorReply(QDBusError::Failed, QStringLiteral("Another backend is already active")); return false; } else { // If caller requested the same one as already loaded, or did not // request a specific backend, hapilly reuse the existing one return true; } } KScreen::AbstractBackend *backend = loadBackend(backendName, arguments); if (!backend) { return false; } mBackend = new BackendDBusWrapper(backend); if (!mBackend->init()) { delete mBackend; mBackend = nullptr; pluginDeleter(mLoader); mLoader = nullptr; return false; } return true; } KScreen::AbstractBackend *BackendLoader::loadBackend(const QString &name, const QVariantMap &arguments) { if (mLoader == nullptr) { std::unique_ptr<QPluginLoader, void (*)(QPluginLoader *)> loader(new QPluginLoader(), pluginDeleter); mLoader = loader.release(); } return KScreen::BackendManager::loadBackendPlugin(mLoader, name, arguments); } void BackendLoader::quit() { qApp->quit(); } 070701000000AF000081A40000000000000000000000016638555E0000039B000000000000000000000000000000000000005200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/backendloader.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef BACKENDLAUNCHER_H #define BACKENDLAUNCHER_H #include <QDBusContext> #include <QObject> namespace KScreen { class AbstractBackend; } class QPluginLoader; class BackendDBusWrapper; class BackendLoader : public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KScreen") public: explicit BackendLoader(); ~BackendLoader() override; bool init(); Q_INVOKABLE QString backend() const; Q_INVOKABLE bool requestBackend(const QString &name, const QVariantMap &arguments); Q_INVOKABLE void quit(); private: KScreen::AbstractBackend *loadBackend(const QString &name, const QVariantMap &arguments); private: QPluginLoader *mLoader = nullptr; BackendDBusWrapper *mBackend = nullptr; }; #endif // BACKENDLAUNCHER_H 070701000000B0000081A40000000000000000000000016638555E00000422000000000000000000000000000000000000004B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/main.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include <QDBusConnection> #include <QGuiApplication> #include "backendloader.h" #include "kscreen_backendLauncher_debug.h" #include "log.h" int main(int argc, char **argv) { KScreen::Log::instance(); QGuiApplication::setDesktopSettingsAware(false); QCoreApplication::setAttribute(Qt::AA_DisableSessionManager); QGuiApplication app(argc, argv); if (!QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KScreen"))) { qCWarning(KSCREEN_BACKEND_LAUNCHER) << "Cannot register org.kde.KScreen service. Another launcher already running?"; return -1; } BackendLoader *loader = new BackendLoader; if (!loader->init()) { return -2; } const int ret = app.exec(); // Make sure the backend is destroyed and unloaded before we return (i.e. // as long as QApplication object and it's XCB connection still exist delete loader; return ret; } 070701000000B1000081A40000000000000000000000016638555E0000008D000000000000000000000000000000000000006000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/org.kde.kscreen.service.cmake[D-BUS Service] Name=org.kde.KScreen Exec=@CMAKE_INSTALL_FULL_LIBEXECDIR_KF5@/kscreen_backend_launcher SystemdService=plasma-kscreen.service 070701000000B2000081A40000000000000000000000016638555E00000112000000000000000000000000000000000000005F00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendlauncher/plasma-kscreen.service.cmake[Unit] Description=KScreen PartOf=graphical-session.target [Service] ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR_KF5@/kscreen_backend_launcher Type=dbus BusName=org.kde.KScreen TimeoutSec=5sec Slice=background.slice # Disable restart as we're dbus activated anyway Restart=no 070701000000B3000081A40000000000000000000000016638555E00003388000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendmanager.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "backendmanager_p.h" #include "abstractbackend.h" #include "backendinterface.h" #include "configmonitor.h" #include "configserializer_p.h" #include "getconfigoperation.h" #include "kscreen_debug.h" #include "log.h" #include <QDBusConnection> #include <QDBusConnectionInterface> #include <QDBusPendingCall> #include <QDBusPendingCallWatcher> #include <QDBusPendingReply> #include <QGuiApplication> #include <QStandardPaths> #include <QThread> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif #include <memory> using namespace KScreen; Q_DECLARE_METATYPE(org::kde::kscreen::Backend *) const int BackendManager::sMaxCrashCount = 4; BackendManager *BackendManager::sInstance = nullptr; BackendManager *BackendManager::instance() { if (!sInstance) { sInstance = new BackendManager(); } return sInstance; } BackendManager::BackendManager() : mInterface(nullptr) , mCrashCount(0) , mShuttingDown(false) , mRequestsCounter(0) , mLoader(nullptr) , mInProcessBackend(nullptr) , mMethod(OutOfProcess) { Log::instance(); // Decide whether to run in, or out-of-process // if KSCREEN_BACKEND_INPROCESS is set explicitly, we respect that const auto _inprocess = qgetenv("KSCREEN_BACKEND_INPROCESS"); if (!_inprocess.isEmpty()) { const QByteArrayList falses({QByteArray("0"), QByteArray("false")}); if (!falses.contains(_inprocess.toLower())) { mMethod = InProcess; } else { mMethod = OutOfProcess; } } else { // For XRandR backends, use out of process if (preferredBackend().fileName().startsWith(QLatin1String("KSC_XRandR"))) { mMethod = OutOfProcess; } else { mMethod = InProcess; } } initMethod(); } void BackendManager::initMethod() { if (mMethod == OutOfProcess) { qRegisterMetaType<org::kde::kscreen::Backend *>("OrgKdeKscreenBackendInterface"); mServiceWatcher.setConnection(QDBusConnection::sessionBus()); connect(&mServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &BackendManager::backendServiceUnregistered); mResetCrashCountTimer.setSingleShot(true); mResetCrashCountTimer.setInterval(60000); connect(&mResetCrashCountTimer, &QTimer::timeout, this, [=]() { mCrashCount = 0; }); } } void BackendManager::setMethod(BackendManager::Method m) { if (mMethod == m) { return; } shutdownBackend(); mMethod = m; initMethod(); } BackendManager::Method BackendManager::method() const { return mMethod; } BackendManager::~BackendManager() { if (mMethod == InProcess) { shutdownBackend(); } } QFileInfo BackendManager::preferredBackend(const QString &backend) { /** this is the logic to pick a backend, in order of priority * * - backend argument is used if not empty * - env var KSCREEN_BACKEND is considered * - if platform is X11, the XRandR backend is picked * - if platform is wayland, KWayland backend is picked * - if neither is the case, QScreen backend is picked * - the QScreen backend is also used as fallback * */ QString backendFilter; const auto env_kscreen_backend = QString::fromUtf8(qgetenv("KSCREEN_BACKEND")); if (!backend.isEmpty()) { backendFilter = backend; } else if (!env_kscreen_backend.isEmpty()) { backendFilter = env_kscreen_backend; } else { if (QX11Info::isPlatformX11()) { backendFilter = QStringLiteral("XRandR"); } else if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { backendFilter = QStringLiteral("KWayland"); } else { backendFilter = QStringLiteral("QScreen"); } } QFileInfo fallback; const auto backends = listBackends(); for (const QFileInfo &f : backends) { // Here's the part where we do the match case-insensitive if (f.baseName().toLower() == QStringLiteral("ksc_%1").arg(backendFilter.toLower())) { return f; } if (f.baseName() == QLatin1String("KSC_QScreen")) { fallback = f; } } // qCWarning(KSCREEN) << "No preferred backend found. KSCREEN_BACKEND is set to " << env_kscreen_backend; // qCWarning(KSCREEN) << "falling back to " << fallback.fileName(); return fallback; } QFileInfoList BackendManager::listBackends() { // Compile a list of installed backends first const QString backendFilter = QStringLiteral("KSC_*"); const QStringList paths = QCoreApplication::libraryPaths(); QFileInfoList finfos; for (const QString &path : paths) { const QDir dir(path + QStringLiteral("/kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kscreen/"), backendFilter, QDir::SortFlags(QDir::QDir::Name), QDir::NoDotAndDotDot | QDir::Files); finfos.append(dir.entryInfoList()); } return finfos; } void BackendManager::setBackendArgs(const QVariantMap &arguments) { if (mBackendArguments != arguments) { mBackendArguments = arguments; } } QVariantMap BackendManager::getBackendArgs() { return mBackendArguments; } KScreen::AbstractBackend *BackendManager::loadBackendPlugin(QPluginLoader *loader, const QString &name, const QVariantMap &arguments) { const auto finfo = preferredBackend(name); loader->setFileName(finfo.filePath()); QObject *instance = loader->instance(); if (!instance) { qCDebug(KSCREEN) << loader->errorString(); return nullptr; } auto backend = qobject_cast<KScreen::AbstractBackend *>(instance); if (backend) { backend->init(arguments); if (!backend->isValid()) { qCDebug(KSCREEN) << "Skipping" << backend->name() << "backend"; delete backend; return nullptr; } // qCDebug(KSCREEN) << "Loaded" << backend->name() << "backend"; return backend; } else { qCDebug(KSCREEN) << finfo.fileName() << "does not provide valid KScreen backend"; } return nullptr; } KScreen::AbstractBackend *BackendManager::loadBackendInProcess(const QString &name) { Q_ASSERT(mMethod == InProcess); if (mMethod == OutOfProcess) { qCWarning(KSCREEN) << "You are trying to load a backend in process, while the BackendManager is set to use OutOfProcess communication. Use " "loadBackendPlugin() instead."; return nullptr; } if (mInProcessBackend != nullptr && (name.isEmpty() || mInProcessBackend->name() == name)) { return mInProcessBackend; } else if (mInProcessBackend != nullptr && mInProcessBackend->name() != name) { shutdownBackend(); } if (mLoader == nullptr) { mLoader = new QPluginLoader(this); } auto backend = BackendManager::loadBackendPlugin(mLoader, name, mBackendArguments); if (!backend) { return nullptr; } // qCDebug(KSCREEN) << "Connecting ConfigMonitor to backend."; ConfigMonitor::instance()->connectInProcessBackend(backend); mInProcessBackend = backend; setConfig(backend->config()); return backend; } void BackendManager::requestBackend() { Q_ASSERT(mMethod == OutOfProcess); if (mInterface && mInterface->isValid()) { ++mRequestsCounter; QMetaObject::invokeMethod(this, "emitBackendReady", Qt::QueuedConnection); return; } // Another request already pending if (mRequestsCounter > 0) { return; } ++mRequestsCounter; startBackend(QString::fromLatin1(qgetenv("KSCREEN_BACKEND")), mBackendArguments); } void BackendManager::emitBackendReady() { Q_ASSERT(mMethod == OutOfProcess); Q_EMIT backendReady(mInterface); --mRequestsCounter; if (mShutdownLoop.isRunning()) { mShutdownLoop.quit(); } } void BackendManager::startBackend(const QString &backend, const QVariantMap &arguments) { // This will autostart the launcher if it's not running already, calling // requestBackend(backend) will: // a) if the launcher is started it will force it to load the correct backend, // b) if the launcher is already running it will make sure it's running with // the same backend as the one we requested and send an error otherwise QDBusConnection conn = QDBusConnection::sessionBus(); QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("requestBackend")); call.setArguments({backend, arguments}); QDBusPendingCall pending = conn.asyncCall(call); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending); connect(watcher, &QDBusPendingCallWatcher::finished, this, &BackendManager::onBackendRequestDone); } void BackendManager::onBackendRequestDone(QDBusPendingCallWatcher *watcher) { Q_ASSERT(mMethod == OutOfProcess); watcher->deleteLater(); QDBusPendingReply<bool> reply = *watcher; // Most probably we requested an explicit backend that is different than the // one already loaded in the launcher if (reply.isError()) { qCWarning(KSCREEN) << "Failed to request backend:" << reply.error().name() << ":" << reply.error().message(); invalidateInterface(); emitBackendReady(); return; } // Most probably request and explicit backend which is not available or failed // to initialize, or the launcher did not find any suitable backend for the // current platform. if (!reply.value()) { qCWarning(KSCREEN) << "Failed to request backend: unknown error"; invalidateInterface(); emitBackendReady(); return; } // The launcher has successfully loaded the backend we wanted and registered // it to DBus (hopefuly), let's try to get an interface for the backend. if (mInterface) { invalidateInterface(); } mInterface = new org::kde::kscreen::Backend(QStringLiteral("org.kde.KScreen"), QStringLiteral("/backend"), QDBusConnection::sessionBus()); if (!mInterface->isValid()) { qCWarning(KSCREEN) << "Backend successfully requested, but we failed to obtain a valid DBus interface for it"; invalidateInterface(); emitBackendReady(); return; } // The backend is GO, so let's watch for it's possible disappearance, so we // can invalidate the interface mServiceWatcher.addWatchedService(mBackendService); // Immediatelly request config connect(new GetConfigOperation(GetConfigOperation::NoEDID), &GetConfigOperation::finished, [&](ConfigOperation *op) { mConfig = qobject_cast<GetConfigOperation *>(op)->config(); emitBackendReady(); }); // And listen for its change. connect(mInterface, &org::kde::kscreen::Backend::configChanged, [&](const QVariantMap &newConfig) { mConfig = KScreen::ConfigSerializer::deserializeConfig(newConfig); }); } void BackendManager::backendServiceUnregistered(const QString &serviceName) { Q_ASSERT(mMethod == OutOfProcess); mServiceWatcher.removeWatchedService(serviceName); invalidateInterface(); requestBackend(); } void BackendManager::invalidateInterface() { Q_ASSERT(mMethod == OutOfProcess); delete mInterface; mInterface = nullptr; mBackendService.clear(); } ConfigPtr BackendManager::config() const { return mConfig; } void BackendManager::setConfig(ConfigPtr c) { // qCDebug(KSCREEN) << "BackendManager::setConfig, outputs:" << c->outputs().count(); mConfig = c; } void BackendManager::shutdownBackend() { if (mMethod == InProcess) { delete mLoader; mLoader = nullptr; delete mInProcessBackend; mInProcessBackend = nullptr; } else { if (mBackendService.isEmpty() && !mInterface) { return; } // If there are some currently pending requests, then wait for them to // finish before quitting while (mRequestsCounter > 0) { mShutdownLoop.exec(); } mServiceWatcher.removeWatchedService(mBackendService); mShuttingDown = true; QDBusMessage call = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KScreen"), QStringLiteral("/"), QStringLiteral("org.kde.KScreen"), QStringLiteral("quit")); // Call synchronously QDBusConnection::sessionBus().call(call); invalidateInterface(); while (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.KScreen"))) { QThread::msleep(100); } } } 070701000000B4000081A40000000000000000000000016638555E000012BA000000000000000000000000000000000000004500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/backendmanager_p.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ /** * WARNING: This header is *not* part of public API and is subject to change. * There are not guarantees or API or ABI stability or compatibility between * releases */ #ifndef KSCREEN_BACKENDMANAGER_H #define KSCREEN_BACKENDMANAGER_H #include <QDBusServiceWatcher> #include <QEventLoop> #include <QFileInfoList> #include <QObject> #include <QPluginLoader> #include <QProcess> #include <QTimer> #include "kscreen_export.h" #include "types.h" class QDBusPendingCallWatcher; class OrgKdeKscreenBackendInterface; namespace KScreen { class AbstractBackend; class KSCREEN_EXPORT BackendManager : public QObject { Q_OBJECT public: enum Method { InProcess, OutOfProcess, }; static BackendManager *instance(); ~BackendManager() override; KScreen::ConfigPtr config() const; void setConfig(KScreen::ConfigPtr c); /** Choose which backend to use * * This method uses a couple of heuristics to pick the backend to be loaded: * - If the @p backend argument is specified and not empty it's used to filter the * available backend list * - If specified, the KSCREEN_BACKEND env var is considered (case insensitive) * - Otherwise, the wayland backend is picked when the runtime platform is Wayland * (we assume kwin in this case * - Otherwise, if the runtime platform is X11, the XRandR backend is picked * - If neither is the case, we fall back to the QScreen backend, since that is the * most generally applicable and may work on platforms not explicitly supported * * @return the backend plugin to load * @since 5.7 */ static QFileInfo preferredBackend(const QString &backend = QString()); /** List installed backends * @return a list of installed backend plugins * @since 5.7 */ static QFileInfoList listBackends(); /** Set arguments map which a backend may use on initialization. * * Calling this method after a backend has been initialized will have no effect. * Arguments map will NOT be automatically cleared on backend shutdown; which * makes possible setting arguments before restarting backend in tests. * * @param map of arbitrary arguments for backends; each backend is free to interpret * them as it sees fit. * @since 5.27 */ void setBackendArgs(const QVariantMap &arguments); /** Get arguments map which a backend may use on initialization. * * @return map of arbitrary arguments for backends. * @since 5.27 */ QVariantMap getBackendArgs(); /** Encapsulates the plugin loading logic. * * @param loader a pointer to the QPluginLoader, the caller is * responsible for its memory management. * @param name name of the backend plugin * @param arguments arguments, used for unit tests * @return a pointer to the backend loaded from the plugin * @since 5.6 */ static KScreen::AbstractBackend *loadBackendPlugin(QPluginLoader *loader, const QString &name, const QVariantMap &arguments); KScreen::AbstractBackend *loadBackendInProcess(const QString &name); BackendManager::Method method() const; void setMethod(BackendManager::Method m); // For out-of-process operation void requestBackend(); void shutdownBackend(); Q_SIGNALS: void backendReady(OrgKdeKscreenBackendInterface *backend); private Q_SLOTS: void emitBackendReady(); void startBackend(const QString &backend = QString(), const QVariantMap &arguments = QVariantMap()); void onBackendRequestDone(QDBusPendingCallWatcher *watcher); void backendServiceUnregistered(const QString &serviceName); private: friend class SetInProcessOperation; friend class InProcessConfigOperationPrivate; friend class SetConfigOperation; friend class SetConfigOperationPrivate; explicit BackendManager(); static BackendManager *sInstance; void initMethod(); // For out-of-process operation void invalidateInterface(); void backendServiceReady(); static const int sMaxCrashCount; OrgKdeKscreenBackendInterface *mInterface; int mCrashCount; QString mBackendService; QDBusServiceWatcher mServiceWatcher; KScreen::ConfigPtr mConfig; QVariantMap mBackendArguments; QTimer mResetCrashCountTimer; bool mShuttingDown; int mRequestsCounter; QEventLoop mShutdownLoop; // For in-process operation QPluginLoader *mLoader; KScreen::AbstractBackend *mInProcessBackend; Method mMethod; }; } #endif // KSCREEN_BACKENDMANAGER_H 070701000000B5000081A40000000000000000000000016638555E00003905000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/config.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "config.h" #include "backendmanager_p.h" #include "kscreen_debug.h" #include "mode.h" #include <QCryptographicHash> #include <QDebug> #include <QRect> #include <QStringList> #include <algorithm> #include <utility> using namespace KScreen; class Q_DECL_HIDDEN Config::Private : public QObject { Q_OBJECT public: Private(Config *parent) : QObject(parent) , valid(true) , supportedFeatures(Config::Feature::None) , tabletModeAvailable(false) , tabletModeEngaged(false) , q(parent) { } KScreen::OutputPtr findPrimaryOutput() const { auto iter = std::find_if(outputs.constBegin(), outputs.constEnd(), [](const KScreen::OutputPtr &output) -> bool { return output->isPrimary(); }); return iter == outputs.constEnd() ? KScreen::OutputPtr() : iter.value(); } // output priorities may be inconsistent after this call OutputList::Iterator removeOutput(OutputList::Iterator iter) { if (iter == outputs.end()) { return iter; } const int outputId = iter.key(); OutputPtr output = iter.value(); iter = outputs.erase(iter); if (output) { output->disconnect(q); Q_EMIT q->outputRemoved(outputId); } return iter; } bool valid; ScreenPtr screen; OutputList outputs; Features supportedFeatures; bool tabletModeAvailable; bool tabletModeEngaged; private: Config *q; }; bool Config::canBeApplied(const ConfigPtr &config) { return canBeApplied(config, ValidityFlag::None); } bool Config::canBeApplied(const ConfigPtr &config, ValidityFlags flags) { if (!config) { qCDebug(KSCREEN) << "canBeApplied: Config not available, returning false"; return false; } ConfigPtr currentConfig = BackendManager::instance()->config(); if (!currentConfig) { qCDebug(KSCREEN) << "canBeApplied: Current config not available, returning false"; return false; } QRect rect; OutputPtr currentOutput; const OutputList outputs = config->outputs(); int enabledOutputsCount = 0; for (const OutputPtr &output : outputs) { if (!output->isEnabled()) { continue; } ++enabledOutputsCount; currentOutput = currentConfig->output(output->id()); // If there is no such output if (!currentOutput) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "does not exists"; return false; } // If the output is not connected if (!currentOutput->isConnected()) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "is not connected"; return false; } // if there is no currentMode if (output->currentModeId().isEmpty()) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "has no currentModeId"; return false; } // If the mode is not found in the current output if (!currentOutput->mode(output->currentModeId())) { qCDebug(KSCREEN) << "canBeApplied: The output:" << output->id() << "has no mode:" << output->currentModeId(); return false; } const ModePtr currentMode = output->currentMode(); const QSize outputSize = currentMode->size(); if (output->pos().x() < rect.x()) { rect.setX(output->pos().x()); } if (output->pos().y() < rect.y()) { rect.setY(output->pos().y()); } QPoint bottomRight; if (output->isHorizontal()) { bottomRight = QPoint(output->pos().x() + outputSize.width(), output->pos().y() + outputSize.height()); } else { bottomRight = QPoint(output->pos().x() + outputSize.height(), output->pos().y() + outputSize.width()); } if (bottomRight.x() > rect.width()) { rect.setWidth(bottomRight.x()); } if (bottomRight.y() > rect.height()) { rect.setHeight(bottomRight.y()); } } if (flags & ValidityFlag::RequireAtLeastOneEnabledScreen && enabledOutputsCount == 0) { qCDebug(KSCREEN) << "canBeAppled: There are no enabled screens, at least one required"; return false; } const int maxEnabledOutputsCount = config->screen()->maxActiveOutputsCount(); if (enabledOutputsCount > maxEnabledOutputsCount) { qCDebug(KSCREEN).nospace() << "canBeApplied: Too many active screens. Requested: " << enabledOutputsCount << ", Max: " << maxEnabledOutputsCount; return false; } if (rect.width() > config->screen()->maxSize().width()) { qCDebug(KSCREEN).nospace() << "canBeApplied: The configuration is too wide: " << rect.width() << ", Max: " << config->screen()->maxSize().width(); return false; } if (rect.height() > config->screen()->maxSize().height()) { qCDebug(KSCREEN).nospace() << "canBeApplied: The configuration is too high: " << rect.height() << ", Max: " << config->screen()->maxSize().height(); return false; } return true; } Config::Config() : QObject(nullptr) , d(new Private(this)) { } Config::~Config() { delete d; } ConfigPtr Config::clone() const { ConfigPtr newConfig(new Config()); newConfig->d->screen = d->screen->clone(); newConfig->setSupportedFeatures(supportedFeatures()); newConfig->setTabletModeAvailable(tabletModeAvailable()); newConfig->setTabletModeEngaged(tabletModeEngaged()); for (const OutputPtr &ourOutput : std::as_const(d->outputs)) { newConfig->addOutput(ourOutput->clone()); } return newConfig; } QString Config::connectedOutputsHash() const { QStringList hashedOutputs; const auto outputs = connectedOutputs(); hashedOutputs.reserve(outputs.count()); for (const OutputPtr &output : outputs) { hashedOutputs << output->hash(); } std::sort(hashedOutputs.begin(), hashedOutputs.end()); const auto hash = QCryptographicHash::hash(hashedOutputs.join(QString()).toLatin1(), QCryptographicHash::Md5); return QString::fromLatin1(hash.toHex()); } ScreenPtr Config::screen() const { return d->screen; } void Config::setScreen(const ScreenPtr &screen) { d->screen = screen; } OutputPtr Config::output(int outputId) const { return d->outputs.value(outputId); } Config::Features Config::supportedFeatures() const { return d->supportedFeatures; } void Config::setSupportedFeatures(const Config::Features &features) { d->supportedFeatures = features; } bool Config::tabletModeAvailable() const { return d->tabletModeAvailable; } void Config::setTabletModeAvailable(bool available) { d->tabletModeAvailable = available; } bool Config::tabletModeEngaged() const { return d->tabletModeEngaged; } void Config::setTabletModeEngaged(bool engaged) { d->tabletModeEngaged = engaged; } OutputList Config::outputs() const { return d->outputs; } OutputList Config::connectedOutputs() const { OutputList outputs; for (const OutputPtr &output : std::as_const(d->outputs)) { if (!output->isConnected()) { continue; } outputs.insert(output->id(), output); } return outputs; } OutputPtr Config::primaryOutput() const { return d->findPrimaryOutput(); } void Config::setPrimaryOutput(const OutputPtr &newPrimary) { setOutputPriority(newPrimary, 1); } void Config::addOutput(const OutputPtr &output) { d->outputs.insert(output->id(), output); output->setExplicitLogicalSize(logicalSizeForOutput(*output)); Q_EMIT outputAdded(output); } void Config::removeOutput(int outputId) { d->removeOutput(d->outputs.find(outputId)); } void Config::setOutputs(const OutputList &outputs) { for (auto iter = d->outputs.begin(), end = d->outputs.end(); iter != end;) { iter = d->removeOutput(iter); end = d->outputs.end(); } for (const OutputPtr &output : outputs) { addOutput(output); } adjustPriorities(); } void Config::setOutputPriority(const OutputPtr &output, uint32_t priority) { if (!d->outputs.contains(output->id()) || d->outputs[output->id()] != output) { qCDebug(KSCREEN) << "The output" << output << "does not belong to this config"; return; } if (output->priority() == priority) { return; } output->setEnabled(priority != 0); output->setPriority(priority); adjustPriorities((priority != 0) ? std::optional(output) : std::nullopt); } void Config::setOutputPriorities(QMap<OutputPtr, uint32_t> &priorities) { for (auto it = priorities.constBegin(); it != priorities.constEnd(); it++) { const OutputPtr &output = it.key(); const uint32_t priority = it.value(); if (!d->outputs.contains(output->id()) || d->outputs[output->id()] != output) { qCDebug(KSCREEN) << "The output" << output << "does not belong to this config"; return; } output->setEnabled(priority != 0); output->setPriority(priority); } adjustPriorities(); } static std::optional<OutputPtr> removeOptional(QList<OutputPtr> &haystack, std::optional<OutputPtr> &needle) { if (!needle.has_value()) { return std::nullopt; } const OutputPtr &value = needle.value(); const bool removed = haystack.removeOne(value); return removed ? needle : std::nullopt; } void Config::adjustPriorities(std::optional<OutputPtr> keep) { // we need specifically tree-based QMap for this QMap<uint32_t, QList<OutputPtr>> multimap; uint32_t maxPriority = 0; bool found = false; for (const OutputPtr &output : d->outputs) { maxPriority = std::max(maxPriority, output->priority()); } if (keep.has_value() && keep.value()->priority() == 0) { qCDebug(KSCREEN) << "The output to keep" << keep.value() << "has zero priority. Did you forget to set priority after enabling it?"; keep.reset(); } for (const OutputPtr &output : d->outputs) { if (keep.has_value() && keep.value() == output) { found = true; } if (!output->isEnabled()) { output->setPriority(0); } else { // XXX: we are currently not enforcing consistency after enabling an output. if (output->priority() == 0) { output->setPriority(maxPriority + 1); } QList<OutputPtr> &entry = multimap[output->priority()]; entry.append(output); } } if (keep.has_value() && !found) { qCDebug(KSCREEN) << "The output to keep" << keep.value() << "is not in the list of outputs" << d->outputs; keep.reset(); } uint32_t nextPriority = 1; for (QList<OutputPtr> ¤t_list : multimap) { std::optional<OutputPtr> currentKeep = removeOptional(current_list, keep); // deterministic sorting of identically-prioritized outputs. // ordering reversed, so that later we can use pop() operation instead of removing from the beginning. std::stable_sort(current_list.begin(), current_list.end(), [](const OutputPtr &lhs, const OutputPtr &rhs) -> bool { return rhs->name() < lhs->name(); }); while (currentKeep.has_value() || !current_list.isEmpty()) { OutputPtr nextOutput; if (currentKeep.has_value() && (currentKeep.value()->priority() <= nextPriority || current_list.isEmpty())) { nextOutput = currentKeep.value(); currentKeep.reset(); } else { Q_ASSERT(!current_list.isEmpty()); nextOutput = current_list.takeLast(); } nextOutput->setPriority(nextPriority); nextPriority += 1; } } Q_EMIT prioritiesChanged(); } bool Config::isValid() const { return d->valid; } void Config::setValid(bool valid) { d->valid = valid; } void Config::apply(const ConfigPtr &other) { d->screen->apply(other->screen()); setTabletModeAvailable(other->tabletModeAvailable()); setTabletModeEngaged(other->tabletModeEngaged()); // Remove removed outputs for (auto it = d->outputs.begin(); it != d->outputs.end();) { if (!other->d->outputs.contains((*it)->id())) { it = d->removeOutput(it); } else { ++it; } } for (const OutputPtr &otherOutput : std::as_const(other->d->outputs)) { // Add new outputs if (!d->outputs.contains(otherOutput->id())) { addOutput(otherOutput->clone()); } else { // Update existing outputs d->outputs[otherOutput->id()]->apply(otherOutput); d->outputs[otherOutput->id()]->setExplicitLogicalSize(logicalSizeForOutput(*d->outputs[otherOutput->id()])); } } // Update validity setValid(other->isValid()); Q_EMIT prioritiesChanged(); } QRect Config::outputGeometryForOutput(const KScreen::Output &output) const { QSize size = logicalSizeForOutput(output).toSize(); if (!size.isValid()) { return QRect(); } return QRect(output.pos(), size); } QSizeF Config::logicalSizeForOutput(const KScreen::Output &output) const { QSizeF size = output.enforcedModeSize(); if (!size.isValid()) { return QSizeF(); } // ignore scale where scaling is not per-output if (supportedFeatures().testFlag(Feature::PerOutputScaling)) { size = size / output.scale(); } // We can't use output.size(), because it does not reflect the actual rotation() set by caller. // It is only updated when we get update from KScreen, but not when user changes mode or // rotation manually. if (!output.isHorizontal()) { size = size.transposed(); } return size; } QDebug operator<<(QDebug dbg, const KScreen::ConfigPtr &config) { if (config) { dbg << "KScreen::Config("; const auto outputs = config->outputs(); for (const auto &output : outputs) { if (output->isConnected()) { dbg << Qt::endl << output; } } dbg << ")"; } else { dbg << "KScreen::Config(NULL)"; } return dbg; } #include "config.moc" 070701000000B6000081A40000000000000000000000016638555E000024A1000000000000000000000000000000000000003B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/config.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_CONFIG_H #define KSCREEN_CONFIG_H #include "kscreen_export.h" #include "output.h" #include "screen.h" #include "types.h" #include <QHash> #include <QMetaType> #include <QObject> #include <cstdint> #include <optional> namespace KScreen { class Output; /** * Represents a (or the) screen configuration. * * This is the main class of KScreen, with it you can use * the static methods current() to get the systems config and * setConfig() to apply a config to the system. * * Also, you can instantiate an empty Config, this is usually done * to create a config (with the objective of setting it) from scratch * and for example unserialize a saved config to it. * */ class KSCREEN_EXPORT Config : public QObject { Q_OBJECT Q_PROPERTY(ScreenPtr screen READ screen) Q_PROPERTY(OutputList outputs READ outputs) public: enum class ValidityFlag { None = 0x0, RequireAtLeastOneEnabledScreen = 0x1, }; Q_ENUM(ValidityFlag) Q_DECLARE_FLAGS(ValidityFlags, ValidityFlag) /** This indicates which features the used backend supports. * * @see supportedFeatures * @since 5.7 */ enum class Feature { None = 0, ///< None of the mentioned features are supported. PrimaryDisplay = 1, ///< The backend knows about the concept of a primary display Writable = 1 << 1, ///< The backend supports setting the config, it's not read-only. PerOutputScaling = 1 << 2, ///< The backend supports scaling each output individually. OutputReplication = 1 << 3, ///< The backend supports replication of outputs. AutoRotation = 1 << 4, ///< The backend supports automatic rotation of outputs. TabletMode = 1 << 5, ///< The backend supports querying if a device is in tablet mode. SynchronousOutputChanges = 1 << 6, ///< The backend supports blocking until the output setting changes are applied XwaylandScales = 1 << 7, ///< The backend supports adapting Xwayland clients to a certain scale }; Q_ENUM(Feature) Q_DECLARE_FLAGS(Features, Feature) /** * Validates that a config can be applied in the current system * * Each system has different constrains, this method will test * the given config with those constrains to see if it * can be applied. * * @arg config to be checked * @flags enable additional optional checks * @return true if the configuration can be applied, false if not. * @since 5.3.0 */ static bool canBeApplied(const ConfigPtr &config, ValidityFlags flags); /** * Validates that a config can be applied in the current system * * Each system has different constrains, this method will test * the given config with those constrains to see if it * can be applied. * * @arg config to be checked * @return true if the configuration can be applied, false if not. */ static bool canBeApplied(const ConfigPtr &config); /** * Instantiate an empty config * * Usually you do not want to use this constructor since there are some * values that make no sense to set (for example you want the Screen of * the current systme). * * So usually what you do is call current() and then modify * whatever you need. */ explicit Config(); ~Config() override; /** * Duplicates the config * * @return a new Config instance with same property values */ ConfigPtr clone() const; /** * Returns an identifying hash for this config in regards to its * connected outputs. * * The hash is calculated with a sorted combination of all * connected output hashes. * * @return sorted hash combination of all connected outputs * @since 5.15 */ QString connectedOutputsHash() const; ScreenPtr screen() const; void setScreen(const ScreenPtr &screen); OutputPtr output(int outputId) const; OutputList outputs() const; OutputList connectedOutputs() const; /** * Find primary output. Primary output is the output with priority 1. May be * null. */ OutputPtr primaryOutput() const; /** * Setting output to be the primary one is equivalent to setting its * priority to 1. */ void setPrimaryOutput(const OutputPtr &output); /** * Add an output to this configuration. * * This method does not ensure consistency of priorities, it is up to the * caller to perform necessary adjustments afterwards. The reason is that it * might be used in a loop (such as adding all outputs) where committing * intermediate states is undesirable. */ void addOutput(const OutputPtr &output); /** * Remove an output with matching ID from this configuration. * * This method does not ensure consistency of priorities, it is up to the * caller to perform necessary adjustments afterwards. The reason is that it * might be used in a loop (such as removing all outputs) where committing * intermediate states is undesirable. */ void removeOutput(int outputId); /** * Replace all existing outputs with the given ones. * * Unlike addOutput and removeOutput which operate on individual items * presumably in a loop, this method will call adjustPriorities() before * returning. */ void setOutputs(const OutputList &outputs); /** * Set output's priority and call adjustPriorities() trying to retain * relative ordering of the output. Setting priority to zero with this * method will disable the output, otherwise the output will be enabled. */ void setOutputPriority(const OutputPtr &output, uint32_t priority); void setOutputPriorities(QMap<OutputPtr, uint32_t> &priorities); /** * Ensure consistency and continuity of priorities. * * Most methods operating on outputs are doing so in loop, where committing * intermediate states is undesirable. This method restores the balance by * settings priority of all disabled outputs to 0, disabling all outputs * whose priority is 0 (i.e. it works in both directions), and sorting all * the remaining ones, such that they are numbered strictly sequentially * starting from 1. * @param keep The output, which priority should stay as close as possible * to its current one. It is not possible to guarantee, but the algorithm * will do its best to prioritize this output among others, if there happens * to be multiple ones with the same priority number. */ void adjustPriorities(std::optional<OutputPtr> keep = std::nullopt); bool isValid() const; void setValid(bool valid); void apply(const ConfigPtr &other); /** Indicates features supported by the backend. This exists to allow the user * to find out which of the features offered by libkscreen are actually supported * by the backend. Not all backends are writable (QScreen, for example is * read-only, only XRandR, but not KWayland support the primary display, etc.). * * @return Flags for features that are supported for this config, determined by * the backend. * @see setSupportedFeatures * @since 5.7 */ Features supportedFeatures() const; /** Sets the features supported by this backend. This should not be called by the * user, but by the backend. * * @see supportedFeatures * @since 5.7 */ void setSupportedFeatures(const Features &features); /** * Indicates that the device supports switching between a default and a tablet mode. This is * common for convertibles. * * @return true when tablet mode is available, otherwise false * @see setTabletModeAvailable * @since 5.18 */ bool tabletModeAvailable() const; /** Sets if the device supports a tablet mode. This should not be called by the * user, but by the backend. * * @see tabletModeAvailable * @since 5.18 */ void setTabletModeAvailable(bool available); /** * Indicates that the device is currently in tablet mode. * * @return true when in tablet mode, otherwise false * @see setTabletModeEngaged * @since 5.18 */ bool tabletModeEngaged() const; /** * Sets if the device is currently in tablet mode. This should not be called by the * user, but by the backend. * * @see tabletModeEngaged * @since 5.18 */ void setTabletModeEngaged(bool engaged); QRect outputGeometryForOutput(const KScreen::Output &output) const; QSizeF logicalSizeForOutput(const KScreen::Output &output) const; Q_SIGNALS: void outputAdded(const KScreen::OutputPtr &output); void outputRemoved(int outputId); void prioritiesChanged(); private: Q_DISABLE_COPY(Config) class Private; Private *const d; }; } // KScreen namespace Q_DECLARE_OPERATORS_FOR_FLAGS(KScreen::Config::Features) KSCREEN_EXPORT QDebug operator<<(QDebug dbg, const KScreen::ConfigPtr &config); #endif // KSCREEN_CONFIG_H 070701000000B7000081A40000000000000000000000016638555E00001E37000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configmonitor.cpp/* * SPDX-FileCopyrightText: 2012-2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "configmonitor.h" #include "abstractbackend.h" #include "backendinterface.h" #include "backendmanager_p.h" #include "configserializer_p.h" #include "getconfigoperation.h" #include "kscreen_debug.h" #include "output.h" #include <QDBusPendingCallWatcher> using namespace KScreen; class Q_DECL_HIDDEN ConfigMonitor::Private : public QObject { Q_OBJECT public: Private(ConfigMonitor *q); void onBackendReady(org::kde::kscreen::Backend *backend); void backendConfigChanged(const QVariantMap &configMap); void configDestroyed(QObject *removedConfig); void getConfigFinished(ConfigOperation *op); void updateConfigs(const KScreen::ConfigPtr &newConfig); void edidReady(QDBusPendingCallWatcher *watcher); QList<QWeakPointer<KScreen::Config>> watchedConfigs; QPointer<org::kde::kscreen::Backend> mBackend; bool mFirstBackend; QMap<KScreen::ConfigPtr, QList<int>> mPendingEDIDRequests; private: ConfigMonitor *q; }; ConfigMonitor::Private::Private(ConfigMonitor *q) : QObject(q) , mFirstBackend(true) , q(q) { } void ConfigMonitor::Private::onBackendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); if (backend == mBackend) { return; } if (mBackend) { disconnect(mBackend.data(), &org::kde::kscreen::Backend::configChanged, this, &ConfigMonitor::Private::backendConfigChanged); } mBackend = QPointer<org::kde::kscreen::Backend>(backend); // If we received a new backend interface, then it's very likely that it is // because the backend process has crashed - just to be sure we haven't missed // any change, request the current config now and update our watched configs // // Only request the config if this is not initial backend request, because it // can happen that if a change happened before now, or before we get the config, // the result will be invalid. This can happen when KScreen KDED launches and // detects changes need to be done. if (!mFirstBackend && !watchedConfigs.isEmpty()) { connect(new GetConfigOperation(), &GetConfigOperation::finished, this, &Private::getConfigFinished); } mFirstBackend = false; connect(mBackend.data(), &org::kde::kscreen::Backend::configChanged, this, &ConfigMonitor::Private::backendConfigChanged); } void ConfigMonitor::Private::getConfigFinished(ConfigOperation *op) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); if (op->hasError()) { qCWarning(KSCREEN) << "Failed to retrieve current config: " << op->errorString(); return; } const KScreen::ConfigPtr config = qobject_cast<GetConfigOperation *>(op)->config(); updateConfigs(config); } void ConfigMonitor::Private::backendConfigChanged(const QVariantMap &configMap) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); ConfigPtr newConfig = ConfigSerializer::deserializeConfig(configMap); if (!newConfig) { qCWarning(KSCREEN) << "Failed to deserialize config from DBus change notification"; return; } const auto connectedOutputs = newConfig->connectedOutputs(); for (const OutputPtr &output : connectedOutputs) { if (!output->edid() && output->isConnected()) { QDBusPendingReply<QByteArray> reply = mBackend->getEdid(output->id()); mPendingEDIDRequests[newConfig].append(output->id()); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); watcher->setProperty("outputId", output->id()); watcher->setProperty("config", QVariant::fromValue(newConfig)); connect(watcher, &QDBusPendingCallWatcher::finished, this, &ConfigMonitor::Private::edidReady); } } if (mPendingEDIDRequests.contains(newConfig)) { qCDebug(KSCREEN) << "Requesting missing EDID for outputs" << mPendingEDIDRequests[newConfig]; } else { updateConfigs(newConfig); } } void ConfigMonitor::Private::edidReady(QDBusPendingCallWatcher *watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); const int outputId = watcher->property("outputId").toInt(); const ConfigPtr config = watcher->property("config").value<KScreen::ConfigPtr>(); Q_ASSERT(mPendingEDIDRequests.contains(config)); Q_ASSERT(mPendingEDIDRequests[config].contains(outputId)); watcher->deleteLater(); mPendingEDIDRequests[config].removeOne(outputId); const QDBusPendingReply<QByteArray> reply = *watcher; if (reply.isError()) { qCWarning(KSCREEN) << "Error when retrieving EDID: " << reply.error().message(); } else { const QByteArray edid = reply.argumentAt<0>(); if (!edid.isEmpty()) { OutputPtr output = config->output(outputId); output->setEdid(edid); } } if (mPendingEDIDRequests[config].isEmpty()) { mPendingEDIDRequests.remove(config); updateConfigs(config); } } void ConfigMonitor::Private::updateConfigs(const KScreen::ConfigPtr &newConfig) { QMutableListIterator<QWeakPointer<Config>> iter(watchedConfigs); while (iter.hasNext()) { KScreen::ConfigPtr config = iter.next().toStrongRef(); if (!config) { iter.remove(); continue; } config->apply(newConfig); iter.setValue(config.toWeakRef()); } Q_EMIT q->configurationChanged(); } void ConfigMonitor::Private::configDestroyed(QObject *removedConfig) { for (auto iter = watchedConfigs.begin(); iter != watchedConfigs.end();) { if (iter->toStrongRef() == removedConfig) { iter = watchedConfigs.erase(iter); // Iterate over the entire list in case there are duplicates } else { ++iter; } } } ConfigMonitor *ConfigMonitor::instance() { static ConfigMonitor *s_instance = nullptr; if (s_instance == nullptr) { s_instance = new ConfigMonitor(); } return s_instance; } ConfigMonitor::ConfigMonitor() : QObject() , d(new Private(this)) { if (BackendManager::instance()->method() == BackendManager::OutOfProcess) { connect(BackendManager::instance(), &BackendManager::backendReady, d, &ConfigMonitor::Private::onBackendReady); BackendManager::instance()->requestBackend(); } } ConfigMonitor::~ConfigMonitor() { delete d; } void ConfigMonitor::addConfig(const ConfigPtr &config) { const QWeakPointer<Config> weakConfig = config.toWeakRef(); if (!d->watchedConfigs.contains(weakConfig)) { connect(weakConfig.toStrongRef().data(), &QObject::destroyed, d, &Private::configDestroyed); d->watchedConfigs << weakConfig; } } void ConfigMonitor::removeConfig(const ConfigPtr &config) { const QWeakPointer<Config> weakConfig = config.toWeakRef(); if (d->watchedConfigs.contains(config)) { disconnect(weakConfig.toStrongRef().data(), &QObject::destroyed, d, &Private::configDestroyed); d->watchedConfigs.removeAll(config); } } void ConfigMonitor::connectInProcessBackend(KScreen::AbstractBackend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); connect(backend, &AbstractBackend::configChanged, [=](KScreen::ConfigPtr config) { if (config.isNull()) { return; } qCDebug(KSCREEN) << "Backend change!" << config; d->updateConfigs(config); }); } #include "configmonitor.moc" 070701000000B8000081A40000000000000000000000016638555E000003A5000000000000000000000000000000000000004200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configmonitor.h/* * SPDX-FileCopyrightText: 2012-2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_CONFIGMONITOR_H #define KSCREEN_CONFIGMONITOR_H #include <QObject> #include <QPointer> #include "config.h" #include "kscreen_export.h" namespace KScreen { class AbstractBackend; class BackendManager; class KSCREEN_EXPORT ConfigMonitor : public QObject { Q_OBJECT public: static ConfigMonitor *instance(); void addConfig(const KScreen::ConfigPtr &config); void removeConfig(const KScreen::ConfigPtr &config); Q_SIGNALS: void configurationChanged(); private: explicit ConfigMonitor(); ~ConfigMonitor() override; Q_DISABLE_COPY(ConfigMonitor) friend BackendManager; void connectInProcessBackend(KScreen::AbstractBackend *backend); class Private; Private *const d; }; } /* namespace KScreen */ #endif // KSCREEN_CONFIGMONITOR_H 070701000000B9000081A40000000000000000000000016638555E00000DB1000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configoperation.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "configoperation.h" #include "backendmanager_p.h" #include "configoperation_p.h" #include "kscreen_debug.h" using namespace KScreen; ConfigOperationPrivate::ConfigOperationPrivate(ConfigOperation *qq) : QObject() , isExec(false) , q_ptr(qq) { } ConfigOperationPrivate::~ConfigOperationPrivate() { } void ConfigOperationPrivate::requestBackend() { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); connect(BackendManager::instance(), &BackendManager::backendReady, this, &ConfigOperationPrivate::backendReady); BackendManager::instance()->requestBackend(); } void ConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_UNUSED(backend); disconnect(BackendManager::instance(), &BackendManager::backendReady, this, &ConfigOperationPrivate::backendReady); } void ConfigOperationPrivate::doEmitResult() { Q_Q(ConfigOperation); Q_EMIT q->finished(q); // Don't call deleteLater() when this operation is running from exec() // because then the operation will be deleted when we return control to // the nested QEventLoop in exec() (i.e. before loop.exec() returns) // and subsequent hasError() call references deleted "this". Instead we // shedule the operation for deletion manually in exec(), so that it will // be deleted when control returns to parent event loop (or QApplication). if (!isExec) { q->deleteLater(); } } ConfigOperation::ConfigOperation(ConfigOperationPrivate *dd, QObject *parent) : QObject(parent) , d_ptr(dd) { const bool ok = QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection); Q_ASSERT(ok); Q_UNUSED(ok); } ConfigOperation::~ConfigOperation() { delete d_ptr; } bool ConfigOperation::hasError() const { Q_D(const ConfigOperation); return !d->error.isEmpty(); } QString ConfigOperation::errorString() const { Q_D(const ConfigOperation); return d->error; } void ConfigOperation::setError(const QString &error) { Q_D(ConfigOperation); d->error = error; } void ConfigOperation::emitResult() { Q_D(ConfigOperation); const bool ok = QMetaObject::invokeMethod(d, "doEmitResult", Qt::QueuedConnection); Q_ASSERT(ok); Q_UNUSED(ok); } bool ConfigOperation::exec() { Q_D(ConfigOperation); QEventLoop loop; connect(this, &ConfigOperation::finished, this, [&](ConfigOperation *op) { Q_UNUSED(op); loop.quit(); }); d->isExec = true; loop.exec(QEventLoop::ExcludeUserInputEvents); // Schedule the operation for deletion, see doEmitResult() deleteLater(); return !hasError(); } KScreen::AbstractBackend *ConfigOperationPrivate::loadBackend() { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); Q_Q(ConfigOperation); const QString &name = QString::fromUtf8(qgetenv("KSCREEN_BACKEND")); auto backend = KScreen::BackendManager::instance()->loadBackendInProcess(name); if (backend == nullptr) { const QString &e = QStringLiteral("Plugin does not provide valid KScreen backend"); qCDebug(KSCREEN) << e; q->setError(e); q->emitResult(); } return backend; } 070701000000BA000081A40000000000000000000000016638555E0000046A000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configoperation.h/* * <one line to give the library's name and an idea of what it does.> * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef KSCREEN_CONFIGOPERATION_H #define KSCREEN_CONFIGOPERATION_H #include <QObject> #include "kscreen_export.h" #include "types.h" namespace KScreen { class ConfigOperationPrivate; class KSCREEN_EXPORT ConfigOperation : public QObject { Q_OBJECT public: enum Option { NoOptions, NoEDID, }; Q_DECLARE_FLAGS(Options, Option) ~ConfigOperation() override; bool hasError() const; QString errorString() const; virtual KScreen::ConfigPtr config() const = 0; bool exec(); Q_SIGNALS: void finished(ConfigOperation *operation); protected: explicit ConfigOperation(ConfigOperationPrivate *dd, QObject *parent = nullptr); void setError(const QString &error); void emitResult(); protected Q_SLOTS: virtual void start() = 0; protected: ConfigOperationPrivate *const d_ptr; Q_DECLARE_PRIVATE(ConfigOperation) }; } #endif // KSCREEN_CONFIGOPERATION_H 070701000000BB000081A40000000000000000000000016638555E00000362000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configoperation_p.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef CONFIGOPERATIONPRIVATE_H #define CONFIGOPERATIONPRIVATE_H #include <QObject> #include "abstractbackend.h" #include "backendinterface.h" #include "configoperation.h" namespace KScreen { class ConfigOperationPrivate : public QObject { Q_OBJECT public: explicit ConfigOperationPrivate(ConfigOperation *qq); ~ConfigOperationPrivate() override; // For out-of-process void requestBackend(); virtual void backendReady(org::kde::kscreen::Backend *backend); // For in-process KScreen::AbstractBackend *loadBackend(); public Q_SLOTS: void doEmitResult(); private: QString error; bool isExec; protected: ConfigOperation *const q_ptr; Q_DECLARE_PUBLIC(ConfigOperation) }; } #endif 070701000000BC000081A40000000000000000000000016638555E00003284000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configserializer.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "configserializer_p.h" #include "config.h" #include "kscreen_debug.h" #include "mode.h" #include "output.h" #include "screen.h" #include <QDBusArgument> #include <QFile> #include <QJsonDocument> #include <QRect> #include <cstdint> #include <optional> using namespace KScreen; QJsonObject ConfigSerializer::serializePoint(const QPoint &point) { QJsonObject obj; obj[QLatin1String("x")] = point.x(); obj[QLatin1String("y")] = point.y(); return obj; } QJsonObject ConfigSerializer::serializeSize(const QSize &size) { QJsonObject obj; obj[QLatin1String("width")] = size.width(); obj[QLatin1String("height")] = size.height(); return obj; } QJsonObject ConfigSerializer::serializeConfig(const ConfigPtr &config) { QJsonObject obj; if (!config) { return obj; } obj[QLatin1String("features")] = static_cast<int>(config->supportedFeatures()); QJsonArray outputs; for (const OutputPtr &output : config->outputs()) { outputs.append(serializeOutput(output)); } obj[QLatin1String("outputs")] = outputs; if (config->screen()) { obj[QLatin1String("screen")] = serializeScreen(config->screen()); } obj[QLatin1String("tabletModeAvailable")] = config->tabletModeAvailable(); obj[QLatin1String("tabletModeEngaged")] = config->tabletModeEngaged(); return obj; } QJsonObject ConfigSerializer::serializeOutput(const OutputPtr &output) { QJsonObject obj; obj[QLatin1String("id")] = output->id(); obj[QLatin1String("name")] = output->name(); obj[QLatin1String("type")] = static_cast<int>(output->type()); obj[QLatin1String("icon")] = output->icon(); obj[QLatin1String("pos")] = serializePoint(output->pos()); obj[QLatin1String("scale")] = output->scale(); obj[QLatin1String("size")] = serializeSize(output->size()); obj[QLatin1String("rotation")] = static_cast<int>(output->rotation()); obj[QLatin1String("currentModeId")] = output->currentModeId(); obj[QLatin1String("preferredModes")] = serializeList(output->preferredModes()); obj[QLatin1String("connected")] = output->isConnected(); obj[QLatin1String("followPreferredMode")] = output->followPreferredMode(); obj[QLatin1String("enabled")] = output->isEnabled(); obj[QLatin1String("priority")] = static_cast<int>(output->priority()); obj[QLatin1String("clones")] = serializeList(output->clones()); // obj[QLatin1String("edid")] = output->edid()->raw(); obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm()); obj[QLatin1String("replicationSource")] = output->replicationSource(); QJsonArray modes; for (const ModePtr &mode : output->modes()) { modes.append(serializeMode(mode)); } obj[QLatin1String("modes")] = modes; if (output->capabilities() & Output::Capability::Overscan) { obj[QLatin1String("overscan")] = static_cast<int>(output->overscan()); } if (output->capabilities() & Output::Capability::Vrr) { obj[QLatin1String("vrrPolicy")] = static_cast<int>(output->vrrPolicy()); } if (output->capabilities() & Output::Capability::RgbRange) { obj[QLatin1String("rgbRange")] = static_cast<int>(output->rgbRange()); } return obj; } QJsonObject ConfigSerializer::serializeMode(const ModePtr &mode) { QJsonObject obj; obj[QLatin1String("id")] = mode->id(); obj[QLatin1String("name")] = mode->name(); obj[QLatin1String("size")] = serializeSize(mode->size()); obj[QLatin1String("refreshRate")] = mode->refreshRate(); return obj; } QJsonObject ConfigSerializer::serializeScreen(const ScreenPtr &screen) { QJsonObject obj; obj[QLatin1String("id")] = screen->id(); obj[QLatin1String("currentSize")] = serializeSize(screen->currentSize()); obj[QLatin1String("maxSize")] = serializeSize(screen->maxSize()); obj[QLatin1String("minSize")] = serializeSize(screen->minSize()); obj[QLatin1String("maxActiveOutputsCount")] = screen->maxActiveOutputsCount(); return obj; } QPoint ConfigSerializer::deserializePoint(const QDBusArgument &arg) { int x = 0, y = 0; arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1Char('x')) { x = value.toInt(); } else if (key == QLatin1Char('y')) { y = value.toInt(); } else { qCWarning(KSCREEN) << "Invalid key in Point map: " << key; return QPoint(); } arg.endMapEntry(); } arg.endMap(); return QPoint(x, y); } QSize ConfigSerializer::deserializeSize(const QDBusArgument &arg) { int w = 0, h = 0; arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("width")) { w = value.toInt(); } else if (key == QLatin1String("height")) { h = value.toInt(); } else { qCWarning(KSCREEN) << "Invalid key in size struct: " << key; return QSize(); } arg.endMapEntry(); } arg.endMap(); return QSize(w, h); } ConfigPtr ConfigSerializer::deserializeConfig(const QVariantMap &map) { ConfigPtr config(new Config); if (map.contains(QLatin1String("features"))) { config->setSupportedFeatures(static_cast<Config::Features>(map[QStringLiteral("features")].toInt())); } if (map.contains(QLatin1String("tabletModeAvailable"))) { config->setTabletModeAvailable(map[QStringLiteral("tabletModeAvailable")].toBool()); } if (map.contains(QLatin1String("tabletModeEngaged"))) { config->setTabletModeEngaged(map[QStringLiteral("tabletModeEngaged")].toBool()); } if (map.contains(QLatin1String("outputs"))) { const QDBusArgument &outputsArg = map[QStringLiteral("outputs")].value<QDBusArgument>(); outputsArg.beginArray(); OutputList outputs; while (!outputsArg.atEnd()) { QVariant value; outputsArg >> value; const KScreen::OutputPtr output = deserializeOutput(value.value<QDBusArgument>()); if (!output) { return ConfigPtr(); } outputs.insert(output->id(), output); } outputsArg.endArray(); config->setOutputs(outputs); } if (map.contains(QLatin1String("screen"))) { const QDBusArgument &screenArg = map[QStringLiteral("screen")].value<QDBusArgument>(); const KScreen::ScreenPtr screen = deserializeScreen(screenArg); if (!screen) { return ConfigPtr(); } config->setScreen(screen); } return config; } OutputPtr ConfigSerializer::deserializeOutput(const QDBusArgument &arg) { OutputPtr output(new Output); std::optional<bool> primary = std::nullopt; std::optional<uint32_t> priority = std::nullopt; arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { output->setId(value.toInt()); } else if (key == QLatin1String("name")) { output->setName(value.toString()); } else if (key == QLatin1String("type")) { output->setType(static_cast<Output::Type>(value.toInt())); } else if (key == QLatin1String("icon")) { output->setIcon(value.toString()); } else if (key == QLatin1String("pos")) { output->setPos(deserializePoint(value.value<QDBusArgument>())); } else if (key == QLatin1String("scale")) { output->setScale(value.toDouble()); } else if (key == QLatin1String("size")) { output->setSize(deserializeSize(value.value<QDBusArgument>())); } else if (key == QLatin1String("rotation")) { output->setRotation(static_cast<Output::Rotation>(value.toInt())); } else if (key == QLatin1String("currentModeId")) { output->setCurrentModeId(value.toString()); } else if (key == QLatin1String("preferredModes")) { output->setPreferredModes(deserializeList<QString>(value.value<QDBusArgument>())); } else if (key == QLatin1String("connected")) { output->setConnected(value.toBool()); } else if (key == QLatin1String("followPreferredMode")) { output->setFollowPreferredMode(value.toBool()); } else if (key == QLatin1String("enabled")) { output->setEnabled(value.toBool()); } else if (key == QLatin1String("primary")) { // primary is deprecated, but if it appears in config for compatibility reason. primary = value.toBool(); } else if (key == QLatin1String("priority")) { // "priority" takes precedence over "primary", but we need to // check it after the loop, otherwise it may come before the // primary and get overridden. priority = value.toUInt(); } else if (key == QLatin1String("clones")) { output->setClones(deserializeList<int>(value.value<QDBusArgument>())); } else if (key == QLatin1String("replicationSource")) { output->setReplicationSource(value.toInt()); } else if (key == QLatin1String("sizeMM")) { output->setSizeMm(deserializeSize(value.value<QDBusArgument>())); } else if (key == QLatin1String("modes")) { const QDBusArgument arg = value.value<QDBusArgument>(); ModeList modes; arg.beginArray(); while (!arg.atEnd()) { QVariant value; arg >> value; const KScreen::ModePtr mode = deserializeMode(value.value<QDBusArgument>()); if (!mode) { return OutputPtr(); } modes.insert(mode->id(), mode); } arg.endArray(); output->setModes(modes); } else if (key == QLatin1String("overscan")) { output->setOverscan(value.toUInt()); } else if (key == QLatin1String("vrrPolicy")) { output->setVrrPolicy(static_cast<Output::VrrPolicy>(value.toInt())); } else if (key == QLatin1String("rgbRange")) { output->setRgbRange(static_cast<Output::RgbRange>(value.toInt())); } else { qCWarning(KSCREEN) << "Invalid key in Output map: " << key; return OutputPtr(); } arg.endMapEntry(); } arg.endMap(); if (primary.has_value()) { output->setPriority(output->isEnabled() ? (primary.value() ? 1 : 2) : 0); } if (priority.has_value()) { output->setPriority(priority.value()); } return output; } ModePtr ConfigSerializer::deserializeMode(const QDBusArgument &arg) { ModePtr mode(new Mode); arg.beginMap(); while (!arg.atEnd()) { QString key; QVariant value; arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { mode->setId(value.toString()); } else if (key == QLatin1String("name")) { mode->setName(value.toString()); } else if (key == QLatin1String("size")) { mode->setSize(deserializeSize(value.value<QDBusArgument>())); } else if (key == QLatin1String("refreshRate")) { mode->setRefreshRate(value.toFloat()); } else { qCWarning(KSCREEN) << "Invalid key in Mode map: " << key; return ModePtr(); } arg.endMapEntry(); } arg.endMap(); return mode; } ScreenPtr ConfigSerializer::deserializeScreen(const QDBusArgument &arg) { ScreenPtr screen(new Screen); arg.beginMap(); QString key; QVariant value; while (!arg.atEnd()) { arg.beginMapEntry(); arg >> key >> value; if (key == QLatin1String("id")) { screen->setId(value.toInt()); } else if (key == QLatin1String("maxActiveOutputsCount")) { screen->setMaxActiveOutputsCount(value.toInt()); } else if (key == QLatin1String("currentSize")) { screen->setCurrentSize(deserializeSize(value.value<QDBusArgument>())); } else if (key == QLatin1String("maxSize")) { screen->setMaxSize(deserializeSize(value.value<QDBusArgument>())); } else if (key == QLatin1String("minSize")) { screen->setMinSize(deserializeSize(value.value<QDBusArgument>())); } else { qCWarning(KSCREEN) << "Invalid key in Screen map:" << key; return ScreenPtr(); } arg.endMapEntry(); } arg.endMap(); return screen; } 070701000000BD000081A40000000000000000000000016638555E000006CF000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/configserializer_p.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef CONFIGSERIALIZER_H #define CONFIGSERIALIZER_H #include <QDBusArgument> #include <QJsonArray> #include <QJsonObject> #include <QVariant> #include "kscreen_export.h" #include "types.h" namespace KScreen { namespace ConfigSerializer { KSCREEN_EXPORT QJsonObject serializePoint(const QPoint &point); KSCREEN_EXPORT QJsonObject serializeSize(const QSize &size); template<typename T> KSCREEN_EXPORT QJsonArray serializeList(const QList<T> &list) { QJsonArray arr; for (const T &t : list) { arr.append(t); } return arr; } KSCREEN_EXPORT QJsonObject serializeConfig(const KScreen::ConfigPtr &config); KSCREEN_EXPORT QJsonObject serializeOutput(const KScreen::OutputPtr &output); KSCREEN_EXPORT QJsonObject serializeMode(const KScreen::ModePtr &mode); KSCREEN_EXPORT QJsonObject serializeScreen(const KScreen::ScreenPtr &screen); KSCREEN_EXPORT QPoint deserializePoint(const QDBusArgument &map); KSCREEN_EXPORT QSize deserializeSize(const QDBusArgument &map); template<typename T> KSCREEN_EXPORT QList<T> deserializeList(const QDBusArgument &arg) { QList<T> list; arg.beginArray(); while (!arg.atEnd()) { QVariant v; arg >> v; list.append(v.value<T>()); } arg.endArray(); return list; } KSCREEN_EXPORT KScreen::ConfigPtr deserializeConfig(const QVariantMap &map); KSCREEN_EXPORT KScreen::OutputPtr deserializeOutput(const QDBusArgument &output); KSCREEN_EXPORT KScreen::ModePtr deserializeMode(const QDBusArgument &mode); KSCREEN_EXPORT KScreen::ScreenPtr deserializeScreen(const QDBusArgument &screen); } } #endif // CONFIGSERIALIZER_H 070701000000BE000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor070701000000BF000081A40000000000000000000000016638555E0000012B000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor/CMakeLists.txtadd_executable(kscreen-doctor main.cpp doctor.cpp) target_link_libraries(kscreen-doctor Qt::DBus KF5::Screen KF5::ScreenDpms) install(TARGETS kscreen-doctor ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES kscreen-doctor.zsh RENAME _kscreen-doctor DESTINATION ${KDE_INSTALL_ZSHAUTOCOMPLETEDIR}) 070701000000C0000081A40000000000000000000000016638555E0000533E000000000000000000000000000000000000004400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor/doctor.cpp/* * SPDX-FileCopyrightText: 2014-2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "doctor.h" #include "mode.h" #include <dpms.h> #include <QCollator> #include <QCoreApplication> #include <QDateTime> #include <QFile> #include <QGuiApplication> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QLoggingCategory> #include <QRect> #include <QScreen> #include <QStandardPaths> #include <utility> #include "../backendmanager_p.h" #include "../config.h" #include "../configoperation.h" #include "../getconfigoperation.h" #include "../log.h" #include "../output.h" #include "../setconfigoperation.h" Q_LOGGING_CATEGORY(KSCREEN_DOCTOR, "kscreen.doctor") static QTextStream cout(stdout); static QTextStream cerr(stderr); const static QString green = QStringLiteral("\033[01;32m"); const static QString red = QStringLiteral("\033[01;31m"); const static QString yellow = QStringLiteral("\033[01;33m"); const static QString blue = QStringLiteral("\033[01;34m"); const static QString bold = QStringLiteral("\033[01;39m"); const static QString cr = QStringLiteral("\033[0;0m"); namespace KScreen { namespace ConfigSerializer { // Exported private symbol in configserializer_p.h in KScreen extern QJsonObject serializeConfig(const KScreen::ConfigPtr &config); } } using namespace KScreen; Doctor::Doctor(QObject *parent) : QObject(parent) , m_config(nullptr) , m_changed(false) , m_dpmsClient(nullptr) { } Doctor::~Doctor() { } void Doctor::start(QCommandLineParser *parser) { m_parser = parser; if (m_parser->isSet(QStringLiteral("info"))) { showBackends(); } if (parser->isSet(QStringLiteral("json")) || parser->isSet(QStringLiteral("outputs")) || !m_outputArgs.isEmpty()) { KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation(); connect(op, &KScreen::GetConfigOperation::finished, this, [this](KScreen::ConfigOperation *op) { configReceived(op); }); return; } if (m_parser->isSet(QStringLiteral("dpms"))) { if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"))) { cerr << "DPMS is only supported on Wayland." << Qt::endl; // We need to kick the event loop, otherwise .quit() hangs QTimer::singleShot(0, qApp->quit); return; } m_dpmsClient = new Dpms(this); auto screens = qGuiApp->screens(); if (m_parser->isSet(QStringLiteral("dpms-excluded"))) { const auto excludedConnectors = m_parser->values(QStringLiteral("dpms-excluded")); auto it = std::remove_if(screens.begin(), screens.end(), [&excludedConnectors](QScreen *screen) { return excludedConnectors.contains(screen->name()); }); screens.erase(it, screens.end()); } connect(m_dpmsClient, &Dpms::hasPendingChangesChanged, qGuiApp, [](bool hasChanges) { if (!hasChanges) { // We need to hit the event loop, otherwise .quit() hangs QTimer::singleShot(0, qApp->quit); } }); const QString dpmsArg = m_parser->value(QStringLiteral("dpms")); if (dpmsArg == QLatin1String("show")) { } else { auto performSwitch = [this, dpmsArg, screens](bool supported) { if (!supported) { cerr << "DPMS not supported in this system"; qGuiApp->quit(); return; } if (dpmsArg == QLatin1String("off")) { m_dpmsClient->switchMode(KScreen::Dpms::Off, screens); } else if (dpmsArg == QLatin1String("on")) { m_dpmsClient->switchMode(KScreen::Dpms::On, screens); } else { cerr << "--dpms argument not understood (" << dpmsArg << ")"; } }; if (m_dpmsClient->isSupported()) { performSwitch(m_dpmsClient->isSupported()); } else { connect(m_dpmsClient, &Dpms::supportedChanged, this, performSwitch); } } return; } if (m_parser->isSet(QStringLiteral("log"))) { const QString logmsg = m_parser->value(QStringLiteral("log")); if (!Log::instance()->enabled()) { qCWarning(KSCREEN_DOCTOR) << "Logging is disabled, unset KSCREEN_LOGGING in your environment."; } else { Log::log(logmsg); } } // We need to kick the event loop, otherwise .quit() hangs QTimer::singleShot(0, qApp->quit); } void Doctor::showBackends() const { cout << "Environment: " << Qt::endl; auto env_kscreen_backend = qEnvironmentVariable("KSCREEN_BACKEND", QStringLiteral("[not set]")); cout << " * KSCREEN_BACKEND : " << env_kscreen_backend << Qt::endl; auto env_kscreen_backend_inprocess = qEnvironmentVariable("KSCREEN_BACKEND_INPROCESS", QStringLiteral("[not set]")); cout << " * KSCREEN_BACKEND_INPROCESS : " << env_kscreen_backend_inprocess << Qt::endl; auto env_kscreen_logging = qEnvironmentVariable("KSCREEN_LOGGING", QStringLiteral("[not set]")); cout << " * KSCREEN_LOGGING : " << env_kscreen_logging << Qt::endl; cout << "Logging to : " << (Log::instance()->enabled() ? Log::instance()->logFile() : QStringLiteral("[logging disabled]")) << Qt::endl; const auto backends = BackendManager::instance()->listBackends(); auto preferred = BackendManager::instance()->preferredBackend(); cout << "Preferred KScreen backend : " << green << preferred.fileName() << cr << Qt::endl; cout << "Available KScreen backends:" << Qt::endl; for (const QFileInfo &f : backends) { auto c = blue; if (preferred == f) { c = green; } cout << " * " << c << f.fileName() << cr << ": " << f.absoluteFilePath() << Qt::endl; } cout << Qt::endl; } void Doctor::setOptionList(const QStringList &outputArgs) { m_outputArgs = outputArgs; } OutputPtr Doctor::findOutput(const QString &query) { // try as an output name or ID for (const auto &output : m_config->outputs()) { if (output->name() == query) { return output; } } bool ok; int id = query.toInt(&ok); if (!ok) { cerr << "Output with name " << query << " not found." << Qt::endl; return OutputPtr(); } if (m_config->outputs().contains(id)) { return m_config->outputs()[id]; } else { cerr << "Output with id " << id << " not found." << Qt::endl; return OutputPtr(); } } void Doctor::parseOutputArgs() { // qCDebug(KSCREEN_DOCTOR) << "POSARGS" << m_positionalArgs; for (const QString &op : std::as_const(m_outputArgs)) { auto ops = op.split(QLatin1Char('.')); if (ops.count() > 2) { bool ok; if (ops[0] == QLatin1String("output")) { OutputPtr output = findOutput(ops[1]); if (!output) { qApp->exit(3); return; } int output_id = output->id(); const QString subcmd = ops.length() > 2 ? ops[2] : QString(); if (ops.count() == 3 && subcmd == QLatin1String("primary")) { setPrimary(output); } else if (ops.count() == 4 && subcmd == QLatin1String("priority")) { uint32_t priority = ops[3].toUInt(&ok); if (!ok || priority > 100) { qCWarning(KSCREEN_DOCTOR) << "Wrong input: allowed values for priority are from 1 to 100"; qApp->exit(5); return; } setPriority(output, priority); } else if (ops.count() == 3 && subcmd == QLatin1String("enable")) { setEnabled(output, true); } else if (ops.count() == 3 && subcmd == QLatin1String("disable")) { setEnabled(output, false); } else if (ops.count() == 4 && subcmd == QLatin1String("mode")) { QString mode_id = ops[3]; // set mode if (!setMode(output, mode_id)) { qApp->exit(9); return; } qCDebug(KSCREEN_DOCTOR) << "Output" << output_id << "set mode" << mode_id; } else if (ops.count() == 4 && subcmd == QLatin1String("position")) { QStringList _pos = ops[3].split(QLatin1Char(',')); if (_pos.count() != 2) { qCWarning(KSCREEN_DOCTOR) << "Invalid position:" << ops[3]; qApp->exit(5); return; } int x = _pos[0].toInt(&ok); int y = _pos[1].toInt(&ok); if (!ok) { cerr << "Unable to parse position: " << ops[3] << Qt::endl; qApp->exit(5); return; } QPoint p(x, y); qCDebug(KSCREEN_DOCTOR) << "Output position" << p; setPosition(output, p); } else if ((ops.count() == 4 || ops.count() == 5) && subcmd == QLatin1String("scale")) { // be lenient about . vs. comma as separator qreal scale = ops[3].replace(QLatin1Char(','), QLatin1Char('.')).toDouble(&ok); if (ops.count() == 5) { const QString dbl = ops[3] + QStringLiteral(".") + ops[4]; scale = dbl.toDouble(&ok); }; // set scale if (!ok || qFuzzyCompare(scale, 0.0)) { qCDebug(KSCREEN_DOCTOR) << "Could not set scale " << scale << " to output " << output_id; qApp->exit(9); return; } setScale(output, scale); } else if ((ops.count() == 4) && (subcmd == QLatin1String("orientation") || subcmd == QStringLiteral("rotation"))) { const QString _rotation = ops[3].toLower(); bool ok = false; const QHash<QString, KScreen::Output::Rotation> rotationMap({{QStringLiteral("none"), KScreen::Output::None}, {QStringLiteral("normal"), KScreen::Output::None}, {QStringLiteral("left"), KScreen::Output::Left}, {QStringLiteral("right"), KScreen::Output::Right}, {QStringLiteral("inverted"), KScreen::Output::Inverted}}); KScreen::Output::Rotation rot = KScreen::Output::None; // set orientation if (rotationMap.contains(_rotation)) { ok = true; rot = rotationMap[_rotation]; } if (!ok) { qCDebug(KSCREEN_DOCTOR) << "Could not set orientation " << _rotation << " to output " << output_id; qApp->exit(9); return; } setRotation(output, rot); } else if (ops.count() == 4 && subcmd == QLatin1String("overscan")) { const uint32_t overscan = ops[3].toInt(); if (overscan > 100) { qCWarning(KSCREEN_DOCTOR) << "Wrong input: allowed values for overscan are from 0 to 100"; qApp->exit(9); return; } setOverscan(output, overscan); } else if (ops.count() == 4 && subcmd == QLatin1String("vrrpolicy")) { const QString _policy = ops[3].toLower(); KScreen::Output::VrrPolicy policy; if (_policy == QStringLiteral("never")) { policy = KScreen::Output::VrrPolicy::Never; } else if (_policy == QStringLiteral("always")) { policy = KScreen::Output::VrrPolicy::Always; } else if (_policy == QStringLiteral("automatic")) { policy = KScreen::Output::VrrPolicy::Automatic; } else { qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values are \"never\", \"always\" and \"automatic\""; qApp->exit(9); return; } setVrrPolicy(output, policy); } else if (ops.count() == 4 && subcmd == QLatin1String("rgbrange")) { const QString _range = ops[3].toLower(); KScreen::Output::RgbRange range; if (_range == QStringLiteral("automatic")) { range = KScreen::Output::RgbRange::Automatic; } else if (_range == QStringLiteral("full")) { range = KScreen::Output::RgbRange::Full; } else if (_range == QStringLiteral("limited")) { range = KScreen::Output::RgbRange::Limited; } else { qCDebug(KSCREEN_DOCTOR) << "Wrong input: Only allowed values for rgbrange are \"automatic\", \"full\" and \"limited\""; qApp->exit(9); return; } setRgbRange(output, range); } else { cerr << "Unable to parse arguments: " << op << Qt::endl; qApp->exit(2); return; } } } } } void Doctor::configReceived(KScreen::ConfigOperation *op) { m_config = op->config(); if (!m_config) { qCWarning(KSCREEN_DOCTOR) << "Invalid config."; return; } if (m_parser->isSet(QStringLiteral("json"))) { showJson(); qApp->quit(); } if (m_parser->isSet(QStringLiteral("outputs"))) { showOutputs(); qApp->quit(); } parseOutputArgs(); if (m_changed) { applyConfig(); m_changed = false; } } void Doctor::showOutputs() const { QHash<KScreen::Output::Type, QString> typeString; typeString[KScreen::Output::Unknown] = QStringLiteral("Unknown"); typeString[KScreen::Output::VGA] = QStringLiteral("VGA"); typeString[KScreen::Output::DVI] = QStringLiteral("DVI"); typeString[KScreen::Output::DVII] = QStringLiteral("DVII"); typeString[KScreen::Output::DVIA] = QStringLiteral("DVIA"); typeString[KScreen::Output::DVID] = QStringLiteral("DVID"); typeString[KScreen::Output::HDMI] = QStringLiteral("HDMI"); typeString[KScreen::Output::Panel] = QStringLiteral("Panel"); typeString[KScreen::Output::TV] = QStringLiteral("TV"); typeString[KScreen::Output::TVComposite] = QStringLiteral("TVComposite"); typeString[KScreen::Output::TVSVideo] = QStringLiteral("TVSVideo"); typeString[KScreen::Output::TVComponent] = QStringLiteral("TVComponent"); typeString[KScreen::Output::TVSCART] = QStringLiteral("TVSCART"); typeString[KScreen::Output::TVC4] = QStringLiteral("TVC4"); typeString[KScreen::Output::DisplayPort] = QStringLiteral("DisplayPort"); QCollator collator; collator.setNumericMode(true); for (const auto &output : m_config->outputs()) { cout << green << "Output: " << cr << output->id() << " " << output->name(); cout << " " << (output->isEnabled() ? green + QStringLiteral("enabled") : red + QStringLiteral("disabled")) << cr; cout << " " << (output->isConnected() ? green + QStringLiteral("connected") : red + QStringLiteral("disconnected")) << cr; cout << " " << (output->isEnabled() ? green : red) + QStringLiteral("priority ") << output->priority() << cr; auto _type = typeString[output->type()]; cout << " " << yellow << (_type.isEmpty() ? QStringLiteral("UnmappedOutputType") : _type); cout << blue << " Modes: " << cr; const auto modes = output->modes(); auto modeKeys = modes.keys(); std::sort(modeKeys.begin(), modeKeys.end(), collator); for (const auto &key : modeKeys) { auto mode = *modes.find(key); auto name = QStringLiteral("%1x%2@%3") .arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); if (mode == output->currentMode()) { name = green + name + QLatin1Char('*') + cr; } if (mode == output->preferredMode()) { name = name + QLatin1Char('!'); } cout << mode->id() << ":" << name << " "; } const auto g = output->geometry(); cout << yellow << "Geometry: " << cr << g.x() << "," << g.y() << " " << g.width() << "x" << g.height() << " "; cout << yellow << "Scale: " << cr << output->scale() << " "; cout << yellow << "Rotation: " << cr << output->rotation() << " "; cout << yellow << "Overscan: " << cr << output->overscan() << " "; cout << yellow << "Vrr: "; if (output->capabilities() & Output::Capability::Vrr) { switch (output->vrrPolicy()) { case Output::VrrPolicy::Never: cout << cr << "Never "; break; case Output::VrrPolicy::Automatic: cout << cr << "Automatic "; break; case Output::VrrPolicy::Always: cout << cr << "Always "; } } else { cout << cr << "incapable "; } cout << yellow << "RgbRange: "; if (output->capabilities() & Output::Capability::RgbRange) { switch (output->rgbRange()) { case Output::RgbRange::Automatic: cout << cr << "Automatic"; break; case Output::RgbRange::Full: cout << cr << "Full"; break; case Output::RgbRange::Limited: cout << cr << "Limited"; } } else { cout << cr << "unknown"; } cout << cr << Qt::endl; } } void Doctor::showJson() const { QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); cout << doc.toJson(QJsonDocument::Indented); } void Doctor::setEnabled(OutputPtr output, bool enable) { cout << (enable ? "Enabling " : "Disabling ") << "output " << output->id() << Qt::endl; output->setEnabled(enable); m_changed = true; } void Doctor::setPosition(OutputPtr output, const QPoint &pos) { qCDebug(KSCREEN_DOCTOR) << "Set output position" << pos; output->setPos(pos); m_changed = true; } KScreen::ModePtr Doctor::findMode(OutputPtr output, const QString &query) { for (const KScreen::ModePtr &mode : output->modes()) { auto name = QStringLiteral("%1x%2@%3") .arg(QString::number(mode->size().width()), QString::number(mode->size().height()), QString::number(qRound(mode->refreshRate()))); if (mode->id() == query || name == query) { qCDebug(KSCREEN_DOCTOR) << "Taddaaa! Found mode" << mode->id() << name; return mode; } } cout << "Output mode " << query << " not found." << Qt::endl; return ModePtr(); } bool Doctor::setMode(OutputPtr output, const QString &query) { // find mode const KScreen::ModePtr mode = findMode(output, query); if (!mode) { return false; } output->setCurrentModeId(mode->id()); m_changed = true; return true; } void Doctor::setScale(OutputPtr output, qreal scale) { output->setScale(scale); m_changed = true; } void Doctor::setRotation(OutputPtr output, KScreen::Output::Rotation rot) { output->setRotation(rot); m_changed = true; } void Doctor::setOverscan(OutputPtr output, uint32_t overscan) { output->setOverscan(overscan); m_changed = true; } void Doctor::setVrrPolicy(OutputPtr output, KScreen::Output::VrrPolicy policy) { output->setVrrPolicy(policy); m_changed = true; } void Doctor::setRgbRange(OutputPtr output, KScreen::Output::RgbRange rgbRange) { output->setRgbRange(rgbRange); m_changed = true; } void KScreen::Doctor::setPrimary(OutputPtr output) { setPriority(output, 1); } void KScreen::Doctor::setPriority(OutputPtr output, uint32_t priority) { m_config->setOutputPriority(output, priority); m_changed = true; } void Doctor::applyConfig() { if (!m_changed) { return; } auto setop = new SetConfigOperation(m_config, this); setop->exec(); qCDebug(KSCREEN_DOCTOR) << "setop exec returned" << m_config; qApp->exit(0); } 070701000000C1000081A40000000000000000000000016638555E00000749000000000000000000000000000000000000004200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor/doctor.h/* * SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_DOCTOR_H #define KSCREEN_DOCTOR_H #include <QCommandLineParser> #include <QObject> #include "output.h" #include "types.h" namespace KScreen { class ConfigOperation; class Dpms; class Doctor : public QObject { Q_OBJECT public: explicit Doctor(QObject *parent = nullptr); ~Doctor() override; void setOptionList(const QStringList &positionalArgs); void start(QCommandLineParser *m_parser); void configReceived(KScreen::ConfigOperation *op); OutputPtr findOutput(const QString &query); KScreen::ModePtr findMode(OutputPtr output, const QString &query); void showBackends() const; void showOutputs() const; void showJson() const; void setEnabled(OutputPtr output, bool enable = true); void setPosition(OutputPtr output, const QPoint &pos); bool setMode(OutputPtr output, const QString &query); void setScale(OutputPtr output, qreal scale); void setRotation(OutputPtr output, KScreen::Output::Rotation rot); void setOverscan(OutputPtr output, uint32_t overscan); void setVrrPolicy(OutputPtr output, KScreen::Output::VrrPolicy policy); void setRgbRange(OutputPtr output, KScreen::Output::RgbRange rgbRange); void setPrimary(OutputPtr output); void setPriority(OutputPtr output, uint32_t priority); Q_SIGNALS: void outputsChanged(); void started(); void configChanged(); private: // static QString modeString(KWayland::Server::OutputDeviceInterface* outputdevice, int mid); void applyConfig(); void parseOutputArgs(); KScreen::ConfigPtr m_config; QCommandLineParser *m_parser; bool m_changed; QStringList m_outputArgs; Dpms *m_dpmsClient; }; } // namespace #endif // KSCREEN_WAYLAND_SCREEN_H 070701000000C2000081A40000000000000000000000016638555E0000242A000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor/kscreen-doctor.zsh#compdef kscreen-doctor # SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk> # # SPDX-License-Identifier: GPL-2.0-or-later local curcontext="$curcontext" state expl ret=1 _kscreen-doctor-check-jq() { local tag=$1 descr=$2 if (( $+commands[jq] )); then return 0 else local -a empty # Is there a better way to print description? _describe -t "$tag" "$descr (completions unavailable, please install jq)" empty return 1 fi } _kscreen-doctor-outputs() { local -a outputs # array of triples: id, name, enabled status (true/false) _kscreen-doctor-check-jq outputs output || return 1 data=(${(f)"$( kscreen-doctor --json | jq --raw-output ' .outputs | map(select(.connected)) | sort_by((.enabled | not), .name) | map(.id, .name, .enabled) | .[] ' )"}) local id name enabled desc for id name enabled in $data ; do if [[ "$enabled" == true ]]; then enabled="[Enabled] " # a bit of right padding, like git does for recent commits list else enabled="[Disabled]" fi desc="$enabled Output ID $id, connected as $name" # Duplicate completions for id and name. But given identical description, # they will occupy the same row. outputs+=( "$id:$desc" "$name:$desc" ) done _describe -t outputs "output" outputs "$@" -V unsorted } _kscreen-mode-fmt() { local dst="$1" mode="$2" local size refresh_rate size=${mode%@*} refresh_rate=${mode#*@} # WWWWxHHHH @ RRR, (l:n:) is padding with spaces on the left printf -v $dst " ${(l:9:)size} @ ${(l:3:)refresh_rate} " } _kscreen-doctor-mode() { local ret=1 output="$1" _kscreen-doctor-check-jq modes mode || return $ret # 'top_comp' is a list of current and preferred mode IDs (literal completions). # 'top_descr' is a list of pretty-printed version of top_comp and their descriptions. # 'rest_comp' is a list of mode IDs, and 'rest_fmt' is pretty-printed version of rest (without descriptions). local -a stdout top_comp top_descr rest_comp rest_fmt stdout=(${(f)"$( kscreen-doctor --json | jq --raw-output --arg output "$output" ' .outputs[] # note about "\(.id)": stringifying known-to-be integer sounds safer # than parsing untrusted input as an int. | select("\(.id)" == $output or .name == $output) | [ .preferredModes as $pref | .currentModeId as $curr | .modes | map({ id: .id, w: .size.width, h: .size.height, r: (.refreshRate | round), }) # Some names may be duplicated after rounding refresh rates. # Group them by what going to be part of a name, # while keeping a list of IDs to be able to determine which ones are "current" and/or "preferred". | group_by(.w, .h, .r) | map({ # flatten back p: any( # this is how you do a nested loop .id as $id | $pref[] as $p | $id == $p ), c: any(.id == $curr), # Just take the first mode`s data. They are identical, and there will always be at least one. w: .[0].w, h: .[0].h, r: .[0].r }) | sort_by(.w, .h, .r) | reverse | map({ name: "\(.w)x\(.h)@\(.r)", p: .p, c: .c }) # show current mode on top, then preferred, then the rest. # Second line is a flag that indicates whether current mode is also the preferred one. | map(select(.c) | "\(.name);\(.p)")[], "--", map(select((.c | not) and .p) | .name)[], "++", map(select((.c | not) and (.p | not)) | .name)[] ][] ' )"}) # sample result: # 4096x2160@60;false # -- # 1920x1080@60 # ++ # 4096x2160@50 # 4096x2160@30 # 3840x2160@60 # 3840x2160@50 # 3840x2160@30 # 1920x1080@50 # 1920x1080@30 # 1920x1080@25 # ... # The 'false' on a first line indicates that current is not also a # preferred one. If it were, it would not appear in the list of preferred # modes below. local current label formatted line parser=current # then "preferred" and "rest" for line in $stdout ; do case $line in --) parser=preferred continue ;; ++) parser=rest continue esac case $parser in current) current=${line%;*} _kscreen-mode-fmt formatted "$current" label="Current" # current is also preferred if [[ "${line#*;}" != "false" ]]; then label="$label & Preferred" fi top_comp+=( "$current" ) top_descr+=( "${formatted}:${label} mode" ) ;; preferred) _kscreen-mode-fmt formatted "$line" top_comp+=( "$line" ) top_descr+=( "${formatted}:Preferred mode" ) ;; rest) _kscreen-mode-fmt formatted "$line" rest_comp+=( $line ) rest_fmt+=( $formatted ) esac done ret=1 # Resetting expl to avoid it 'leaking' from one line to the next. expl=() _describe -V -t notable-modes 'notable modes' top_descr top_comp && ret=0 expl=() _wanted all-modes expl 'other available modes' \ compadd -o nosort -d rest_fmt -a rest_comp \ && ret=0 return $ret } _kscreen-doctor-priorities() { local connected_count if (( $+commands[jq] )); then connected_count="$( kscreen-doctor --json | jq --raw-output ' .outputs | map(select(.connected)) | length ')" else # best effort fallback connected_count="$(kscreen-doctor --outputs | wc -l)" fi _alternative "priority::( {0..${connected_count}} )" } _arguments -C \ '(-h --help)'{-h,--help}'[Displays help on commandline options]' \ '--help-all[Displays help including Qt specific options]' \ '(-i --info)'{-i,--info}'[Show runtime information: backends, logging, etc]' \ '(-j --json)'{-j,--json}'[Show configuration in JSON format]' \ '(-o --outputs)'{-o,--outputs}'[Show outputs]' \ '(DPMS)'{-d=,--dpms=}'[(Wayland only) Display power management]:status:(on off)' \ '--dpms-excluded=[Do not apply the dpms change to the output with said model names]:output:_kscreen-doctor-outputs' \ '(-l --log)'{-l=,--log=}'[Write a comment to the log file]:comment' \ '*: :->settings' && ret=0 case $state in settings) if compset -P 'output.' ; then if compset -P 1 '*.' ; then local output; output="${${IPREFIX#*.}%.}" if compset -P 1 'mode.' ; then _kscreen-doctor-mode "$output" && ret=0 elif compset -P 1 'priority.' ; then _kscreen-doctor-priorities && ret=0 elif compset -P 1 'position.' ; then _arguments '1::x,y:' && ret=0 elif compset -P 1 'rgbrange.' ; then _alternative 'rgbrange::(automatic full limited)' && ret=0 elif compset -P 1 'rotation.' || compset -P 1 'orientation.' ; then _alternative 'rotation::(none normal left right inverted)' && ret=0 elif compset -P 1 'overscan.' ; then local -a overscan_descr overscan_comp overscan_descr=( ' 0%:Disable overscan (Default)' ' 3%:Action safe area (Vertical, round down)' ' 4%:Action safe area (Vertical, round up)' '10%:Action safe area (Horizontal, 14:9 displayed on 16:9)' '15%:Action safe area (Horizontal, 4:3 displayed on 16:9)' '17%:Title safe area (Horizontal, 4:3 displayed on 16:9)' ) overscan_comp=('0' '3' '4' '10' '15' '17') _describe -t overscan "(Wayland only) output overscan, 0%%..100%%" \ overscan_descr overscan_comp -o nosort && ret=0 elif compset -P 1 'scale.' ; then local -a scale_descr scale_comp scale_descr=('100%' '125%' '150%' '175%' '200%' '225%' '250%' '275%' '300%' ) scale_comp=( '1' '1,25' '1,5' '1,75' '2' '2,25' '2,5' '2,75' '3' ) _describe -t scale "(Wayland only) per-output scaling" scale_descr scale_comp -o nosort && ret=0 elif compset -P 1 'vrrpolicy.' ; then _alternative 'vrrpolicy::(never always automatic)' && ret=0 else # two groups: first without suffix, and second with '.' at the end _describe -t subcommands 'subcommand' '( enable:"Toggle output" disable:"Toggle output" primary:"Make this output primary (same as priority.1)" )' -- '( mode:"Resolution and refresh rate" orientation:"Display orientation" overscan:"(Wayland only) Overscan area size" position:"x and y coordinates" priority:"Set output priority" rgbrange:"RGB range" rotation:"Display orientation" scale:"(Wayland only) Per-output scaling" vrrpolicy:"(Wayland only) Variable refresh rate" )' -S '.' && ret=0 fi else _kscreen-doctor-outputs -S '.' && ret=0 fi else _sep_parts '(output)' . && ret=0 fi ;; esac return $ret 070701000000C3000081A40000000000000000000000016638555E00001393000000000000000000000000000000000000004200000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/doctor/main.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "doctor.h" #include <QCommandLineParser> #include <QGuiApplication> #include <QDebug> /** Usage example: * kscreen-doctor --set output.0.disable output.1.mode.1 output.1.enable" * * Error codes: * 2 : general parse error * 3 : output id parse error * 4 : mode id parse error * 5 : position parse error * * 8 : invalid output id * 9 : invalid mode id * */ int main(int argc, char **argv) { const QString desc = QStringLiteral( "kscreen-doctor allows to change the screen setup from the command-line.\n" "\n" "Setting the output configuration is done in an atomic fashion, all settings\n" "are applied in a single command.\n" "kscreen-doctor can be used to enable and disable outputs, to position screens,\n" "change resolution (mode setting), etc.. You should put all your options into \n" "a single invocation of kscreen-doctor, so they can all be applied at once.\n" "\n" "Usage examples:\n\n" " Show output information:\n" " $ kscreen-doctor -o\n" " Output: 1 eDP-1 enabled connected Panel Modes: Modes: 1:800x600@60 [...] Geometry: 0,0 1280x800\n" " Output: 70 HDMI-2 enabled connected HDMI Modes: 1:800x600@60 [...] Geometry: 1280,0 1920x1080\n" "\n Disable the hdmi output, enable the laptop panel and set it to a specific mode\n" " $ kscreen-doctor output.HDMI-2.disable output.eDP-1.mode.1 output.eDP-1.enable\n" "\n Position the hdmi monitor on the right of the laptop panel\n" " $ kscreen-doctor output.HDMI-2.position.1280,0 output.eDP-1.position.0,0\n" "\n Set resolution mode\n" " $ kscreen-doctor output.HDMI-2.mode.1920x1080@60 \n" "\n Set scale (note: fractional scaling is only supported on wayland)\n" " $ kscreen-doctor output.HDMI-2.scale.2 \n" "\n Set rotation (possible values: none, left, right, inverted)\n" " $ kscreen-doctor output.HDMI-2.rotation.left \n"); /* "\nError codes:\n" " 2 : general parse error\n" " 3 : output id parse error\n" " 4 : mode id parse error\n" " 5 : position parse error\n" " 8 : invalid output id\n" " 9 : invalid mode id\n"; */ const QString syntax = QStringLiteral( "Specific output settings are separated by spaces, each setting is in the form of\n" "output.<name>.<setting>[.<value>]\n" "For example:\n" "$ kscreen-doctor output.HDMI-2.enable \\ \n" " output.eDP-1.mode.4 \\ \n" " output.eDP-1.position.1280,0\n" "Multiple settings are passed in order to have kscreen-doctor apply these settings in one go.\n"); QGuiApplication app(argc, argv); KScreen::Doctor server; QCommandLineOption info = QCommandLineOption(QStringList() << QStringLiteral("i") << QStringLiteral("info"), QStringLiteral("Show runtime information: backends, logging, etc.")); QCommandLineOption outputs = QCommandLineOption(QStringList() << QStringLiteral("o") << QStringLiteral("outputs"), QStringLiteral("Show outputs")); QCommandLineOption json = QCommandLineOption(QStringList() << QStringLiteral("j") << QStringLiteral("json"), QStringLiteral("Show configuration in JSON format")); QCommandLineOption dpms = QCommandLineOption(QStringList() << QStringLiteral("d") << QStringLiteral("dpms"), QStringLiteral("Display power management (wayland only)"), QStringLiteral("off")); QCommandLineOption dpmsExcluded = QCommandLineOption({QStringLiteral("dpms-excluded")}, QStringLiteral("Do not apply the dpms change to the output with said model names"), QStringLiteral("connector")); QCommandLineOption log = QCommandLineOption(QStringList() << QStringLiteral("l") << QStringLiteral("log"), QStringLiteral("Write a comment to the log file"), QStringLiteral("comment")); QCommandLineParser parser; parser.setApplicationDescription(desc); parser.addPositionalArgument(QStringLiteral("config"), syntax, QStringLiteral("[output.<name>.<setting> output.<name>.setting [...]]")); parser.addHelpOption(); parser.addOption(info); parser.addOption(json); parser.addOption(outputs); parser.addOption(dpms); parser.addOption(log); parser.addOption(dpmsExcluded); parser.process(app); if (!parser.positionalArguments().isEmpty()) { server.setOptionList(parser.positionalArguments()); } server.start(&parser); return app.exec(); } 070701000000C4000081A40000000000000000000000016638555E00002865000000000000000000000000000000000000003B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/edid.cpp/* * SPDX-FileCopyrightText: 2012 Daniel Nicoletti <dantti12@gmail.com> * SPDX-FileCopyrightText: 2012-2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "edid.h" #include "kscreen_debug_edid.h" #include <math.h> #include <QCryptographicHash> #include <QFile> #include <QStringBuilder> #include <QStringList> #define GCM_EDID_OFFSET_PNPID 0x08 #define GCM_EDID_OFFSET_SERIAL 0x0c #define GCM_EDID_OFFSET_SIZE 0x15 #define GCM_EDID_OFFSET_GAMMA 0x17 #define GCM_EDID_OFFSET_DATA_BLOCKS 0x36 #define GCM_EDID_OFFSET_LAST_BLOCK 0x6c #define GCM_EDID_OFFSET_EXTENSION_BLOCK_COUNT 0x7e #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc #define GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff #define GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA 0xf9 #define GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe #define GCM_DESCRIPTOR_COLOR_POINT 0xfb #define PNP_IDS "/usr/share/hwdata/pnp.ids" using namespace KScreen; class Q_DECL_HIDDEN Edid::Private { public: Private() : valid(false) , width(0) , height(0) , gamma(0) { } Private(const Private &other) : valid(other.valid) , monitorName(other.monitorName) , vendorName(other.vendorName) , serialNumber(other.serialNumber) , eisaId(other.eisaId) , checksum(other.checksum) , pnpId(other.pnpId) , width(other.width) , height(other.height) , gamma(other.gamma) , red(other.red) , green(other.green) , blue(other.blue) , white(other.white) { } bool parse(const QByteArray &data); int edidGetBit(int in, int bit) const; int edidGetBits(int in, int begin, int end) const; float edidDecodeFraction(int high, int low) const; QString edidParseString(const quint8 *data) const; bool valid; QString monitorName; QString vendorName; QString serialNumber; QString eisaId; QString checksum; QString pnpId; uint width; uint height; qreal gamma; QQuaternion red; QQuaternion green; QQuaternion blue; QQuaternion white; }; Edid::Edid() : QObject() , d(new Private()) { } Edid::Edid(const QByteArray &data, QObject *parent) : QObject(parent) , d(new Private()) { d->parse(data); } Edid::Edid(Edid::Private *dd) : QObject() , d(dd) { } Edid::~Edid() { delete d; } Edid *Edid::clone() const { return new Edid(new Private(*d)); } bool Edid::isValid() const { return d->valid; } QString Edid::deviceId(const QString &fallbackName) const { QString id = QStringLiteral("xrandr"); // if no info was added check if the fallbacName is provided if (vendor().isNull() && name().isNull() && serial().isNull()) { if (!fallbackName.isEmpty()) { id.append(QLatin1Char('-') % fallbackName); } else { // all info we have are empty strings id.append(QLatin1String("-unknown")); } } else if (d->valid) { if (!vendor().isNull()) { id.append(QLatin1Char('-') % vendor()); } if (!name().isNull()) { id.append(QLatin1Char('-') % name()); } if (!serial().isNull()) { id.append(QLatin1Char('-') % serial()); } } return id; } QString Edid::name() const { if (d->valid) { return d->monitorName; } return QString(); } QString Edid::vendor() const { if (d->valid) { return d->vendorName; } return QString(); } QString Edid::serial() const { if (d->valid) { return d->serialNumber; } return QString(); } QString Edid::eisaId() const { if (d->valid) { return d->eisaId; } return QString(); } QString Edid::hash() const { if (d->valid) { return d->checksum; } return QString(); } QString Edid::pnpId() const { if (d->valid) { return d->pnpId; } return QString(); } uint Edid::width() const { return d->width; } uint Edid::height() const { return d->height; } qreal Edid::gamma() const { return d->gamma; } QQuaternion Edid::red() const { return d->red; } QQuaternion Edid::green() const { return d->green; } QQuaternion Edid::blue() const { return d->blue; } QQuaternion Edid::white() const { return d->white; } bool Edid::Private::parse(const QByteArray &rawData) { quint32 serial; const quint8 *data = reinterpret_cast<const quint8 *>(rawData.constData()); int length = rawData.length(); /* check header */ if (length < 128) { if (length > 0) { qCWarning(KSCREEN_EDID) << "Invalid EDID length (" << length << " bytes)"; } valid = false; return valid; } if (data[0] != 0x00 || data[1] != 0xff) { qCWarning(KSCREEN_EDID) << "Failed to parse EDID header"; valid = false; return valid; } /* decode the PNP ID from three 5 bit words packed into 2 bytes * /--08--\/--09--\ * 7654321076543210 * |\---/\---/\---/ * R C1 C2 C3 */ pnpId.resize(3); pnpId[0] = QLatin1Char('A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1); pnpId[1] = QLatin1Char('A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[GCM_EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1); pnpId[2] = QLatin1Char('A' + (data[GCM_EDID_OFFSET_PNPID + 1] & 0x1f) - 1); // load the PNP_IDS file and load the vendor name QFile pnpIds(QStringLiteral(PNP_IDS)); if (pnpIds.open(QIODevice::ReadOnly)) { while (!pnpIds.atEnd()) { QString line = QString::fromUtf8(pnpIds.readLine()); if (line.startsWith(pnpId)) { const QStringList parts = line.split(QLatin1Char('\t')); if (parts.size() == 2) { vendorName = parts.at(1).simplified(); } break; } } } /* maybe there isn't a ASCII serial number descriptor, so use this instead */ serial = static_cast<quint32>(data[GCM_EDID_OFFSET_SERIAL + 0]); serial += static_cast<quint32>(data[GCM_EDID_OFFSET_SERIAL + 1] * 0x100); serial += static_cast<quint32>(data[GCM_EDID_OFFSET_SERIAL + 2] * 0x10000); serial += static_cast<quint32>(data[GCM_EDID_OFFSET_SERIAL + 3] * 0x1000000); if (serial > 0) { serialNumber = QString::number(serial); } /* get the size */ width = data[GCM_EDID_OFFSET_SIZE + 0]; height = data[GCM_EDID_OFFSET_SIZE + 1]; /* we don't care about aspect */ if (width == 0 || height == 0) { width = 0; height = 0; } /* get gamma */ if (data[GCM_EDID_OFFSET_GAMMA] == 0xff) { gamma = 1.0; } else { gamma = data[GCM_EDID_OFFSET_GAMMA] / 100.0 + 1.0; } /* get color red */ red.setX(edidDecodeFraction(data[0x1b], edidGetBits(data[0x19], 6, 7))); red.setY(edidDecodeFraction(data[0x1c], edidGetBits(data[0x19], 5, 4))); /* get color green */ green.setX(edidDecodeFraction(data[0x1d], edidGetBits(data[0x19], 2, 3))); green.setY(edidDecodeFraction(data[0x1e], edidGetBits(data[0x19], 0, 1))); /* get color blue */ blue.setX(edidDecodeFraction(data[0x1f], edidGetBits(data[0x1a], 6, 7))); blue.setY(edidDecodeFraction(data[0x20], edidGetBits(data[0x1a], 4, 5))); /* get color white */ white.setX(edidDecodeFraction(data[0x21], edidGetBits(data[0x1a], 2, 3))); white.setY(edidDecodeFraction(data[0x22], edidGetBits(data[0x1a], 0, 1))); /* parse EDID data */ for (uint i = GCM_EDID_OFFSET_DATA_BLOCKS; i <= GCM_EDID_OFFSET_LAST_BLOCK; i += 18) { /* ignore pixel clock data */ if (data[i] != 0) { continue; } if (data[i + 2] != 0) { continue; } /* any useful blocks? */ if (data[i + 3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_NAME) { QString tmp = edidParseString(&data[i + 5]); if (!tmp.isEmpty()) { monitorName = tmp; } } else if (data[i + 3] == GCM_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) { QString tmp = edidParseString(&data[i + 5]); if (!tmp.isEmpty()) { serialNumber = tmp; } } else if (data[i + 3] == GCM_DESCRIPTOR_COLOR_MANAGEMENT_DATA) { qCWarning(KSCREEN_EDID) << "failing to parse color management data"; } else if (data[i + 3] == GCM_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) { QString tmp = edidParseString(&data[i + 5]); if (!tmp.isEmpty()) { eisaId = tmp; } } else if (data[i + 3] == GCM_DESCRIPTOR_COLOR_POINT) { if (data[i + 3 + 9] != 0xff) { /* extended EDID block(1) which contains * a better gamma value */ gamma = (data[i + 3 + 9] / 100.0) + 1; } if (data[i + 3 + 14] != 0xff) { /* extended EDID block(2) which contains * a better gamma value */ gamma = (data[i + 3 + 9] / 100.0) + 1; } } } // calculate checksum QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(reinterpret_cast<const char *>(data), length); checksum = QString::fromLatin1(hash.result().toHex()); valid = true; return valid; } int Edid::Private::edidGetBit(int in, int bit) const { return (in & (1 << bit)) >> bit; } int Edid::Private::edidGetBits(int in, int begin, int end) const { int mask = (1 << (end - begin + 1)) - 1; return (in >> begin) & mask; } float Edid::Private::edidDecodeFraction(int high, int low) const { float result = 0.0; high = (high << 2) | low; for (int i = 0; i < 10; ++i) { result += edidGetBit(high, i) * pow(2, i - 10); } return result; } QString Edid::Private::edidParseString(const quint8 *data) const { /* this is always 13 bytes, but we can't guarantee it's null * terminated or not junk. */ auto text = QString::fromLatin1(reinterpret_cast<const char *>(data), 13).simplified(); for (int i = 0; i < text.length(); ++i) { if (!text.at(i).isPrint()) { text[i] = QLatin1Char('-'); } } return text; } 070701000000C5000081A40000000000000000000000016638555E00000756000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/edid.h/* * SPDX-FileCopyrightText: 2012 Daniel Nicoletti <dantti12@gmail.com> * SPDX-FileCopyrightText: 2012, 2013 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_EDID_H #define KSCREEN_EDID_H #include "kscreen_export.h" #include <QObject> #include <QQuaternion> #include <QtGlobal> namespace KScreen { class KSCREEN_EXPORT Edid : public QObject { Q_OBJECT Q_PROPERTY(QString deviceId READ deviceId CONSTANT) Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString vendor READ vendor CONSTANT) Q_PROPERTY(QString serial READ serial CONSTANT) Q_PROPERTY(QString eisaId READ eisaId CONSTANT) Q_PROPERTY(QString hash READ hash CONSTANT) Q_PROPERTY(uint width READ width CONSTANT) Q_PROPERTY(uint height READ height CONSTANT) Q_PROPERTY(qreal gamma READ gamma CONSTANT) Q_PROPERTY(QQuaternion red READ red CONSTANT) Q_PROPERTY(QQuaternion green READ green CONSTANT) Q_PROPERTY(QQuaternion blue READ blue CONSTANT) Q_PROPERTY(QQuaternion white READ white CONSTANT) public: explicit Edid(); explicit Edid(const QByteArray &data, QObject *parent = nullptr); ~Edid() override; Q_REQUIRED_RESULT Edid *clone() const; bool isValid() const; QString deviceId(const QString &fallbackName = QString()) const; QString name() const; QString vendor() const; QString serial() const; QString eisaId() const; QString hash() const; QString pnpId() const; uint width() const; uint height() const; qreal gamma() const; QQuaternion red() const; QQuaternion green() const; QQuaternion blue() const; QQuaternion white() const; private: Q_DISABLE_COPY(Edid) class Private; Private *const d; explicit Edid(Private *dd); }; } Q_DECLARE_METATYPE(KScreen::Edid *) #endif // EDID_H 070701000000C6000081A40000000000000000000000016638555E0000143F000000000000000000000000000000000000004900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/getconfigoperation.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "getconfigoperation.h" #include "backendinterface.h" #include "backendmanager_p.h" #include "config.h" #include "configoperation_p.h" #include "configserializer_p.h" #include "log.h" #include "output.h" using namespace KScreen; namespace KScreen { class GetConfigOperationPrivate : public ConfigOperationPrivate { Q_OBJECT public: GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation *qq); void backendReady(org::kde::kscreen::Backend *backend) override; void onConfigReceived(QDBusPendingCallWatcher *watcher); void onEDIDReceived(QDBusPendingCallWatcher *watcher); public: GetConfigOperation::Options options; ConfigPtr config; // For in-process void loadEdid(KScreen::AbstractBackend *backend); // For out-of-process int pendingEDIDs; QPointer<org::kde::kscreen::Backend> mBackend; private: Q_DECLARE_PUBLIC(GetConfigOperation) }; } GetConfigOperationPrivate::GetConfigOperationPrivate(GetConfigOperation::Options options, GetConfigOperation *qq) : ConfigOperationPrivate(qq) , options(options) { } void GetConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); ConfigOperationPrivate::backendReady(backend); Q_Q(GetConfigOperation); if (!backend) { q->setError(tr("Failed to prepare backend")); q->emitResult(); return; } mBackend = backend; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getConfig(), this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onConfigReceived); } void GetConfigOperationPrivate::onConfigReceived(QDBusPendingCallWatcher *watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_Q(GetConfigOperation); QDBusPendingReply<QVariantMap> reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } config = ConfigSerializer::deserializeConfig(reply.value()); if (!config) { q->setError(tr("Failed to deserialize backend response")); q->emitResult(); return; } if (options & GetConfigOperation::NoEDID || config->outputs().isEmpty()) { q->emitResult(); return; } pendingEDIDs = 0; if (!mBackend) { q->setError(tr("Backend invalidated")); q->emitResult(); return; } const auto outputs = config->outputs(); for (const OutputPtr &output : outputs) { if (!output->isConnected()) { continue; } QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mBackend->getEdid(output->id()), this); watcher->setProperty("outputId", output->id()); connect(watcher, &QDBusPendingCallWatcher::finished, this, &GetConfigOperationPrivate::onEDIDReceived); ++pendingEDIDs; } } void GetConfigOperationPrivate::onEDIDReceived(QDBusPendingCallWatcher *watcher) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::OutOfProcess); Q_Q(GetConfigOperation); QDBusPendingReply<QByteArray> reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } const QByteArray edidData = reply.value(); const int outputId = watcher->property("outputId").toInt(); config->output(outputId)->setEdid(edidData); if (--pendingEDIDs == 0) { q->emitResult(); } } GetConfigOperation::GetConfigOperation(Options options, QObject *parent) : ConfigOperation(new GetConfigOperationPrivate(options, this), parent) { } GetConfigOperation::~GetConfigOperation() { } KScreen::ConfigPtr GetConfigOperation::config() const { Q_D(const GetConfigOperation); return d->config; } void GetConfigOperation::start() { Q_D(GetConfigOperation); if (BackendManager::instance()->method() == BackendManager::InProcess) { auto backend = d->loadBackend(); if (!backend) { return; // loadBackend() already set error and called emitResult() for us } d->config = backend->config()->clone(); d->loadEdid(backend); emitResult(); } else { d->requestBackend(); } } void GetConfigOperationPrivate::loadEdid(KScreen::AbstractBackend *backend) { Q_ASSERT(BackendManager::instance()->method() == BackendManager::InProcess); if (options & KScreen::ConfigOperation::NoEDID) { return; } if (!config) { return; } auto outputs = config->outputs(); for (auto it = outputs.begin(); it != outputs.end(); ++it) { auto output = *it; if (output->edid() == nullptr) { const QByteArray edidData = backend->edid(output->id()); output->setEdid(edidData); } } } #include "getconfigoperation.moc" 070701000000C7000081A40000000000000000000000016638555E000002E5000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/getconfigoperation.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef KSCREEN_GETCONFIGOPERATION_H #define KSCREEN_GETCONFIGOPERATION_H #include "configoperation.h" #include "kscreen_export.h" #include "types.h" namespace KScreen { class GetConfigOperationPrivate; class KSCREEN_EXPORT GetConfigOperation : public KScreen::ConfigOperation { Q_OBJECT public: explicit GetConfigOperation(Options options = NoOptions, QObject *parent = nullptr); ~GetConfigOperation() override; KScreen::ConfigPtr config() const override; protected: void start() override; private: Q_DECLARE_PRIVATE(GetConfigOperation) }; } #endif // KSCREEN_GETCONFIGOPERATION_H 070701000000C8000081A40000000000000000000000016638555E00000120000000000000000000000000000000000000004100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/kscreen2.pc.inprefix=@CMAKE_INSTALL_PREFIX@ libdir=@KDE_INSTALL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/include Name: kscreen2 Description: Qt Based library to manage screens with backends (xrandr, whatevercomesnext) Version: @KSCREEN_VERSION@ Libs: -L${libdir} -lKF5Screen Cflags: -I${includedir} 070701000000C9000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms070701000000CA000081A40000000000000000000000016638555E0000083F000000000000000000000000000000000000004900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/CMakeLists.txt# SPDX-License-Identifier: BSD-3-Clause # SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> add_library(KF5ScreenDpms SHARED) target_sources(KF5ScreenDpms PRIVATE dpms.cpp abstractdpmshelper.cpp waylanddpmshelper.cpp xcbdpmshelper.cpp waylanddpmshelper.cpp) target_link_libraries(KF5ScreenDpms PUBLIC Qt::Gui PRIVATE XCB::XCB XCB::DPMS XCB::RANDR Qt::GuiPrivate Qt::WaylandClient Wayland::Client ) if (QT_MAJOR_VERSION EQUAL "5") target_link_libraries(KF5ScreenDpms PRIVATE Qt::X11Extras) ecm_add_qtwayland_client_protocol(KF5ScreenDpms PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/dpms.xml BASENAME dpms ) else() qt6_generate_wayland_protocol_client_sources(KF5ScreenDpms FILES ${PLASMA_WAYLAND_PROTOCOLS_DIR}/dpms.xml) endif() set_target_properties(KF5ScreenDpms PROPERTIES VERSION "${KSCREEN_VERSION}" SOVERSION "${KSCREEN_SOVERSION}" EXPORT_NAME ScreenDpms ) generate_export_header(KF5ScreenDpms BASE_NAME KScreenDpms) install(TARGETS KF5ScreenDpms EXPORT KF5ScreenTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) add_library(KF5::ScreenDpms ALIAS KF5ScreenDpms) ecm_qt_declare_logging_category(KF5ScreenDpms HEADER kscreendpms_debug.h IDENTIFIER KSCREEN_DPMS CATEGORY_NAME org.kde.kscreen.dpms ) ecm_generate_headers(KScreenDpms_HEADERS HEADER_NAMES Dpms PREFIX KScreenDpms REQUIRED_HEADERS KScreenDpms_REQ_HEADERS ) target_include_directories(KF5ScreenDpms INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KScreen>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}>" ) install(FILES ${KScreenDpms_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KScreen/KScreenDpms COMPONENT Devel) install(FILES ${KScreenDpms_REQ_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kscreendpms_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KScreen/kscreendpms) 070701000000CB000081A40000000000000000000000016638555E00000128000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/abstractdpmshelper.cpp// // SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #include "abstractdpmshelper_p.h" KScreen::AbstractDpmsHelper::~AbstractDpmsHelper() = default; 070701000000CC000081A40000000000000000000000016638555E000005EB000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/abstractdpmshelper_p.h// SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #ifndef ABSTRACTDPMSHELPER_H #define ABSTRACTDPMSHELPER_H #include "dpms.h" #include <QObject> #include <optional> class QScreen; namespace KScreen { class AbstractDpmsHelper : public QObject { Q_OBJECT public: virtual ~AbstractDpmsHelper(); virtual void trigger(Dpms::Mode, const QList<QScreen *> &screens) = 0; bool isSupported() { if (!m_supported.has_value()) { blockUntilSupported(); } Q_ASSERT(m_supported.has_value()); return *m_supported; } void setSupported(bool supported) { if (m_supported != supported) { m_supported = supported; Q_EMIT supportedChanged(supported); } } void setHasPendingChanges(bool hasThem) { if (m_hasPendingChanges != hasThem) { return; } m_hasPendingChanges = hasThem; Q_EMIT hasPendingChangesChanged(hasThem); } bool hasPendingChanges() const { return m_hasPendingChanges; } Q_SIGNALS: void supportedChanged(bool supported); void modeChanged(Dpms::Mode mode, QScreen *screen); void hasPendingChangesChanged(bool pendingChanges); private: virtual void blockUntilSupported() {} std::optional<bool> m_supported; bool m_hasPendingChanges = false; }; } #endif 070701000000CD000081A40000000000000000000000016638555E000005B1000000000000000000000000000000000000004300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/dpms.cpp// SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org // // SPDX-License-Identifier: LGPL-2.1-or-later #include "dpms.h" #include "kscreendpms_debug.h" #include "waylanddpmshelper_p.h" #include "xcbdpmshelper_p.h" #include <QGuiApplication> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #include <private/qtx11extras_p.h> #else #include <QX11Info> #endif KScreen::Dpms::Dpms(QObject *parent) : QObject(parent) { if (QX11Info::isPlatformX11()) { m_helper.reset(new XcbDpmsHelper); } else if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) { m_helper.reset(new WaylandDpmsHelper); } else { qCWarning(KSCREEN_DPMS) << "dpms unsupported on this system"; return; } connect(m_helper.data(), &AbstractDpmsHelper::supportedChanged, this, &Dpms::supportedChanged); connect(m_helper.data(), &AbstractDpmsHelper::modeChanged, this, &Dpms::modeChanged); connect(m_helper.data(), &AbstractDpmsHelper::hasPendingChangesChanged, this, &Dpms::hasPendingChangesChanged); } KScreen::Dpms::~Dpms() { } void KScreen::Dpms::switchMode(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens) { m_helper->trigger(mode, screens.isEmpty() ? qGuiApp->screens() : screens); } bool KScreen::Dpms::isSupported() const { return m_helper->isSupported(); } bool KScreen::Dpms::hasPendingChanges() const { return m_helper->hasPendingChanges(); } 070701000000CE000081A40000000000000000000000016638555E000007F5000000000000000000000000000000000000004100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/dpms.h// SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #ifndef KSCREENDPMS_DPMS_H #define KSCREENDPMS_DPMS_H #include <QScreen> #include "kscreendpms_export.h" namespace KScreen { class AbstractDpmsHelper; /** * @class Dpms, allows controlling the system's Display Power Management Signaling * * Provides an API to switch the system's mode on a per-display basis. * * It has backends for X11 and Wayland. */ class KSCREENDPMS_EXPORT Dpms : public QObject { Q_OBJECT Q_PROPERTY(bool isSupported READ isSupported NOTIFY supportedChanged) Q_PROPERTY(bool hasPendingChanges READ hasPendingChanges NOTIFY hasPendingChangesChanged) public: explicit Dpms(QObject *parent = nullptr); ~Dpms() override; enum Mode { On, Standby, Suspend, Off, Toggle, }; Q_ENUM(Mode) /** * @returns true if the DPMS system is supported * * If we are still figuring out if it's supported, it will block. */ bool isSupported() const; /** * @returns true if there still are pending DPMS changes * This would happen after @m switchMode is called as most implementations will be async. */ bool hasPendingChanges() const; /** * Switches the @p screens to @p mode * * If @p screens is empty, it will use all the screens as returned by QGuiApplication::screens() */ Q_SCRIPTABLE void switchMode(Mode mode, const QList<QScreen *> &screen = {}); Q_SIGNALS: /** * Notifies about the class being ready for usage */ void supportedChanged(bool supported); /** * Tells which is the new @p mode that the @p screen just adopted */ void modeChanged(Mode mode, QScreen *screen); void hasPendingChangesChanged(bool hasPendingChanges); private: QScopedPointer<AbstractDpmsHelper> m_helper; }; } // namespace #endif // KSCREENDPMS_DPMS_H 070701000000CF000081A40000000000000000000000016638555E000015AA000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/waylanddpmshelper.cpp// SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #include "kscreendpms_debug.h" #include "waylanddpmshelper_p.h" #include "qwayland-dpms.h" #include <QDebug> #include <QGuiApplication> #include <QPointer> #include <QScreen> #include <QVector> #include <QWaylandClientExtensionTemplate> #include <qpa/qplatformnativeinterface.h> class Dpms : public QObject, public QtWayland::org_kde_kwin_dpms { public: Dpms(struct ::org_kde_kwin_dpms *object, WaylandDpmsHelper *dpms, QScreen *parent) : QObject(parent) , org_kde_kwin_dpms(object) , m_screen(parent) , m_dpms(dpms) { } ~Dpms() { release(); } bool isSupported() const { return m_supported; } void org_kde_kwin_dpms_supported(uint32_t supported) override { m_pendingSupported = supported; } void org_kde_kwin_dpms_mode(uint32_t newMode) override { m_mode = mode(newMode); } void org_kde_kwin_dpms_done() override { m_supported = m_pendingSupported; KScreen::Dpms::Mode mode; switch (m_mode) { case Dpms::mode_On: mode = KScreen::Dpms::On; break; case Dpms::mode_Standby: mode = KScreen::Dpms::Standby; break; case Dpms::mode_Suspend: mode = KScreen::Dpms::Suspend; break; case Dpms::mode_Off: mode = KScreen::Dpms::Off; break; } if (m_dpms) { Q_EMIT m_dpms->modeChanged(mode, m_screen); } } QScreen *const m_screen; QPointer<WaylandDpmsHelper> m_dpms; bool m_supported = false; bool m_pendingSupported = false; mode m_mode; }; class DpmsManager : public QWaylandClientExtensionTemplate<::DpmsManager>, public QtWayland::org_kde_kwin_dpms_manager { public: DpmsManager(WaylandDpmsHelper *dpms) : QWaylandClientExtensionTemplate<DpmsManager>(1) , m_dpms(dpms) { connect(this, &DpmsManager::activeChanged, this, [this] { const bool hasDpms = isActive(); if (hasDpms) { qCDebug(KSCREEN_DPMS) << "Compositor provides a DpmsManager"; } else { qCDebug(KSCREEN_DPMS) << "Compositor does not provide a DpmsManager"; m_dpms->setSupported(hasDpms); return; } const auto screens = qGuiApp->screens(); for (QScreen *screen : screens) { addScreen(screen); } connect(qGuiApp, &QGuiApplication::screenAdded, this, &DpmsManager::addScreen); connect(qGuiApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) { delete m_dpmsPerScreen.take(screen); }); m_dpms->setSupported(hasDpms); }); } ~DpmsManager() { qDeleteAll(m_dpmsPerScreen); } Dpms *fetch(QScreen *screen) { return m_dpmsPerScreen.value(screen); } private: void addScreen(QScreen *screen) { // We can't rely on checking the wl_output being null yet // https://codereview.qt-project.org/c/qt/qtwayland/+/464669 const bool fake = screen->geometry().isEmpty() || screen->name().isEmpty(); if (fake) { return; } QPlatformNativeInterface *native = qGuiApp->platformNativeInterface(); wl_output *output = reinterpret_cast<wl_output *>(native->nativeResourceForScreen(QByteArrayLiteral("output"), screen)); if (output) { m_dpmsPerScreen[screen] = new Dpms(get(output), m_dpms, screen); } } WaylandDpmsHelper *const m_dpms; QHash<QScreen *, Dpms *> m_dpmsPerScreen; }; WaylandDpmsHelper::WaylandDpmsHelper() : AbstractDpmsHelper() , m_dpmsManager(new DpmsManager(this)) { } WaylandDpmsHelper::~WaylandDpmsHelper() { delete m_dpmsManager; } void WaylandDpmsHelper::trigger(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens) { Q_ASSERT(isSupported()); if (screens.isEmpty()) { return; } setHasPendingChanges(true); auto level = Dpms::mode_On; switch (mode) { case KScreen::Dpms::Toggle: { for (QScreen *screen : screens) { auto dpms = m_dpmsManager->fetch(screen); if (!dpms || !dpms->isSupported()) { qCDebug(KSCREEN_DPMS) << "screen does not provide dpms" << screen; continue; } if (dpms->m_mode == Dpms::mode_On) { dpms->set(Dpms::mode_Off); } else { dpms->set(Dpms::mode_On); } } } return; case KScreen::Dpms::Off: level = Dpms::mode_Off; break; case KScreen::Dpms::Standby: level = Dpms::mode_Standby; break; case KScreen::Dpms::Suspend: level = Dpms::mode_Suspend; break; case KScreen::Dpms::On: level = Dpms::mode_On; break; } for (auto screen : screens) { auto dpms = m_dpmsManager->fetch(screen); if (dpms) { dpms->set(level); } } setHasPendingChanges(false); } void WaylandDpmsHelper::blockUntilSupported() { QMetaObject::invokeMethod(m_dpmsManager, "addRegistryListener"); if (!m_dpmsManager->isActive()) { setSupported(false); } } 070701000000D0000081A40000000000000000000000016638555E000002A4000000000000000000000000000000000000005000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/waylanddpmshelper_p.h// SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #ifndef WAYLANDDPMSHELPER_H #define WAYLANDDPMSHELPER_H #include "abstractdpmshelper_p.h" #include <QMap> #include <QObject> class DpmsManager; class WaylandDpmsHelper : public KScreen::AbstractDpmsHelper { Q_OBJECT public: WaylandDpmsHelper(); ~WaylandDpmsHelper() override; void trigger(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens) override; private: void blockUntilSupported() override; DpmsManager *m_dpmsManager = nullptr; }; #endif 070701000000D1000081A40000000000000000000000016638555E00000A63000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/xcbdpmshelper.cpp// SPDX-FileCopyrightText: 2010 by Dario Freddi <drf@kde.org> // SPDX-FileCopyrightText: 2015 by Kai Uwe Broulik <kde@privat.broulik.de> // SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #include "xcbdpmshelper_p.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include <QX11Info> #else #include <private/qtx11extras_p.h> #endif #include <QGuiApplication> #include "kscreendpms_debug.h" #include <xcb/dpms.h> template<typename T> using ScopedCPointer = QScopedPointer<T, QScopedPointerPodDeleter>; XcbDpmsHelper::XcbDpmsHelper() : AbstractDpmsHelper() { auto *c = QX11Info::connection(); xcb_prefetch_extension_data(c, &xcb_dpms_id); auto *extension = xcb_get_extension_data(c, &xcb_dpms_id); if (!extension || !extension->present) { qCWarning(KSCREEN_DPMS) << "DPMS extension not available"; setSupported(false); return; } ScopedCPointer<xcb_dpms_capable_reply_t> capableReply(xcb_dpms_capable_reply(c, xcb_dpms_capable(c), nullptr)); setSupported(capableReply && capableReply->capable); // Disable a default timeout, if any xcb_dpms_set_timeouts(QX11Info::connection(), 0, 0, 0); } XcbDpmsHelper::~XcbDpmsHelper() = default; void XcbDpmsHelper::trigger(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens) { setHasPendingChanges(true); auto *c = QX11Info::connection(); if (screens != qGuiApp->screens()) { qCWarning(KSCREEN_DPMS) << "DPMS actions are applied to all screens on X11"; } ScopedCPointer<xcb_dpms_info_reply_t> infoReply(xcb_dpms_info_reply(c, xcb_dpms_info(c), nullptr)); if (!infoReply) { qCWarning(KSCREEN_DPMS) << "Failed to query DPMS state, cannot trigger"; return; } xcb_dpms_dpms_mode_t level = XCB_DPMS_DPMS_MODE_ON; switch (mode) { case KScreen::Dpms::Toggle: if (infoReply->power_level == XCB_DPMS_DPMS_MODE_ON) { level = XCB_DPMS_DPMS_MODE_OFF; } else { level = XCB_DPMS_DPMS_MODE_ON; } break; case KScreen::Dpms::Off: level = XCB_DPMS_DPMS_MODE_OFF; break; case KScreen::Dpms::Standby: level = XCB_DPMS_DPMS_MODE_STANDBY; break; case KScreen::Dpms::Suspend: level = XCB_DPMS_DPMS_MODE_SUSPEND; break; case KScreen::Dpms::On: level = XCB_DPMS_DPMS_MODE_ON; break; } if (!infoReply->state) { xcb_dpms_enable(c); } xcb_dpms_force_level(c, level); setHasPendingChanges(false); } 070701000000D2000081A40000000000000000000000016638555E0000020F000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/libdpms/xcbdpmshelper_p.h// // SPDX-FileCopyrightText: 2015 by Martin Gräßlin <mgraesslin@kde.org> // // SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org> // // SPDX-License-Identifier: LGPL-2.1-or-later #ifndef XCBDPMSHELPER_H #define XCBDPMSHELPER_H #include "abstractdpmshelper_p.h" #include <QScopedPointer> class XcbDpmsHelper : public KScreen::AbstractDpmsHelper { public: XcbDpmsHelper(); ~XcbDpmsHelper() override; void trigger(KScreen::Dpms::Mode mode, const QList<QScreen *> &screens) override; }; #endif 070701000000D3000081A40000000000000000000000016638555E00000AD6000000000000000000000000000000000000003A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/log.cpp/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "log.h" #include <QDateTime> #include <QDir> #include <QFile> #include <QFileInfo> #include <QStandardPaths> namespace KScreen { Log *Log::sInstance = nullptr; QtMessageHandler sDefaultMessageHandler = nullptr; void kscreenLogOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { auto category = QString::fromLocal8Bit(context.category); if (category.startsWith(QLatin1String("kscreen"))) { Log::log(msg, category); } sDefaultMessageHandler(type, context, msg); } void log(const QString &msg) { Log::log(msg); } Log *Log::instance() { if (!sInstance) { sInstance = new Log(); } return sInstance; } using namespace KScreen; class Q_DECL_HIDDEN Log::Private { public: QString context; bool enabled = false; QString logFile; }; Log::Log() : d(new Private) { const char *logging_env = "KSCREEN_LOGGING"; if (qEnvironmentVariableIsSet(logging_env)) { const QString logging_env_value = QString::fromUtf8(qgetenv(logging_env)); if (logging_env_value != QLatin1Char('0') && logging_env_value.toLower() != QLatin1String("false")) { d->enabled = true; } } if (!d->enabled) { return; } d->logFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kscreen/kscreen.log"); QLoggingCategory::setFilterRules(QStringLiteral("kscreen.*=true")); QFileInfo fi(d->logFile); if (!QDir().mkpath(fi.absolutePath())) { qWarning() << "Failed to create logging dir" << fi.absolutePath(); } if (!sDefaultMessageHandler) { sDefaultMessageHandler = qInstallMessageHandler(kscreenLogOutput); } } Log::Log(Log::Private *dd) : d(dd) { } Log::~Log() { delete d; sInstance = nullptr; } QString Log::context() const { return d->context; } void Log::setContext(const QString &context) { d->context = context; } bool Log::enabled() const { return d->enabled; } QString Log::logFile() const { return d->logFile; } void Log::log(const QString &msg, const QString &category) { if (!instance()->enabled()) { return; } auto _cat = category; _cat.remove(QStringLiteral("kscreen.")); const QString timestamp = QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss.zzz")); QString logMessage = QStringLiteral("\n%1 ; %2 ; %3 : %4").arg(timestamp, _cat, instance()->context(), msg); QFile file(instance()->logFile()); if (!file.open(QIODevice::Append | QIODevice::Text)) { return; } file.write(logMessage.toUtf8()); } } // ns 070701000000D4000081A40000000000000000000000016638555E00000A18000000000000000000000000000000000000003800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/log.h/* * SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_LOG_H #define KSCREEN_LOG_H #include "kscreen_export.h" #include <QLoggingCategory> #include <QObject> namespace KScreen { void log(const QString &msg); /** KScreen-internal file logging. * * The purpose of this class is to allow better debugging of kscreen. QDebug falls short here, since * we need to debug the concert of kscreen components from different processes. * * KScreen::Log manages access to kscreen's log file. * * The following environment variables are considered: * - disable logging by setting * KSCREEN_LOGGING=false * - set the log file to a custom path, the default is in ~/.local/share/kscreen/kscreen.log * * Please do not translate messages written to the logs, it's developer information and should be * english, independent from the user's locale preferences. * * @code * * Log::instance()->setContext("resume"); * Log::log("Applying detected output configuration."); * * @endcode * * @since 5.8 */ class KSCREEN_EXPORT Log { public: virtual ~Log(); static Log *instance(); /** Log a message to a file * * Call this static method to add a new line to the log. * * @arg msg The log message to write. */ static void log(const QString &msg, const QString &category = QString()); /** Context for the logs. * * The context can be used to indicate what is going on overall, it is used to be able * to group log entries into subsequential operations. For example the context can be * "handling resume", which is then added to the log messages. * * @arg msg The log message to write to the file. * * @see ontext() */ QString context() const; /** Set the context for the logs. * * @see context() */ void setContext(const QString &context); /** Logging to file is enabled by environmental var, is it? * * @arg msg The log message to write to the file. * @return Whether logging is enabled. */ bool enabled() const; /** Path to the log file * * This is usually ~/.local/share/kscreen/kscreen.log, but can be changed by setting * KSCREEN_LOGFILE in the environment. * * @return The path to the log file. */ QString logFile() const; private: explicit Log(); class Private; Private *const d; static Log *sInstance; explicit Log(Private *dd); }; } // KSCreen namespace #endif // KSCREEN_LOG_H 070701000000D5000081A40000000000000000000000016638555E00000740000000000000000000000000000000000000003B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/mode.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "mode.h" using namespace KScreen; class Q_DECL_HIDDEN Mode::Private { public: Private() : rate(0) { } Private(const Private &other) : id(other.id) , name(other.name) , size(other.size) , rate(other.rate) { } QString id; QString name; QSize size; float rate; }; Mode::Mode() : QObject(nullptr) , d(new Private()) { } Mode::Mode(Mode::Private *dd) : QObject() , d(dd) { } Mode::~Mode() { delete d; } ModePtr Mode::clone() const { return ModePtr(new Mode(new Private(*d))); } const QString Mode::id() const { return d->id; } void Mode::setId(const QString &id) { if (d->id == id) { return; } d->id = id; Q_EMIT modeChanged(); } QString Mode::name() const { return d->name; } void Mode::setName(const QString &name) { if (d->name == name) { return; } d->name = name; Q_EMIT modeChanged(); } QSize Mode::size() const { return d->size; } void Mode::setSize(const QSize &size) { if (d->size == size) { return; } d->size = size; Q_EMIT modeChanged(); } float Mode::refreshRate() const { return d->rate; } void Mode::setRefreshRate(float refresh) { if (qFuzzyCompare(d->rate, refresh)) { return; } d->rate = refresh; Q_EMIT modeChanged(); } QDebug operator<<(QDebug dbg, const KScreen::ModePtr &mode) { if (mode) { dbg << "KScreen::Mode(Id:" << mode->id() << ", Size:" << mode->size() << "@" << mode->refreshRate() << ")"; } else { dbg << "KScreen::Mode(NULL)"; } return dbg; } 070701000000D6000081A40000000000000000000000016638555E00000574000000000000000000000000000000000000003900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/mode.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef MODE_CONFIG_H #define MODE_CONFIG_H #include "kscreen_export.h" #include "types.h" #include <QDebug> #include <QMetaType> #include <QObject> #include <QSize> namespace KScreen { class KSCREEN_EXPORT Mode : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id WRITE setId NOTIFY modeChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY modeChanged) Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY modeChanged) Q_PROPERTY(float refreshRate READ refreshRate WRITE setRefreshRate NOTIFY modeChanged) public: explicit Mode(); ~Mode() override; ModePtr clone() const; const QString id() const; void setId(const QString &id); QString name() const; void setName(const QString &name); QSize size() const; void setSize(const QSize &size); float refreshRate() const; void setRefreshRate(float refresh); Q_SIGNALS: void modeChanged(); private: Q_DISABLE_COPY(Mode) class Private; Private *const d; Mode(Private *dd); }; } // KSCreen namespace KSCREEN_EXPORT QDebug operator<<(QDebug dbg, const KScreen::ModePtr &mode); Q_DECLARE_METATYPE(KScreen::ModeList) #endif // MODE_H 070701000000D7000081A40000000000000000000000016638555E00004750000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/output.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "output.h" #include "edid.h" #include "kscreen_debug.h" #include "mode.h" #include <QCryptographicHash> #include <QGuiApplication> #include <QRect> #include <QScopedPointer> #include <cstdint> #include <qobjectdefs.h> #include <utility> using namespace KScreen; class Q_DECL_HIDDEN Output::Private { public: Private() : id(0) , type(Unknown) , replicationSource(0) , rotation(None) , scale(1.0) , explicitLogicalSize(QSizeF()) , connected(false) , enabled(false) , priority(0) , edid(nullptr) { } Private(const Private &other) : id(other.id) , name(other.name) , type(other.type) , icon(other.icon) , pos(other.pos) , size(other.size) , rotation(other.rotation) , currentMode(other.currentMode) , preferredMode(other.preferredMode) , preferredModes(other.preferredModes) , connected(other.connected) , enabled(other.enabled) , priority(other.priority) , clones(other.clones) , replicationSource(other.replicationSource) , sizeMm(other.sizeMm) , scale(other.scale) , followPreferredMode(other.followPreferredMode) , explicitLogicalSize(other.explicitLogicalSize) , capabilities(other.capabilities) , overscan(other.overscan) , vrrPolicy(other.vrrPolicy) , rgbRange(other.rgbRange) { const auto otherModeList = other.modeList; for (const ModePtr &otherMode : otherModeList) { modeList.insert(otherMode->id(), otherMode->clone()); } if (other.edid) { edid.reset(other.edid->clone()); } } QString biggestMode(const ModeList &modes) const; bool compareModeList(const ModeList &before, const ModeList &after); // please keep them consistent with order of Q_PROPERTY declarations int id; QString name; Type type; QString icon; ModeList modeList; QPoint pos; QSize size; Rotation rotation; // next three don't exactly match properties by name, but keep them close to each other anyway QString currentMode; QString preferredMode; QStringList preferredModes; // bool connected; bool enabled; uint32_t priority; QList<int> clones; int replicationSource; QScopedPointer<Edid> edid; QSize sizeMm; qreal scale; bool followPreferredMode = false; QSizeF explicitLogicalSize; Capabilities capabilities; uint32_t overscan = 0; VrrPolicy vrrPolicy = VrrPolicy::Automatic; RgbRange rgbRange = RgbRange::Automatic; }; bool Output::Private::compareModeList(const ModeList &before, const ModeList &after) { if (before.count() != after.count()) { return false; } for (auto itb = before.constBegin(); itb != before.constEnd(); ++itb) { auto ita = after.constFind(itb.key()); if (ita == after.constEnd()) { return false; } const auto &mb = itb.value(); const auto &ma = ita.value(); if (mb->id() != ma->id()) { return false; } if (mb->size() != ma->size()) { return false; } if (!qFuzzyCompare(mb->refreshRate(), ma->refreshRate())) { return false; } if (mb->name() != ma->name()) { return false; } } // They're the same return true; } QString Output::Private::biggestMode(const ModeList &modes) const { int area, total = 0; KScreen::ModePtr biggest; for (const KScreen::ModePtr &mode : modes) { area = mode->size().width() * mode->size().height(); if (area < total) { continue; } if (area == total && mode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && mode->refreshRate() > biggest->refreshRate()) { biggest = mode; continue; } total = area; biggest = mode; } if (!biggest) { return QString(); } return biggest->id(); } Output::Output() : QObject(nullptr) , d(new Private()) { } Output::Output(Output::Private *dd) : QObject() , d(dd) { } Output::~Output() { delete d; } OutputPtr Output::clone() const { return OutputPtr(new Output(new Private(*d))); } int Output::id() const { return d->id; } void Output::setId(int id) { if (d->id == id) { return; } d->id = id; Q_EMIT outputChanged(); } QString Output::name() const { return d->name; } void Output::setName(const QString &name) { if (d->name == name) { return; } d->name = name; Q_EMIT outputChanged(); } // TODO KF6: remove this deprecated method QString Output::hash() const { if (edid() && edid()->isValid()) { return edid()->hash(); } return name(); } QString Output::hashMd5() const { if (edid() && edid()->isValid()) { return edid()->hash(); } const auto hash = QCryptographicHash::hash(name().toLatin1(), QCryptographicHash::Md5); return QString::fromLatin1(hash.toHex()); } Output::Type Output::type() const { return d->type; } void Output::setType(Type type) { if (d->type == type) { return; } d->type = type; Q_EMIT outputChanged(); } QString Output::typeName() const { switch (d->type) { case Output::Unknown: return QStringLiteral("Unknown"); case Output::Panel: return QStringLiteral("Panel (Laptop)"); case Output::VGA: return QStringLiteral("VGA"); case Output::DVI: return QStringLiteral("DVI"); case Output::DVII: return QStringLiteral("DVI-I"); case Output::DVIA: return QStringLiteral("DVI-A"); case Output::DVID: return QStringLiteral("DVI-D"); case Output::HDMI: return QStringLiteral("HDMI"); case Output::TV: return QStringLiteral("TV"); case Output::TVComposite: return QStringLiteral("TV-Composite"); case Output::TVSVideo: return QStringLiteral("TV-SVideo"); case Output::TVComponent: return QStringLiteral("TV-Component"); case Output::TVSCART: return QStringLiteral("TV-SCART"); case Output::TVC4: return QStringLiteral("TV-C4"); case Output::DisplayPort: return QStringLiteral("DisplayPort"); }; return QStringLiteral("Invalid Type") + QString::number(d->type); } QString Output::icon() const { return d->icon; } void Output::setIcon(const QString &icon) { if (d->icon == icon) { return; } d->icon = icon; Q_EMIT outputChanged(); } ModePtr Output::mode(const QString &id) const { if (!d->modeList.contains(id)) { return ModePtr(); } return d->modeList[id]; } ModeList Output::modes() const { return d->modeList; } void Output::setModes(const ModeList &modes) { bool changed = !d->compareModeList(d->modeList, modes); d->modeList = modes; if (changed) { Q_EMIT modesChanged(); Q_EMIT outputChanged(); } } QString Output::currentModeId() const { return d->currentMode; } void Output::setCurrentModeId(const QString &mode) { if (d->currentMode == mode) { return; } d->currentMode = mode; Q_EMIT currentModeIdChanged(); } ModePtr Output::currentMode() const { return d->modeList.value(d->currentMode); } void Output::setPreferredModes(const QStringList &modes) { d->preferredMode = QString(); d->preferredModes = modes; } QStringList Output::preferredModes() const { return d->preferredModes; } QString Output::preferredModeId() const { if (!d->preferredMode.isEmpty()) { return d->preferredMode; } if (d->preferredModes.isEmpty()) { return d->biggestMode(modes()); } int total = 0; KScreen::ModePtr biggest; KScreen::ModePtr candidateMode; for (const QString &modeId : std::as_const(d->preferredModes)) { candidateMode = mode(modeId); const int area = candidateMode->size().width() * candidateMode->size().height(); if (area < total) { continue; } if (area == total && biggest && candidateMode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && biggest && candidateMode->refreshRate() > biggest->refreshRate()) { biggest = candidateMode; continue; } total = area; biggest = candidateMode; } Q_ASSERT_X(biggest, "preferredModeId", "biggest mode must exist"); d->preferredMode = biggest->id(); return d->preferredMode; } ModePtr Output::preferredMode() const { return d->modeList.value(preferredModeId()); } QPoint Output::pos() const { return d->pos; } void Output::setPos(const QPoint &pos) { if (d->pos == pos) { return; } d->pos = pos; Q_EMIT posChanged(); } QSize Output::size() const { return d->size; } void Output::setSize(const QSize &size) { if (d->size == size) { return; } d->size = size; Q_EMIT sizeChanged(); } // TODO KF6: make the Rotation enum an enum class and align values with Wayland transformation property Output::Rotation Output::rotation() const { return d->rotation; } void Output::setRotation(Output::Rotation rotation) { if (d->rotation == rotation) { return; } d->rotation = rotation; Q_EMIT rotationChanged(); } qreal Output::scale() const { return d->scale; } void Output::setScale(qreal factor) { if (qFuzzyCompare(d->scale, factor)) { return; } d->scale = factor; Q_EMIT scaleChanged(); } QSizeF Output::explicitLogicalSize() const { return d->explicitLogicalSize; } void Output::setExplicitLogicalSize(const QSizeF &size) { if (qFuzzyCompare(d->explicitLogicalSize.width(), size.width()) && qFuzzyCompare(d->explicitLogicalSize.height(), size.height())) { return; } d->explicitLogicalSize = size; Q_EMIT explicitLogicalSizeChanged(); } bool Output::isConnected() const { return d->connected; } void Output::setConnected(bool connected) { if (d->connected == connected) { return; } d->connected = connected; Q_EMIT isConnectedChanged(); } bool Output::isEnabled() const { return d->enabled; } void Output::setEnabled(bool enabled) { if (d->enabled == enabled) { return; } d->enabled = enabled; Q_EMIT isEnabledChanged(); } bool Output::isPrimary() const { return d->enabled && (d->priority == 1); } void Output::setPrimary(bool primary) { if (primary) { setPriority(1); } else { qCWarning(KSCREEN) << "Calling Output::setPrimary(false) is not supported. Port your code to Config::setPrimaryOutput"; } } uint32_t Output::priority() const { return d->priority; } void Output::setPriority(uint32_t priority) { if (d->priority == priority) { return; } d->priority = priority; Q_EMIT priorityChanged(); } QList<int> Output::clones() const { return d->clones; } void Output::setClones(const QList<int> &outputlist) { if (d->clones == outputlist) { return; } d->clones = outputlist; Q_EMIT clonesChanged(); } int Output::replicationSource() const { return d->replicationSource; } void Output::setReplicationSource(int source) { if (d->replicationSource == source) { return; } d->replicationSource = source; Q_EMIT replicationSourceChanged(); } void Output::setEdid(const QByteArray &rawData) { Q_ASSERT(d->edid.isNull()); d->edid.reset(new Edid(rawData)); } Edid *Output::edid() const { return d->edid.data(); } QSize Output::sizeMm() const { return d->sizeMm; } void Output::setSizeMm(const QSize &size) { d->sizeMm = size; } bool KScreen::Output::followPreferredMode() const { return d->followPreferredMode; } void KScreen::Output::setFollowPreferredMode(bool follow) { if (follow == d->followPreferredMode) { return; } d->followPreferredMode = follow; Q_EMIT followPreferredModeChanged(follow); } bool Output::isPositionable() const { return isConnected() && isEnabled() && !replicationSource(); } QSize Output::enforcedModeSize() const { if (const auto mode = currentMode()) { return mode->size(); } else if (const auto mode = preferredMode()) { return mode->size(); } else if (d->modeList.count() > 0) { return d->modeList.first()->size(); } return QSize(); } QRect Output::geometry() const { QSize size = explicitLogicalSize().toSize(); if (!size.isValid()) { return QRect(); } return QRect(d->pos, size); } Output::Capabilities Output::capabilities() const { return d->capabilities; } void Output::setCapabilities(Capabilities capabilities) { if (d->capabilities == capabilities) { return; } d->capabilities = capabilities; Q_EMIT capabilitiesChanged(); } uint32_t Output::overscan() const { return d->overscan; } void Output::setOverscan(uint32_t overscan) { if (d->overscan == overscan) { return; } d->overscan = overscan; Q_EMIT overscanChanged(); } Output::VrrPolicy Output::vrrPolicy() const { return d->vrrPolicy; } void Output::setVrrPolicy(VrrPolicy policy) { if (d->vrrPolicy == policy) { return; } d->vrrPolicy = policy; Q_EMIT vrrPolicyChanged(); } Output::RgbRange Output::rgbRange() const { return d->rgbRange; } void Output::setRgbRange(Output::RgbRange rgbRange) { if (d->rgbRange == rgbRange) { return; } d->rgbRange = rgbRange; Q_EMIT rgbRangeChanged(); } void Output::apply(const OutputPtr &other) { typedef void (KScreen::Output::*ChangeSignal)(); QList<ChangeSignal> changes; // We block all signals, and emit them only after we have set up everything // This is necessary in order to prevent clients from accessing inconsistent // outputs from intermediate change signals const bool keepBlocked = blockSignals(true); if (d->name != other->d->name) { changes << &Output::outputChanged; setName(other->d->name); } if (d->type != other->d->type) { changes << &Output::outputChanged; setType(other->d->type); } if (d->icon != other->d->icon) { changes << &Output::outputChanged; setIcon(other->d->icon); } if (d->pos != other->d->pos) { changes << &Output::posChanged; setPos(other->pos()); } if (d->rotation != other->d->rotation) { changes << &Output::rotationChanged; setRotation(other->d->rotation); } if (!qFuzzyCompare(d->scale, other->d->scale)) { changes << &Output::scaleChanged; setScale(other->d->scale); } if (d->currentMode != other->d->currentMode) { changes << &Output::currentModeIdChanged; setCurrentModeId(other->d->currentMode); } if (d->connected != other->d->connected) { changes << &Output::isConnectedChanged; setConnected(other->d->connected); } if (d->enabled != other->d->enabled) { changes << &Output::isEnabledChanged; setEnabled(other->d->enabled); } if (d->priority != other->d->priority) { changes << &Output::priorityChanged; setPriority(other->d->priority); } if (d->clones != other->d->clones) { changes << &Output::clonesChanged; setClones(other->d->clones); } if (d->replicationSource != other->d->replicationSource) { changes << &Output::replicationSourceChanged; setReplicationSource(other->d->replicationSource); } if (!d->compareModeList(d->modeList, other->d->modeList)) { changes << &Output::outputChanged; changes << &Output::modesChanged; } setPreferredModes(other->d->preferredModes); ModeList modes; for (const ModePtr &otherMode : other->modes()) { modes.insert(otherMode->id(), otherMode->clone()); } setModes(modes); if (d->capabilities != other->d->capabilities) { changes << &Output::capabilitiesChanged; setCapabilities(other->d->capabilities); } if (d->vrrPolicy != other->d->vrrPolicy) { changes << &Output::vrrPolicyChanged; setVrrPolicy(other->d->vrrPolicy); } if (d->overscan != other->d->overscan) { changes << &Output::overscanChanged; setOverscan(other->d->overscan); } if (d->rgbRange != other->d->rgbRange) { changes << &Output::rgbRangeChanged; setRgbRange(other->d->rgbRange); } // Non-notifyable changes if (other->d->edid) { d->edid.reset(other->d->edid->clone()); } blockSignals(keepBlocked); while (!changes.isEmpty()) { const ChangeSignal &sig = changes.first(); Q_EMIT(this->*sig)(); changes.removeAll(sig); } } QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output) { QDebugStateSaver saver(dbg); if (!output) { dbg << "KScreen::Output(NULL)"; return dbg; } // clang-format off dbg.nospace() << "KScreen::Output(" << output->id() << ", " << output->name() << ", " << (output->isConnected() ? "connected " : "disconnected ") << (output->isEnabled() ? "enabled" : "disabled") << " priority " << output->priority() << ", pos: " << output->pos() << ", res: " << output->size() << ", modeId: " << output->currentModeId() << ", scale: " << output->scale() << ", clone: " << (output->clones().isEmpty() ? "no" : "yes") << ", rotation: " << output->rotation() << ", followPreferredMode: " << output->followPreferredMode() << ")"; // clang-format on return dbg; } 070701000000D8000081ED0000000000000000000000016638555E000032E4000000000000000000000000000000000000003B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/output.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef OUTPUT_CONFIG_H #define OUTPUT_CONFIG_H #include "kscreen_export.h" #include "types.h" #include <QDebug> #include <QMetaType> #include <QObject> #include <QPoint> #include <QSize> #include <QStringList> namespace KScreen { class Edid; class Mode; class KSCREEN_EXPORT Output : public QObject { Q_OBJECT Q_CLASSINFO("RegisterEnumClassesUnscoped", "false") public: Q_PROPERTY(int id READ id CONSTANT) Q_PROPERTY(QString name READ name WRITE setName NOTIFY outputChanged) Q_PROPERTY(Type type READ type WRITE setType NOTIFY outputChanged) Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY outputChanged) Q_PROPERTY(ModeList modes READ modes NOTIFY modesChanged) Q_PROPERTY(QPoint pos READ pos WRITE setPos NOTIFY posChanged) Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged) Q_PROPERTY(Rotation rotation READ rotation WRITE setRotation NOTIFY rotationChanged) Q_PROPERTY(QString currentModeId READ currentModeId WRITE setCurrentModeId NOTIFY currentModeIdChanged) Q_PROPERTY(QString preferredModeId READ preferredModeId CONSTANT) Q_PROPERTY(bool connected READ isConnected WRITE setConnected NOTIFY isConnectedChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY isEnabledChanged) Q_PROPERTY(bool primary READ isPrimary WRITE setPrimary NOTIFY priorityChanged) Q_PROPERTY(uint32_t priority READ priority WRITE setPriority NOTIFY priorityChanged) Q_PROPERTY(QList<int> clones READ clones WRITE setClones NOTIFY clonesChanged) Q_PROPERTY(int replicationSource READ replicationSource WRITE setReplicationSource NOTIFY replicationSourceChanged) Q_PROPERTY(KScreen::Edid *edid READ edid CONSTANT) Q_PROPERTY(QSize sizeMm READ sizeMm CONSTANT) Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) Q_PROPERTY(bool followPreferredMode READ followPreferredMode WRITE setFollowPreferredMode NOTIFY followPreferredModeChanged) Q_PROPERTY(QSizeF explicitLogicalSize READ explicitLogicalSize WRITE setExplicitLogicalSize NOTIFY explicitLogicalSizeChanged) Q_PROPERTY(Capabilities capabilities READ capabilities NOTIFY capabilitiesChanged) Q_PROPERTY(uint32_t overscan READ overscan WRITE setOverscan NOTIFY overscanChanged) Q_PROPERTY(VrrPolicy vrrPolicy READ vrrPolicy WRITE setVrrPolicy NOTIFY vrrPolicyChanged) Q_PROPERTY(RgbRange rgbRange READ rgbRange WRITE setRgbRange NOTIFY rgbRangeChanged) enum Type { Unknown, VGA, DVI, DVII, DVIA, DVID, HDMI, Panel, TV, TVComposite, TVSVideo, TVComponent, TVSCART, TVC4, DisplayPort, }; Q_ENUM(Type) enum Rotation { None = 1, Left = 2, Inverted = 4, Right = 8, }; Q_ENUM(Rotation) enum class Capability { Overscan = 0x1, Vrr = 0x2, RgbRange = 0x4, }; Q_ENUM(Capability) Q_DECLARE_FLAGS(Capabilities, Capability) Q_FLAG(Capabilities) enum class VrrPolicy { Never = 0, Always = 1, Automatic = 2, }; Q_ENUM(VrrPolicy) enum class RgbRange { Automatic = 0, Full = 1, Limited = 2, }; Q_ENUM(RgbRange) explicit Output(); ~Output() override; OutputPtr clone() const; int id() const; void setId(int id); QString name() const; void setName(const QString &name); /** * Returns an identifying hash for this output. * * The hash is calculated either via the edid hash or if no * edid is available by the output name. * * @return identifying hash of this output * @since 5.15 * @deprecated * @see hashMd5 */ QString hash() const; /** * Returns an identifying hex encoded MD5-hash for this output. * * The hash is calculated either via the edid hash or if no * edid is available by the output name, which is hashed as well. * * @return identifying hash of this output * @since 5.17 */ QString hashMd5() const; Type type() const; QString typeName() const; void setType(Type type); QString icon() const; void setIcon(const QString &icon); Q_INVOKABLE ModePtr mode(const QString &id) const; ModeList modes() const; void setModes(const ModeList &modes); QString currentModeId() const; void setCurrentModeId(const QString &mode); Q_INVOKABLE ModePtr currentMode() const; void setPreferredModes(const QStringList &modes); QStringList preferredModes() const; /** * Returns the preferred mode with higher resolution and refresh */ Q_INVOKABLE QString preferredModeId() const; /** * Returns KScreen::Mode associated with preferredModeId() */ Q_INVOKABLE ModePtr preferredMode() const; QPoint pos() const; void setPos(const QPoint &pos); /*** * Returns actual size being rendered in the output * * The returned valued is after transformations have been applied to * the resolution of the current mode. * * For example if currentMode is 1280x800 but it is a vertical screen * the returned size will be 800x1280. * * If that same resolution (1280x800) is transformed and scale x2, the * value returned will be 2560x1600. * * This property reflects the currently active output configuration and * is not affected by current mode or orientation change made by user * until the config is applied. * * @since 5.4 */ QSize size() const; void setSize(const QSize &size); /** * Returns either current mode size or if not available preferred one or if also not * available the first one in the ModeList. * * @return mode size */ QSize enforcedModeSize() const; Rotation rotation() const; void setRotation(Rotation rotation); /** * A comfortable function that returns true when output is not rotated * or is rotated upside down. */ Q_INVOKABLE inline bool isHorizontal() const { return ((rotation() == Output::None) || (rotation() == Output::Inverted)); } bool isConnected() const; void setConnected(bool connected); bool isEnabled() const; void setEnabled(bool enabled); bool isPrimary() const; void setPrimary(bool primary); uint32_t priority() const; void setPriority(uint32_t priority); /** * @brief Immutable clones because of hardware restrictions * * Clones are set symmetrically on all outputs. The list contains ids * for all other outputs being clones of this output. * * @return List of output ids being clones of each other. */ QList<int> clones() const; /** * @brief Set the clones list. * * When this output is part of a configuration this call is followed by * similar calls on other outputs making the lists in all outputs * symmetric. * @param outputlist */ void setClones(const QList<int> &outputlist); /** * @brief Provides the source for an ongoing replication * * If the returned output id is non-null this output is a replica of the * returned output. If null is returned the output is no replica of any * other output. * * @return Replication source output id of this output */ int replicationSource() const; /** * @brief Set the replication source. * @param source */ void setReplicationSource(int source); void setEdid(const QByteArray &rawData); /** * edid returns the output's EDID information if available. * * The output maintains ownership of the returned Edid, so the caller should not delete it. * Note that the edid is only valid as long as the output is alive. */ Edid *edid() const; /** * Returns the physical size of the screen in milimeters. * * @note Some broken GPUs or monitors return the size in centimeters instead * of millimeters. KScreen at the moment is not sanitizing the values. */ QSize sizeMm() const; void setSizeMm(const QSize &size); /** * Returns if the output needs to be taken account for in the overall compositor/screen * space and if it should be depicted on its own in a graphical view for repositioning. * * @return true if the output is positionable in compositor/screen space. * * @since 5.17 */ bool isPositionable() const; /** * Returns a rectangle containing the currently set output position and * size. * * The geometry also reflects current orientation (i.e. if current mode * is 1920x1080 and orientation is @p KScreen::Output::Left, then the * size of the returned rectangle will be 1080x1920. * * This property contains the current settings stored in the particular * Output object, so it is updated even when user changes current mode * or orientation without applying the whole config/ */ QRect geometry() const; /** * returns the scaling factor to use for this output * * @since 5.9 */ qreal scale() const; /** * Set the scaling factor for this output. * * @arg factor Scale factor to use for this output, the backend may or may not * be able to deal with non-integer values, in that case, the factor gets rounded. * * @since 5.9 */ void setScale(qreal factor); /** * The logical size is the output's representation internal to the display server and its * overall screen geometry. * * returns the explicitly set logical size of this output, is an invalid size if not set * * @since 5.18 */ QSizeF explicitLogicalSize() const; /** * Specifies explicitly the logical size of this output and by that overrides any other * logical size calculation through mode and scale. To enable this instead again call this * function with an invalid size as argument. * * @param size of this output in logical space * * @since 5.24 */ void setExplicitLogicalSize(const QSizeF &size); /** * @returns whether the mode should be changed to the new preferred mode * once it changes * * @since 5.15 */ bool followPreferredMode() const; /** * Set whether the preferred mode should be followed through @arg follow * * @since 5.15 */ void setFollowPreferredMode(bool follow); /** * @returns the capabilities of this output * @since 5.22 */ Capabilities capabilities() const; /** * sets the capabilities of this output * @since 5.22 */ void setCapabilities(Capabilities capabilities); /** * @returns the overscan value of this output in % * @since 5.22 */ uint32_t overscan() const; /** * Set the overscan for this output * @param overscan the overscan value in % * @since 5.22 */ void setOverscan(uint32_t overscan); /** * @returns when variable refresh rate should be used on this output * * @since 5.22 */ VrrPolicy vrrPolicy() const; /** * Set when variable refresh rate should be used on this output * * @since 5.22 */ void setVrrPolicy(VrrPolicy policy); /** * @returns which rgb range the output is using * @since 5.23 */ RgbRange rgbRange() const; /** * Set which rgb range the output should use * @since 5.23 */ void setRgbRange(RgbRange rgbRange); void apply(const OutputPtr &other); Q_SIGNALS: void outputChanged(); void posChanged(); void sizeChanged(); void currentModeIdChanged(); void rotationChanged(); void isConnectedChanged(); void isEnabledChanged(); void priorityChanged(); void clonesChanged(); void replicationSourceChanged(); void scaleChanged(); void explicitLogicalSizeChanged(); void followPreferredModeChanged(bool followPreferredMode); void capabilitiesChanged(); void overscanChanged(); void vrrPolicyChanged(); void rgbRangeChanged(); /** The mode list changed. * * This may happen when a mode is added or changed. * * @since 5.8.3 */ void modesChanged(); private: Q_DISABLE_COPY(Output) class Private; Private *const d; explicit Output(Private *dd); }; } // KScreen namespace KSCREEN_EXPORT QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output); Q_DECLARE_METATYPE(KScreen::OutputList) Q_DECLARE_METATYPE(KScreen::Output::Rotation) Q_DECLARE_METATYPE(KScreen::Output::Type) #endif // OUTPUT_H 070701000000D9000081A40000000000000000000000016638555E000007D6000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/screen.cpp/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "screen.h" using namespace KScreen; class Q_DECL_HIDDEN Screen::Private { public: Private() : id(0) , maxActiveOutputsCount(0) { } Private(const Private &other) : id(other.id) , maxActiveOutputsCount(other.maxActiveOutputsCount) , currentSize(other.currentSize) , minSize(other.minSize) , maxSize(other.maxSize) { } int id; int maxActiveOutputsCount; QSize currentSize; QSize minSize; QSize maxSize; }; Screen::Screen() : QObject(nullptr) , d(new Private()) { } Screen::Screen(Screen::Private *dd) : QObject() , d(dd) { } Screen::~Screen() { delete d; } ScreenPtr Screen::clone() const { return ScreenPtr(new Screen(new Private(*d))); } int Screen::id() const { return d->id; } void Screen::setId(int id) { d->id = id; } QSize Screen::currentSize() const { return d->currentSize; } void Screen::setCurrentSize(const QSize ¤tSize) { if (d->currentSize == currentSize) { return; } d->currentSize = currentSize; Q_EMIT currentSizeChanged(); } QSize Screen::maxSize() const { return d->maxSize; } void Screen::setMaxSize(const QSize &maxSize) { d->maxSize = maxSize; } QSize Screen::minSize() const { return d->minSize; } void Screen::setMinSize(const QSize &minSize) { d->minSize = minSize; } int Screen::maxActiveOutputsCount() const { return d->maxActiveOutputsCount; } void Screen::setMaxActiveOutputsCount(int maxActiveOutputsCount) { d->maxActiveOutputsCount = maxActiveOutputsCount; } void Screen::apply(const ScreenPtr &other) { // Only set values that can change setMaxActiveOutputsCount(other->d->maxActiveOutputsCount); setCurrentSize(other->d->currentSize); } 070701000000DA000081A40000000000000000000000016638555E0000087B000000000000000000000000000000000000003B00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/screen.h/* * SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef SCREEN_CONFIG_H #define SCREEN_CONFIG_H #include "kscreen_export.h" #include "types.h" #include <QObject> #include <QSize> namespace KScreen { class KSCREEN_EXPORT Screen : public QObject { Q_OBJECT public: Q_PROPERTY(int id READ id CONSTANT) Q_PROPERTY(QSize currentSize READ currentSize WRITE setCurrentSize NOTIFY currentSizeChanged) Q_PROPERTY(QSize minSize READ minSize CONSTANT) Q_PROPERTY(QSize maxSize READ maxSize CONSTANT) Q_PROPERTY(int maxActiveOutputsCount READ maxActiveOutputsCount CONSTANT) explicit Screen(); ~Screen() override; ScreenPtr clone() const; /** * The id of this screen. * @return id of this screen */ int id() const; /** * The identifier of this screen. * @param id id of the screen */ void setId(int id); /** * The current screen size in pixels. * @return Screen size in pixels */ QSize currentSize() const; /** * Set the current screen size in pixels. * @param currentSize Screen size in pixels */ void setCurrentSize(const QSize ¤tSize); /** * The minimum screen size in pixels. * @return Minimum screen size in pixels */ QSize minSize() const; /** * Set the minimum screen size in pixels. * @param minSize Minimum screen size in pixels */ void setMinSize(const QSize &minSize); /** * The maximum screen size in pixels. * @return Maximum screen size in pixels */ QSize maxSize() const; /** * Set the maximum screen size in pixels. * @param maxSize Maximum screen size in pixels */ void setMaxSize(const QSize &maxSize); int maxActiveOutputsCount() const; void setMaxActiveOutputsCount(int maxActiveOutputsCount); void apply(const ScreenPtr &other); Q_SIGNALS: void currentSizeChanged(); private: Q_DISABLE_COPY(Screen) class Private; Private *const d; Screen(Private *dd); }; } // KScreen namespace #endif // SCREEN_H 070701000000DB000081A40000000000000000000000016638555E000014CC000000000000000000000000000000000000004900000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/setconfigoperation.cpp/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include "setconfigoperation.h" #include "abstractbackend.h" #include "backendmanager_p.h" #include "config.h" #include "configoperation_p.h" #include "configserializer_p.h" #include "kscreen_debug.h" #include "output.h" #include <QDBusPendingCall> #include <QDBusPendingCallWatcher> using namespace KScreen; namespace KScreen { class SetConfigOperationPrivate : public ConfigOperationPrivate { Q_OBJECT public: explicit SetConfigOperationPrivate(const KScreen::ConfigPtr &config, ConfigOperation *qq); void backendReady(org::kde::kscreen::Backend *backend) override; void onConfigSet(QDBusPendingCallWatcher *watcher); void normalizeOutputPositions(); void fixPrimaryOutput(); KScreen::ConfigPtr config; private: Q_DECLARE_PUBLIC(SetConfigOperation) }; } SetConfigOperationPrivate::SetConfigOperationPrivate(const ConfigPtr &config, ConfigOperation *qq) : ConfigOperationPrivate(qq) , config(config) { } void SetConfigOperationPrivate::backendReady(org::kde::kscreen::Backend *backend) { ConfigOperationPrivate::backendReady(backend); Q_Q(SetConfigOperation); if (!backend) { q->setError(tr("Failed to prepare backend")); q->emitResult(); return; } const QVariantMap map = ConfigSerializer::serializeConfig(config).toVariantMap(); if (map.isEmpty()) { q->setError(tr("Failed to serialize request")); q->emitResult(); return; } QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(backend->setConfig(map), this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &SetConfigOperationPrivate::onConfigSet); } void SetConfigOperationPrivate::onConfigSet(QDBusPendingCallWatcher *watcher) { Q_Q(SetConfigOperation); QDBusPendingReply<QVariantMap> reply = *watcher; watcher->deleteLater(); if (reply.isError()) { q->setError(reply.error().message()); q->emitResult(); return; } config = ConfigSerializer::deserializeConfig(reply.value()); if (!config) { q->setError(tr("Failed to deserialize backend response")); } q->emitResult(); } SetConfigOperation::SetConfigOperation(const ConfigPtr &config, QObject *parent) : ConfigOperation(new SetConfigOperationPrivate(config, this), parent) { } SetConfigOperation::~SetConfigOperation() { } ConfigPtr SetConfigOperation::config() const { Q_D(const SetConfigOperation); return d->config; } void SetConfigOperation::start() { Q_D(SetConfigOperation); d->normalizeOutputPositions(); d->fixPrimaryOutput(); if (BackendManager::instance()->method() == BackendManager::InProcess) { auto backend = d->loadBackend(); backend->setConfig(d->config); emitResult(); } else { d->requestBackend(); } } void SetConfigOperationPrivate::normalizeOutputPositions() { if (!config) { return; } int offsetX = INT_MAX; int offsetY = INT_MAX; const auto outputs = config->outputs(); for (const KScreen::OutputPtr &output : outputs) { if (!output->isPositionable()) { continue; } offsetX = qMin(output->pos().x(), offsetX); offsetY = qMin(output->pos().y(), offsetY); } if (!offsetX && !offsetY) { return; } qCDebug(KSCREEN) << "Correcting output positions by:" << QPoint(offsetX, offsetY); for (const KScreen::OutputPtr &output : outputs) { if (!output->isConnected() || !output->isEnabled()) { continue; } QPoint newPos = QPoint(output->pos().x() - offsetX, output->pos().y() - offsetY); qCDebug(KSCREEN) << "Moved output from" << output->pos() << "to" << newPos; output->setPos(newPos); } } void SetConfigOperationPrivate::fixPrimaryOutput() { if (!config || !(config->supportedFeatures() & Config::Feature::PrimaryDisplay)) { return; } const auto outputs = config->outputs(); if (outputs.isEmpty()) { return; } // Here we make sure that: // - that our primary is enabled // - we have at least a primary // - we have exactly 1 primary // - we have a primary at all bool found = false; KScreen::OutputPtr primary; KScreen::OutputPtr candidate; for (const KScreen::OutputPtr &output : outputs) { if (output->isPrimary()) { if (!output->isEnabled()) { qCDebug(KSCREEN) << "can't be primary if disabled!!" << output; } else if (found) { qCDebug(KSCREEN) << "can only have 1 primary" << output; } else { found = true; primary = output; } } else if (output->isEnabled()) { candidate = output; } } if (!found && candidate) { qCDebug(KSCREEN) << "setting primary instead" << candidate; config->setPrimaryOutput(candidate); } else if (primary) { // ensures all others are set to non-primary. It's OK if all outputs // are disabled and `primary` is essentially nullptr. config->setPrimaryOutput(primary); } } #include "setconfigoperation.moc" 070701000000DC000081A40000000000000000000000016638555E000002EA000000000000000000000000000000000000004700000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/setconfigoperation.h/* * SPDX-FileCopyrightText: 2014 Daniel Vratil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later * */ #ifndef KSCREEN_SETCONFIGOPERATION_H #define KSCREEN_SETCONFIGOPERATION_H #include "configoperation.h" #include "kscreen_export.h" #include "types.h" namespace KScreen { class SetConfigOperationPrivate; class KSCREEN_EXPORT SetConfigOperation : public KScreen::ConfigOperation { Q_OBJECT public: explicit SetConfigOperation(const KScreen::ConfigPtr &config, QObject *parent = nullptr); ~SetConfigOperation() override; KScreen::ConfigPtr config() const override; protected: void start() override; private: Q_DECLARE_PRIVATE(SetConfigOperation) }; } #endif // KSCREEN_SETCONFIGOPERATION_H 070701000000DD000081A40000000000000000000000016638555E0000025D000000000000000000000000000000000000003A00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/src/types.h/* * SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_TYPES_H #define KSCREEN_TYPES_H #include <QMap> #include <QSharedPointer> namespace KScreen { class Config; typedef QSharedPointer<KScreen::Config> ConfigPtr; class Screen; typedef QSharedPointer<KScreen::Screen> ScreenPtr; class Output; typedef QSharedPointer<KScreen::Output> OutputPtr; typedef QMap<int, KScreen::OutputPtr> OutputList; class Mode; typedef QSharedPointer<KScreen::Mode> ModePtr; typedef QMap<QString, KScreen::ModePtr> ModeList; } #endif 070701000000DE000041ED0000000000000000000000036638555E00000000000000000000000000000000000000000000003400000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests070701000000DF000081A40000000000000000000000016638555E0000008F000000000000000000000000000000000000004300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/CMakeLists.txtadd_executable(printconfig testplugandplay.cpp testpnp.cpp) target_link_libraries(printconfig Qt::Gui KF5::Screen) add_subdirectory(kwayland) 070701000000E0000041ED0000000000000000000000026638555E00000000000000000000000000000000000000000000003D00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland070701000000E1000081A40000000000000000000000016638555E00000162000000000000000000000000000000000000004C00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/CMakeLists.txtif (NOT TARGET KF5::WaylandServer) message(WARNING "Skipping KF5::WaylandServer based tests!") return() endif() add_definitions(-DTEST_DATA="${CMAKE_SOURCE_DIR}/autotests/configs/") add_executable(waylandtestserver main.cpp waylandtestserver.cpp waylandconfigreader.cpp) target_link_libraries(waylandtestserver KF5::Screen KF5::WaylandServer) 070701000000E2000081A40000000000000000000000016638555E0000036C000000000000000000000000000000000000004600000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/main.cpp/* * SPDX-FileCopyrightText: 2014-2016 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "waylandtestserver.h" #include <QCommandLineParser> #include <QCoreApplication> int main(int argc, char **argv) { QCoreApplication app(argc, argv); KScreen::WaylandTestServer server; QCommandLineOption config = QCommandLineOption(QStringList() << QStringLiteral("c") << QStringLiteral("config"), QStringLiteral("Config file"), QStringLiteral("config")); QCommandLineParser parser; parser.addHelpOption(); parser.addOption(config); parser.process(app); if (parser.isSet(config)) { server.setConfig(parser.value(config)); } else { server.setConfig(QString::fromLocal8Bit(TEST_DATA) + QLatin1String("/multipleoutput.json")); } server.start(); return app.exec(); } 070701000000E3000081A40000000000000000000000016638555E000027A6000000000000000000000000000000000000005500000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/waylandconfigreader.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandconfigreader.h" #include <QDebug> #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QUuid> #include "edid.h" using namespace KScreen; static QList<int> s_outputIds; void WaylandConfigReader::outputsFromConfig(const QString &configfile, KWayland::Server::Display *display, QList<KWayland::Server::OutputDeviceInterface *> &outputs) { qDebug() << "Loading server from" << configfile; QFile file(configfile); file.open(QIODevice::ReadOnly); QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll()); QJsonObject json = jsonDoc.object(); QJsonArray omap = json[QStringLiteral("outputs")].toArray(); for (const QJsonValue &value : omap) { const QVariantMap &output = value.toObject().toVariantMap(); if (output[QStringLiteral("connected")].toBool()) { outputs << createOutputDevice(output, display); // qDebug() << "new Output created: " << output["name"].toString(); } else { // qDebug() << "disconnected Output" << output["name"].toString(); } } auto outpus = WaylandConfigReader::createOutputs(display, outputs); s_outputIds.clear(); } OutputDeviceInterface *WaylandConfigReader::createOutputDevice(const QVariantMap &outputConfig, KWayland::Server::Display *display) { KWayland::Server::OutputDeviceInterface *outputdevice = display->createOutputDevice(display); QByteArray data = QByteArray::fromBase64(outputConfig[QStringLiteral("edid")].toByteArray()); outputdevice->setEdid(data); Edid edid(data, display); // qDebug() << "EDID Info: "; if (edid.isValid()) { // qDebug() << "\tDevice ID: " << edid.deviceId(); // qDebug() << "\tName: " << edid.name(); // qDebug() << "\tVendor: " << edid.vendor(); // qDebug() << "\tSerial: " << edid.serial(); // qDebug() << "\tEISA ID: " << edid.eisaId(); // qDebug() << "\tHash: " << edid.hash(); // qDebug() << "\tWidth (mm): " << edid.width(); // qDebug() << "\tHeight (mm): " << edid.height(); // qDebug() << "\tGamma: " << edid.gamma(); // qDebug() << "\tRed: " << edid.red(); // qDebug() << "\tGreen: " << edid.green(); // qDebug() << "\tBlue: " << edid.blue(); // qDebug() << "\tWhite: " << edid.white(); outputdevice->setPhysicalSize(QSize(edid.width() * 10, edid.height() * 10)); outputdevice->setManufacturer(edid.vendor()); outputdevice->setModel(edid.name()); } else { outputdevice->setPhysicalSize(sizeFromJson(outputConfig[QStringLiteral("sizeMM")])); outputdevice->setManufacturer(outputConfig[QStringLiteral("manufacturer")].toString()); outputdevice->setModel(outputConfig[QStringLiteral("model")].toString()); } auto uuid = QUuid::createUuid().toByteArray(); auto _id = outputConfig[QStringLiteral("id")].toInt(); if (_id) { uuid = QString::number(_id).toLocal8Bit(); } outputdevice->setUuid(uuid); const QMap<int, KWayland::Server::OutputDeviceInterface::Transform> transformMap = {{0, KWayland::Server::OutputDeviceInterface::Transform::Normal}, {1, KWayland::Server::OutputDeviceInterface::Transform::Normal}, {2, KWayland::Server::OutputDeviceInterface::Transform::Rotated270}, {3, KWayland::Server::OutputDeviceInterface::Transform::Rotated180}, {4, KWayland::Server::OutputDeviceInterface::Transform::Rotated90}}; outputdevice->setTransform(transformMap[outputConfig[QStringLiteral("rotation")].toInt()]); int currentModeId = outputConfig[QStringLiteral("currentModeId")].toInt(); QVariantList preferredModes = outputConfig[QStringLiteral("preferredModes")].toList(); int mode_id = 0; for (const QVariant &_mode : outputConfig[QStringLiteral("modes")].toList()) { mode_id++; const QVariantMap &mode = _mode.toMap(); OutputDeviceInterface::Mode m0; const QSize _size = sizeFromJson(mode[QStringLiteral("size")]); auto refreshRateIt = mode.constFind(QStringLiteral("refreshRate")); if (refreshRateIt != mode.constEnd()) { m0.refreshRate = qRound(refreshRateIt->toReal() * 1000); // config has it in Hz } bool isCurrent = currentModeId == mode[QStringLiteral("id")].toInt(); bool isPreferred = preferredModes.contains(mode[QStringLiteral("id")]); OutputDeviceInterface::ModeFlags flags; if (isCurrent && isPreferred) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current | OutputDeviceInterface::ModeFlag::Preferred); } else if (isCurrent) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current); } else if (isPreferred) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); } auto idIt = mode.constFind(QStringLiteral("id")); if (idIt != mode.constEnd()) { m0.id = idIt->toInt(); } else { m0.id = mode_id; } m0.size = _size; m0.flags = flags; outputdevice->addMode(m0); if (isCurrent) { outputdevice->setCurrentMode(m0.id); } } outputdevice->setGlobalPosition(pointFromJson(outputConfig[QStringLiteral("pos")])); outputdevice->setEnabled(outputConfig[QStringLiteral("enabled")].toBool() ? OutputDeviceInterface::Enablement::Enabled : OutputDeviceInterface::Enablement::Disabled); outputdevice->create(); return outputdevice; } QList<KWayland::Server::OutputInterface *> KScreen::WaylandConfigReader::createOutputs(KWayland::Server::Display *display, QList<KWayland::Server::OutputDeviceInterface *> &outputdevices) { const QMap<KWayland::Server::OutputDeviceInterface::Transform, KWayland::Server::OutputInterface::Transform> transformMap = { {KWayland::Server::OutputDeviceInterface::Transform::Normal, KWayland::Server::OutputInterface::Transform::Normal}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated270, KWayland::Server::OutputInterface::Transform::Rotated270}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated180, KWayland::Server::OutputInterface::Transform::Rotated180}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated90, KWayland::Server::OutputInterface::Transform::Rotated90}, }; QList<KWayland::Server::OutputInterface *> outputs; for (const auto outputdevice : outputdevices) { qDebug() << "New Output!"; KWayland::Server::OutputInterface *output = display->createOutput(display); // Sync properties from outputdevice to the newly created output interface output->setManufacturer(outputdevice->manufacturer()); output->setModel(outputdevice->model()); // output->setUuid(outputdevice->uuid()); for (const auto mode : outputdevice->modes()) { bool isCurrent = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current); bool isPreferred = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current); OutputInterface::ModeFlags flags; if (isPreferred && isCurrent) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred); } else if (isCurrent) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current); } else if (isPreferred) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred); } OutputInterface::Mode m0; m0.size = mode.size; output->addMode(m0.size, m0.flags, m0.refreshRate); if (isCurrent) { output->setCurrentMode(m0.size, m0.refreshRate); } // qDebug() << "mode added:" << m0.size << m0.refreshRate << isCurrent; } output->setGlobalPosition(outputdevice->globalPosition()); output->setPhysicalSize(outputdevice->physicalSize()); output->setTransform(transformMap.value(outputdevice->transform())); output->setDpmsSupported(true); output->setDpmsMode(OutputInterface::DpmsMode::On); QObject::connect(output, &OutputInterface::dpmsModeRequested, [](KWayland::Server::OutputInterface::DpmsMode requestedMode) { Q_UNUSED(requestedMode); // FIXME: make sure this happens in the scope of an object! qDebug() << "DPMS Mode change requested"; }); output->create(); outputs << output; } return outputs; } QSize WaylandConfigReader::sizeFromJson(const QVariant &data) { QVariantMap map = data.toMap(); QSize size; size.setWidth(map[QStringLiteral("width")].toInt()); size.setHeight(map[QStringLiteral("height")].toInt()); return size; } QPoint WaylandConfigReader::pointFromJson(const QVariant &data) { QVariantMap map = data.toMap(); QPoint point; point.setX(map[QStringLiteral("x")].toInt()); point.setY(map[QStringLiteral("y")].toInt()); return point; } QRect WaylandConfigReader::rectFromJson(const QVariant &data) { QRect rect; rect.setSize(WaylandConfigReader::sizeFromJson(data)); rect.setBottomLeft(WaylandConfigReader::pointFromJson(data)); return rect; } 070701000000E4000081A40000000000000000000000016638555E00000554000000000000000000000000000000000000005300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/waylandconfigreader.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_WAYLAND_CONFIGREADER_H #define KSCREEN_WAYLAND_CONFIGREADER_H #include <QObject> #include <QRect> // KWayland #include <KWayland/Server/display.h> #include <KWayland/Server/output_interface.h> #include <KWayland/Server/outputdevice_interface.h> namespace KScreen { using namespace KWayland::Server; class WaylandConfigReader { public: // static QList<KWayland::Server::OutputInterface*> outputsFromConfig(const QString &configfile, KWayland::Server::Display *display); static void outputsFromConfig(const QString &configfile, KWayland::Server::Display *display, QList<KWayland::Server::OutputDeviceInterface *> &outputs); static OutputDeviceInterface *createOutputDevice(const QVariantMap &outputConfig, KWayland::Server::Display *display); static QList<KWayland::Server::OutputInterface *> createOutputs(KWayland::Server::Display *display, QList<KWayland::Server::OutputDeviceInterface *> &outputdevices); static QSize sizeFromJson(const QVariant &data); static QRect rectFromJson(const QVariant &data); static QPoint pointFromJson(const QVariant &data); }; } // namespace #endif // KSCREEN_WAYLAND_CONFIGREADER_H 070701000000E5000081A40000000000000000000000016638555E000018EE000000000000000000000000000000000000005300000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/waylandtestserver.cpp/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "waylandtestserver.h" #include "waylandconfigreader.h" #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QLoggingCategory> #include <QStandardPaths> #include <utility> #include "../src/edid.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND_TESTSERVER, "kscreen.kwayland.testserver") using namespace KScreen; using namespace KWayland::Server; WaylandTestServer::WaylandTestServer(QObject *parent) : QObject(parent) , m_configFile(QLatin1String(TEST_DATA) + QLatin1String("default.json")) , m_display(nullptr) , m_outputManagement(nullptr) , m_dpmsManager(nullptr) , m_suspendChanges(false) , m_waiting(nullptr) { } WaylandTestServer::~WaylandTestServer() { stop(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Wayland server shut down."; } void WaylandTestServer::start() { using namespace KWayland::Server; delete m_display; m_display = new KWayland::Server::Display(this); if (qgetenv("WAYLAND_DISPLAY").isEmpty()) { m_display->setSocketName(s_socketName); } else { m_display->setSocketName(QString::fromLatin1(qgetenv("WAYLAND_DISPLAY"))); } m_display->start(); auto manager = m_display->createDpmsManager(); manager->create(); m_outputManagement = m_display->createOutputManagement(); m_outputManagement->create(); connect(m_outputManagement, &OutputManagementInterface::configurationChangeRequested, this, &WaylandTestServer::configurationChangeRequested); KScreen::WaylandConfigReader::outputsFromConfig(m_configFile, m_display, m_outputs); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("export WAYLAND_DISPLAY=") + m_display->socketName(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("You can specify the WAYLAND_DISPLAY for this server by exporting it in the environment"); // showOutputs(); } void WaylandTestServer::stop() { qDeleteAll(m_outputs); m_outputs.clear(); // actually stop the Wayland server delete m_display; m_display = nullptr; } KWayland::Server::Display *WaylandTestServer::display() { return m_display; } void WaylandTestServer::setConfig(const QString &configfile) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Creating Wayland server from " << configfile; m_configFile = configfile; } int WaylandTestServer::outputCount() const { return m_outputs.count(); } QList<KWayland::Server::OutputDeviceInterface *> WaylandTestServer::outputs() const { return m_outputs; } void WaylandTestServer::suspendChanges(bool suspend) { if (m_suspendChanges == suspend) { return; } m_suspendChanges = suspend; if (!suspend && m_waiting) { m_waiting->setApplied(); m_waiting = nullptr; Q_EMIT configChanged(); } } void WaylandTestServer::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *configurationInterface) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server received change request, changes:" << configurationInterface->changes().count(); Q_EMIT configReceived(); auto changes = configurationInterface->changes(); for (auto it = changes.constBegin(); it != changes.constEnd(); ++it) { auto outputdevice = it.key(); auto c = it.value(); if (c->enabledChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting enabled:"; outputdevice->setEnabled(c->enabled()); } if (c->modeChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting new mode:" << c->mode() << modeString(outputdevice, c->mode()); outputdevice->setCurrentMode(c->mode()); } if (c->transformChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting transform: " << (int)(c->transform()); outputdevice->setTransform(c->transform()); } if (c->positionChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting position: " << c->position(); outputdevice->setGlobalPosition(c->position()); } if (c->scaleChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting scale:" << c->scaleF(); outputdevice->setScaleF(c->scaleF()); } } if (m_suspendChanges) { Q_ASSERT(!m_waiting); m_waiting = configurationInterface; return; } configurationInterface->setApplied(); // showOutputs(); Q_EMIT configChanged(); } void WaylandTestServer::showOutputs() { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******** Wayland server running: " << m_outputs.count() << " outputs. ********"; for (const auto &o : std::as_const(m_outputs)) { bool enabled = (o->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " * Output id: " << o->uuid(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Enabled: " << (enabled ? "enabled" : "disabled"); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Name: " << QStringLiteral("%2-%3").arg(o->manufacturer(), o->model()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Mode: " << modeString(o, o->currentModeId()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Pos: " << o->globalPosition(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Edid: " << o->edid(); // << o->currentMode().size(); } qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******************************************************"; } QString WaylandTestServer::modeString(KWayland::Server::OutputDeviceInterface *outputdevice, int mid) { QString s; QString ids; int _i = 0; for (const auto &_m : outputdevice->modes()) { _i++; if (_i < 6) { ids.append(QString::number(_m.id) + QLatin1String(", ")); } else { ids.append(QLatin1Char('.')); } if (_m.id == mid) { s = QStringLiteral("%1x%2 @%3").arg(QString::number(_m.size.width()), QString::number(_m.size.height()), QString::number(_m.refreshRate)); } } return QStringLiteral("[%1] %2 (%4 modes: %3)").arg(QString::number(mid), s, ids, QString::number(outputdevice->modes().count())); } 070701000000E6000081A40000000000000000000000016638555E000007B4000000000000000000000000000000000000005100000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/kwayland/waylandtestserver.h/* * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_WAYLAND_TESTSERVER_H #define KSCREEN_WAYLAND_TESTSERVER_H #include <QObject> // KWayland #include <KWayland/Server/compositor_interface.h> #include <KWayland/Server/display.h> #include <KWayland/Server/dpms_interface.h> #include <KWayland/Server/outputconfiguration_interface.h> #include <KWayland/Server/outputdevice_interface.h> #include <KWayland/Server/outputmanagement_interface.h> #include <KWayland/Server/seat_interface.h> #include <KWayland/Server/shell_interface.h> namespace KScreen { static const QString s_socketName = QStringLiteral("libkscreen-test-wayland-backend-0"); using namespace KWayland::Server; class WaylandTestServer : public QObject { Q_OBJECT public: explicit WaylandTestServer(QObject *parent = nullptr); ~WaylandTestServer() override; void setConfig(const QString &configfile); void start(); void stop(); void showOutputs(); KWayland::Server::Display *display(); QList<KWayland::Server::OutputDeviceInterface *> outputs() const; int outputCount() const; void suspendChanges(bool suspend); Q_SIGNALS: void outputsChanged(); void started(); void configReceived(); void configChanged(); private Q_SLOTS: void configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *configurationInterface); private: static QString modeString(KWayland::Server::OutputDeviceInterface *outputdevice, int mid); QString m_configFile; KWayland::Server::Display *m_display; QList<KWayland::Server::OutputDeviceInterface *> m_outputs; KWayland::Server::OutputManagementInterface *m_outputManagement; KWayland::Server::DpmsManagerInterface *m_dpmsManager; bool m_suspendChanges; KWayland::Server::OutputConfigurationInterface *m_waiting; }; } // namespace #endif // KSCREEN_WAYLAND_SCREEN_H 070701000000E7000081A40000000000000000000000016638555E00000294000000000000000000000000000000000000004800000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/testplugandplay.cpp/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "testpnp.h" #include <QCommandLineParser> #include <QGuiApplication> using namespace KScreen; int main(int argc, char **argv) { QGuiApplication app(argc, argv); QCommandLineOption input = QCommandLineOption(QStringList() << QStringLiteral("m") << QStringLiteral("monitor"), QStringLiteral("Keep running monitoring for changes")); QCommandLineParser parser; parser.addHelpOption(); parser.addOption(input); parser.process(app); new TestPnp(parser.isSet(input)); return app.exec(); } 070701000000E8000081A40000000000000000000000016638555E000013CE000000000000000000000000000000000000004000000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/testpnp.cpp/* * SPDX-FileCopyrightText: 2013 Alejandro Fiestas Olivares <afiestas@kde.org> * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "testpnp.h" #include "../src/configmonitor.h" #include "../src/edid.h" #include "../src/getconfigoperation.h" #include "../src/mode.h" #include "../src/output.h" #include <QGuiApplication> #include <QRect> #include <QScreen> #include <cstdint> //#include <QX11Info> using namespace KScreen; QString typetoString(const Output::Type &type) { switch (type) { case Output::Unknown: return QStringLiteral("Unknown"); case Output::Panel: return QStringLiteral("Panel (Laptop)"); case Output::VGA: return QStringLiteral("VGA"); case Output::DVII: return QStringLiteral("DVI-I"); case Output::DVIA: return QStringLiteral("DVI-A"); case Output::DVID: return QStringLiteral("DVI-D"); case Output::HDMI: return QStringLiteral("HDMI"); case Output::TV: return QStringLiteral("TV"); case Output::TVComposite: return QStringLiteral("TV-Composite"); case Output::TVSVideo: return QStringLiteral("TV-SVideo"); case Output::TVComponent: return QStringLiteral("TV-Component"); case Output::TVSCART: return QStringLiteral("TV-SCART"); case Output::TVC4: return QStringLiteral("TV-C4"); case Output::DisplayPort: return QStringLiteral("DisplayPort"); default: return QStringLiteral("Invalid Type"); }; } TestPnp::TestPnp(bool monitor, QObject *parent) : QObject(parent) , m_monitor(monitor) { init(); } TestPnp::~TestPnp() { } void TestPnp::init() { connect(new KScreen::GetConfigOperation(), &KScreen::GetConfigOperation::finished, this, &TestPnp::configReady); } void TestPnp::configReady(KScreen::ConfigOperation *op) { m_config = qobject_cast<KScreen::GetConfigOperation *>(op)->config(); if (!m_config) { qDebug() << "Config is invalid, probably backend couldn't load"; qApp->quit(); return; } if (!m_config->screen()) { qDebug() << "No screen in the configuration, broken backend"; qApp->quit(); return; } print(); if (m_monitor) { ConfigMonitor::instance()->addConfig(m_config); } else { qApp->quit(); } } void TestPnp::print() { qDebug() << "Screen:"; qDebug() << "\tmaxSize:" << m_config->screen()->maxSize(); qDebug() << "\tminSize:" << m_config->screen()->minSize(); qDebug() << "\tcurrentSize:" << m_config->screen()->currentSize(); const OutputList outputs = m_config->outputs(); for (const OutputPtr &output : outputs) { qDebug() << "\n-----------------------------------------------------\n"; qDebug() << "Id: " << output->id(); qDebug() << "Name: " << output->name(); qDebug() << "Type: " << typetoString(output->type()); qDebug() << "Connected: " << output->isConnected(); if (!output->isConnected()) { continue; } qDebug() << "Enabled: " << output->isEnabled(); qDebug() << "Priority: " << output->priority(); qDebug() << "Rotation: " << output->rotation(); qDebug() << "Pos: " << output->pos(); qDebug() << "MMSize: " << output->sizeMm(); if (output->currentMode()) { qDebug() << "Size: " << output->currentMode()->size(); } if (output->clones().isEmpty()) { qDebug() << "Clones: " << "None"; } else { qDebug() << "Clones: " << output->clones().count(); } qDebug() << "Mode: " << output->currentModeId(); qDebug() << "Preferred Mode: " << output->preferredModeId(); qDebug() << "Preferred modes: " << output->preferredModes(); qDebug() << "Modes: "; const ModeList modes = output->modes(); for (const ModePtr &mode : modes) { qDebug() << "\t" << mode->id() << " " << mode->name() << " " << mode->size() << " " << mode->refreshRate(); } const Edid *const edid = output->edid(); qDebug() << "EDID Info: "; if (edid && edid->isValid()) { qDebug() << "\tDevice ID: " << edid->deviceId(); qDebug() << "\tName: " << edid->name(); qDebug() << "\tVendor: " << edid->vendor(); qDebug() << "\tSerial: " << edid->serial(); qDebug() << "\tEISA ID: " << edid->eisaId(); qDebug() << "\tHash: " << edid->hash(); qDebug() << "\tWidth: " << edid->width(); qDebug() << "\tHeight: " << edid->height(); qDebug() << "\tGamma: " << edid->gamma(); qDebug() << "\tRed: " << edid->red(); qDebug() << "\tGreen: " << edid->green(); qDebug() << "\tBlue: " << edid->blue(); qDebug() << "\tWhite: " << edid->white(); } else { qDebug() << "\tUnavailable"; } } } 070701000000E9000081A40000000000000000000000016638555E00000295000000000000000000000000000000000000003E00000000libkscreen-VERSIONgit.20240506T035822~1c4fd3a/tests/testpnp.h/* * SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #ifndef KSCREEN_TESTPNP_H #define KSCREEN_TESTPNP_H #include "../src/config.h" #include "../src/screen.h" #include <QObject> namespace KScreen { class Output; class ConfigOperation; class TestPnp : public QObject { Q_OBJECT public: explicit TestPnp(bool monitor, QObject *parent = nullptr); ~TestPnp() override; private Q_SLOTS: void init(); void configReady(KScreen::ConfigOperation *op); void print(); private: ConfigPtr m_config; bool m_monitor; }; } // namespace #endif // KSCREEN_TESTPNP_H 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1436 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor