diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU 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. + + GNU GENERAL PUBLIC LICENSE + 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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/libkscreen-5.15.5/.arcconfig b/libkscreen-5.15.5/.arcconfig new file mode 100644 index 0000000000000000000000000000000000000000..bc0df43f1cf7a566f160dc0216666fc87a0e48f2 --- /dev/null +++ b/libkscreen-5.15.5/.arcconfig @@ -0,0 +1,4 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/" +} + diff --git a/libkscreen-5.15.5/.gitignore b/libkscreen-5.15.5/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0e31076ada2e7a3d0d90bf10da9dc56ae607e2ab --- /dev/null +++ b/libkscreen-5.15.5/.gitignore @@ -0,0 +1,4 @@ +.kdev4 +*.kdev4 +build +build-overlay diff --git a/libkscreen-5.15.5/.mailmap b/libkscreen-5.15.5/.mailmap new file mode 100644 index 0000000000000000000000000000000000000000..d6d02658794b0507e4fe582d8b835a8b3b280b57 --- /dev/null +++ b/libkscreen-5.15.5/.mailmap @@ -0,0 +1 @@ +Sebastian Kügler diff --git a/libkscreen-5.15.5/CMakeLists.txt b/libkscreen-5.15.5/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e9e20e8c29dcf70fa5f72c72307c51beedd60d50 --- /dev/null +++ b/libkscreen-5.15.5/CMakeLists.txt @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.0) + +project(libkscreen) +set(PROJECT_VERSION "5.15.5") + +set(QT_MIN_VERSION "5.11.0") +set(KF5_MIN_VERSION "5.54.0") + +find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) +include(KDEInstallDirs) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(KDECMakeSettings) +include(ECMSetupVersion) +include(ECMMarkAsTest) +include(ECMGenerateHeaders) +include(ECMQtDeclareLoggingCategory) +include(ECMAddQch) +include(FeatureSummary) +include(CheckCXXCompilerFlag) +include(CMakePackageConfigHelpers) +include(GenerateExportHeader) + +find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core DBus Gui Test X11Extras) + +# Wayland backend +find_package(KF5Wayland ${KF5_MIN_VERSION} CONFIG REQUIRED) +add_feature_info("KF5Wayland" KF5Wayland_FOUND "Required for building libkscreen's KWayland backend") + +# xrandr backend + +find_package(XCB COMPONENTS XCB RANDR) +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)") + + +set(KF5_MIN_VERSION ${PROJECT_VERSION}) #When we are happy with the api, we can sync with frameworks +ecm_setup_version(${KF5_MIN_VERSION} VARIABLE_PREFIX KSCREEN + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kscreen_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ScreenConfigVersion.cmake" + SOVERSION 7) + +check_cxx_compiler_flag(-fvisibility=hidden _HAVE_VISIBILITY) +if (_HAVE_VISIBILITY AND NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + + check_cxx_compiler_flag(-fvisibility-inlines-hidden _HAVE_VISIBILITY_INLINES) + if (_HAVE_VISIBILITY_INLINES) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") + endif (_HAVE_VISIBILITY_INLINES) +endif (_HAVE_VISIBILITY AND NOT WIN32) + +add_subdirectory(src) +add_subdirectory(backends) +if(BUILD_TESTING) + add_subdirectory(autotests) + add_subdirectory(tests) +endif() + +set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5Screen") + +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 "${KF5_INCLUDE_INSTALL_DIR}" + COMPONENT Devel +) +install( FILES libkscreen.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) + + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/libkscreen-5.15.5/COPYING b/libkscreen-5.15.5/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1 --- /dev/null +++ b/libkscreen-5.15.5/COPYING @@ -0,0 +1,339 @@ + GNU 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. + + GNU GENERAL PUBLIC LICENSE + 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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/libkscreen-5.15.5/COPYING.LIB b/libkscreen-5.15.5/COPYING.LIB new file mode 100644 index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1 --- /dev/null +++ b/libkscreen-5.15.5/COPYING.LIB @@ -0,0 +1,339 @@ + GNU 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. + + GNU GENERAL PUBLIC LICENSE + 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. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/libkscreen-5.15.5/KF5ScreenConfig.cmake.in b/libkscreen-5.15.5/KF5ScreenConfig.cmake.in new file mode 100644 index 0000000000000000000000000000000000000000..1e4869073d75dedceb913396abec92cc4324e84a --- /dev/null +++ b/libkscreen-5.15.5/KF5ScreenConfig.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(Qt5Core @QT_MIN_VERSION@) +find_dependency(Qt5Gui @QT_MIN_VERSION@) + +include("${CMAKE_CURRENT_LIST_DIR}/KF5ScreenTargets.cmake") +@PACKAGE_INCLUDE_QCHTARGETS@ diff --git a/libkscreen-5.15.5/autotests/CMakeLists.txt b/libkscreen-5.15.5/autotests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c0c5fe0afdef83559af312b3a7928e3a4931b5e7 --- /dev/null +++ b/libkscreen-5.15.5/autotests/CMakeLists.txt @@ -0,0 +1,49 @@ +add_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}) + qt5_add_dbus_interface(test_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.FakeBackend.xml fakebackendinterface) + add_executable(${_testname} ${test_SRCS}) + target_link_libraries(${_testname} Qt5::Core Qt5::Gui Qt5::Test Qt5::DBus KF5::Screen ${KSCREEN_WAYLAND_LIBS}) + add_test(NAME kscreen-${_testname} + COMMAND dbus-launch $ + ) + 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) + +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(textxrandr) +endif() diff --git a/libkscreen-5.15.5/autotests/configs/default.json b/libkscreen-5.15.5/autotests/configs/default.json new file mode 100644 index 0000000000000000000000000000000000000000..063898d10f55b33cb7b2c15236d7c34849aed629 --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/default.json @@ -0,0 +1,673 @@ +{ + "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 + } + } +} diff --git a/libkscreen-5.15.5/autotests/configs/multipleclone.json b/libkscreen-5.15.5/autotests/configs/multipleclone.json new file mode 100644 index 0000000000000000000000000000000000000000..43bd3b56e984840e2883596ee4f29bb1ffc52371 --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/multipleclone.json @@ -0,0 +1,124 @@ +{ + "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==" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/configs/multipleoutput.json b/libkscreen-5.15.5/autotests/configs/multipleoutput.json new file mode 100644 index 0000000000000000000000000000000000000000..6df3529ddb7077a81cdedede98ed29494583538c --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/multipleoutput.json @@ -0,0 +1,132 @@ +{ + "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==" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/configs/singleOutputWithoutPreferred.json b/libkscreen-5.15.5/autotests/configs/singleOutputWithoutPreferred.json new file mode 100644 index 0000000000000000000000000000000000000000..e6bbc1dbeb424da668e6e786a00bf5e61a65282c --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/singleOutputWithoutPreferred.json @@ -0,0 +1,68 @@ +{ + "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=" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/configs/singleoutput.json b/libkscreen-5.15.5/autotests/configs/singleoutput.json new file mode 100644 index 0000000000000000000000000000000000000000..a7cdf4297bc8e2287e7f6acb728508108f761a88 --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/singleoutput.json @@ -0,0 +1,72 @@ +{ + "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=" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/configs/singleoutputBroken.json b/libkscreen-5.15.5/autotests/configs/singleoutputBroken.json new file mode 100644 index 0000000000000000000000000000000000000000..3c4e0db374803284c44ba047ede294ccf5a0a8b0 --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/singleoutputBroken.json @@ -0,0 +1,76 @@ +{ + "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=" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/configs/tooManyOutputs.json b/libkscreen-5.15.5/autotests/configs/tooManyOutputs.json new file mode 100644 index 0000000000000000000000000000000000000000..91799ccfa6b6f51f9762e9ddb8a4e1ea2d021b7b --- /dev/null +++ b/libkscreen-5.15.5/autotests/configs/tooManyOutputs.json @@ -0,0 +1,78 @@ +{ + "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==" + } + ] +} diff --git a/libkscreen-5.15.5/autotests/testbackendloader.cpp b/libkscreen-5.15.5/autotests/testbackendloader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc67888784d7ed34c9f343af9afdd744cb196007 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testbackendloader.cpp @@ -0,0 +1,109 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include +#include + +#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("var"); + QTest::addColumn("backend"); + + 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"; +} + +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" diff --git a/libkscreen-5.15.5/autotests/testconfigmonitor.cpp b/libkscreen-5.15.5/autotests/testconfigmonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4eae7480592a836ff7a31b90985e7e9321b5c199 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testconfigmonitor.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include +#include +#include + +#include "../src/backendmanager_p.h" +#include "../src/config.h" +#include "../src/output.h" +#include "../src/configmonitor.h" +#include "../src/configoperation.h" +#include "../src/getconfigoperation.h" +#include "../src/setconfigoperation.h" +#include "../src/backendmanager_p.h" +#include + +#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 + qputenv("KSCREEN_BACKEND_ARGS", "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" diff --git a/libkscreen-5.15.5/autotests/testconfigserializer.cpp b/libkscreen-5.15.5/autotests/testconfigserializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b6bdb3060696cf192853576624ad4e7c82c4abb --- /dev/null +++ b/libkscreen-5.15.5/autotests/testconfigserializer.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include +#include + +#include "../src/types.h" +#include "../src/configserializer_p.h" +#include "../src/screen.h" +#include "../src/mode.h" +#include "../src/output.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(stringList); + QCOMPARE(arr.size(), stringList.size()); + + for (int i = 0; i < arr.size(); ++i) { + QCOMPARE(arr.at(i).toString(), stringList.at(i)); + } + + + + QList intList; + intList << 4 << 3 << 2 << 1; + + arr = KScreen::ConfigSerializer::serializeList(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->setPrimary(true); + output->setClones(QList() << 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(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(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("primary")].toBool(), output->isPrimary()); + 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" diff --git a/libkscreen-5.15.5/autotests/testedid.cpp b/libkscreen-5.15.5/autotests/testedid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1c656025cd7e6d7e98c264e32854d2f4ec3174f --- /dev/null +++ b/libkscreen-5.15.5/autotests/testedid.cpp @@ -0,0 +1,175 @@ +/************************************************************************************* + * Copyright 2018 by Frederik Gladhorn * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include + +#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 e(new Edid()); + QCOMPARE(e->isValid(), false); + QCOMPARE(e->name(), QString()); + + QScopedPointer e2(e->clone()); + QCOMPARE(e2->isValid(), false); + QCOMPARE(e2->name(), QString()); + + QScopedPointer e3(new Edid("some random data")); + QCOMPARE(e3->isValid(), false); +} + +void TestEdid::testEdidParser_data() +{ + // The raw edid data + QTest::addColumn("raw_edid"); + QTest::addColumn("deviceId"); + QTest::addColumn("name"); + QTest::addColumn("pnpId"); + // List of potential vendor names, this depends on the availablility + // of pnp.ids, otherwise it will be a three letter abbreviation. + QTest::addColumn("vendor"); + QTest::addColumn("serial"); + QTest::addColumn("eisaId"); + QTest::addColumn("hash"); + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::addColumn("gamma"); + + QTest::addColumn("red"); + QTest::addColumn("green"); + QTest::addColumn("blue"); + QTest::addColumn("white"); + + QTest::addRow("cor") + << QByteArray::fromBase64("AP///////wAN8iw0AAAAABwVAQOAHRB4CoPVlFdSjCccUFQAAAABAQEBAQEBAQEBAQEBAQEBEhtWWlAAGTAwIDYAJaQQAAAYEhtWWlAAGTAwIDYAJaQQAAAYAAAA/gBBVU8KICAgICAgICAgAAAA/gBCMTMzWFcwMyBWNCAKAIc=") + << QStringLiteral("xrandr-unknown") + << 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 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-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-unknown") + << 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)); +} + +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 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" diff --git a/libkscreen-5.15.5/autotests/testinprocess.cpp b/libkscreen-5.15.5/autotests/testinprocess.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b71cfca0836c0656dcd48ab1ce60376c5ae5f4b6 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testinprocess.cpp @@ -0,0 +1,344 @@ +/************************************************************************************* + * Copyright 2015 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include +#include +#include + +#include "../src/backendmanager_p.h" +#include "../src/getconfigoperation.h" +#include "../src/setconfigoperation.h" +#include "../src/config.h" +#include "../src/configmonitor.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/edid.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"); + qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "multipleoutput.json"); + + KScreen::BackendManager::instance()->shutdownBackend(); +} + +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(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(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" diff --git a/libkscreen-5.15.5/autotests/testkwaylandbackend.cpp b/libkscreen-5.15.5/autotests/testkwaylandbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5abe6af72f8cbb3dbb400fdfcd0ed2be72f7e529 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testkwaylandbackend.cpp @@ -0,0 +1,316 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include +#include + +#include "../src/backendmanager_p.h" +#include "../src/getconfigoperation.h" +#include "../src/setconfigoperation.h" +#include "../src/config.h" +#include "../src/configmonitor.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/edid.h" + +// KWayland +#include +#include + +#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; + Q_FOREACH (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 ids; + Q_FOREACH (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); + + Q_FOREACH (const auto &output, m_config->outputs()) { + Q_FOREACH (auto mode, output->modes()) { + QVERIFY(!mode->name().isEmpty()); + QVERIFY(mode->refreshRate() > 0); + QVERIFY(mode->size().isValid()); + } + } +} + +void testWaylandBackend::verifyIds() +{ + QList ids; + Q_FOREACH (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(1); + 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/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); + + QScopedPointer 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" diff --git a/libkscreen-5.15.5/autotests/testkwaylandconfig.cpp b/libkscreen-5.15.5/autotests/testkwaylandconfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3eec7a5eab1bbc9f6d1b775b841d927210ce9706 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testkwaylandconfig.cpp @@ -0,0 +1,326 @@ +/************************************************************************************* + * Copyright 2015 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include +#include + +#include "backendmanager_p.h" +#include "getconfigoperation.h" +#include "setconfigoperation.h" +#include "config.h" +#include "configmonitor.h" +#include "output.h" +#include "mode.h" +#include "edid.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("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" diff --git a/libkscreen-5.15.5/autotests/testkwaylanddpms.cpp b/libkscreen-5.15.5/autotests/testkwaylanddpms.cpp new file mode 100644 index 0000000000000000000000000000000000000000..069e543b878a8998634c5cd439cbbfbb821273eb --- /dev/null +++ b/libkscreen-5.15.5/autotests/testkwaylanddpms.cpp @@ -0,0 +1,126 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#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"); + } + 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" diff --git a/libkscreen-5.15.5/autotests/testlog.cpp b/libkscreen-5.15.5/autotests/testlog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..34c1e94b082c4bd64ae4107155bc4e3d521b94e7 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testlog.cpp @@ -0,0 +1,140 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include + +#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" diff --git a/libkscreen-5.15.5/autotests/testmodelistchange.cpp b/libkscreen-5.15.5/autotests/testmodelistchange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee29c83d5c62c3d16c22012f1429917efe7a1f3e --- /dev/null +++ b/libkscreen-5.15.5/autotests/testmodelistchange.cpp @@ -0,0 +1,179 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include + +#include "../src/config.h" +#include "../src/configmonitor.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/getconfigoperation.h" +#include "../src/setconfigoperation.h" +#include "../src/backendmanager_p.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 + qputenv("KSCREEN_BACKEND_ARGS", "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" diff --git a/libkscreen-5.15.5/autotests/testqscreenbackend.cpp b/libkscreen-5.15.5/autotests/testqscreenbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c2ed71bf849d313bcc49ea21dde149601eb296e --- /dev/null +++ b/libkscreen-5.15.5/autotests/testqscreenbackend.cpp @@ -0,0 +1,226 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright 2012 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include +#include + +#include "../src/config.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/edid.h" +#include "../src/getconfigoperation.h" +#include "../src/backendmanager_p.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; + foreach (const KScreen::OutputPtr &op, m_config->outputs()) { + if (op->isPrimary()) { + primaryFound = true; + } + } + qDebug() << "Primary found? " << primaryFound; + QVERIFY(primaryFound); + if (m_backend == QStringLiteral("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 ids; + foreach (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); + + foreach (const KScreen::OutputPtr &output, m_config->outputs()) { + foreach (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; + Q_FOREACH(const KScreen::OutputPtr &output, outputs) { + if (!output->isConnected()) { + continue; + } + + QVariantMap info; + info[QStringLiteral("id")] = output->id(); + info[QStringLiteral("primary")] = output->isPrimary(); + 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" diff --git a/libkscreen-5.15.5/autotests/testscreenconfig.cpp b/libkscreen-5.15.5/autotests/testscreenconfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c09f87d5be5b5cb7af60d5d85f38871da99144c9 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testscreenconfig.cpp @@ -0,0 +1,326 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include +#include + +#include "../src/screen.h" +#include "../src/config.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/getconfigoperation.h" +#include "../src/setconfigoperation.h" +#include "../src/backendmanager_p.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 + qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleoutput.json"); + +// 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() +{ + qputenv("KSCREEN_BACKEND_ARGS", "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() +{ + qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "multipleoutput.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(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() +{ + qputenv("KSCREEN_BACKEND_ARGS", "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() +{ + qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "singleoutputBroken.json"); + const ConfigPtr brokenConfig = getConfig(); + + qputenv("KSCREEN_BACKEND_ARGS", "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)); + + + qputenv("KSCREEN_BACKEND_ARGS", "TEST_DATA=" TEST_DATA "tooManyOutputs.json"); + const ConfigPtr brokenConfig2 = getConfig(); + QVERIFY(!brokenConfig2.isNull()); + + int enabledOutputsCount = 0; + Q_FOREACH (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() +{ + qputenv("KSCREEN_BACKEND_ARGS", "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" diff --git a/libkscreen-5.15.5/autotests/testxrandr.cpp b/libkscreen-5.15.5/autotests/testxrandr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..14f15f81ecb3e026948d23730a7fb7786aedbda7 --- /dev/null +++ b/libkscreen-5.15.5/autotests/testxrandr.cpp @@ -0,0 +1,77 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#define QT_GUI_LIB + +#include +#include + +#include "../src/config.h" +#include "../src/output.h" +#include "../src/mode.h" +#include "../src/getconfigoperation.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" diff --git a/libkscreen-5.15.5/backends/CMakeLists.txt b/libkscreen-5.15.5/backends/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3563e138454edcc0c874237745123b09077d067c --- /dev/null +++ b/libkscreen-5.15.5/backends/CMakeLists.txt @@ -0,0 +1,11 @@ +add_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() diff --git a/libkscreen-5.15.5/backends/fake/CMakeLists.txt b/libkscreen-5.15.5/backends/fake/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..649edba1ba5be89a72949e902787bdd19f44842c --- /dev/null +++ b/libkscreen-5.15.5/backends/fake/CMakeLists.txt @@ -0,0 +1,27 @@ +include_directories(${CMAKE_SOURCE_DIR}/src + ${CMAKE_BUILD_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(fake_SRCS + fake.cpp + parser.cpp +) + +qt5_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/kf5/kscreen") +set_target_properties(KSC_Fake PROPERTIES PREFIX "") +target_link_libraries(KSC_Fake + Qt5::Core + Qt5::DBus + KF5::Screen +) + +install(TARGETS KSC_Fake DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kscreen/) + + diff --git a/libkscreen-5.15.5/backends/fake/fake.cpp b/libkscreen-5.15.5/backends/fake/fake.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba4ff139788bd1769d8fdfd98d845075e8737cae --- /dev/null +++ b/libkscreen-5.15.5/backends/fake/fake.cpp @@ -0,0 +1,203 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "fake.h" +#include "parser.h" + +#include "config.h" +#include "edid.h" +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#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(); + qCDebug(KSCREEN_FAKE) << "Fake profile file:" << mConfigFile; + +} + +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); + } + + return mConfig; +} + +void Fake::setConfig(const ConfigPtr &config) +{ + qCDebug(KSCREEN_FAKE) << "set config" << config->outputs(); + mConfig = config->clone(); + 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(); + Q_FOREACH(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->isPrimary() == primary) { + return; + } + + Q_FOREACH (KScreen::OutputPtr output, config()->outputs()) { + if (output->id() == outputId) { + output->setPrimary(primary); + } else { + output->setPrimary(false); + } + } + 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(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); +} diff --git a/libkscreen-5.15.5/backends/fake/fake.h b/libkscreen-5.15.5/backends/fake/fake.h new file mode 100644 index 0000000000000000000000000000000000000000..1d03bf819cecbb199805207a7810d00323d1dcd4 --- /dev/null +++ b/libkscreen-5.15.5/backends/fake/fake.h @@ -0,0 +1,62 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef FAKE_BACKEND_H +#define FAKE_BACKEND_H + +#include "abstractbackend.h" + +#include +#include + +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; + mutable KScreen::ConfigPtr mConfig; +}; +Q_DECLARE_LOGGING_CATEGORY(KSCREEN_FAKE) +#endif //FAKE_BACKEND_H diff --git a/libkscreen-5.15.5/backends/fake/parser.cpp b/libkscreen-5.15.5/backends/fake/parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30d6f7a6fc7d0f6ad68ccabdb61864a4f60e4c47 --- /dev/null +++ b/libkscreen-5.15.5/backends/fake/parser.cpp @@ -0,0 +1,258 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "parser.h" +#include "fake.h" + +#include "config.h" +#include "output.h" + +#include +#include +#include +#include +#include +#include +#include + +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; + Q_FOREACH(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(); + Q_FOREACH(const QVariant &mode, prefModes) { + preferredModes.append(mode.toString()); + } + output->setPreferredModes(preferredModes); + map.remove(QStringLiteral("preferredModes")); + + ModeList modelist; + const QVariantList modes = map[QStringLiteral("modes")].toList(); + Q_FOREACH(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 clones; + Q_FOREACH(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")); + } + + auto scale = QStringLiteral("scale"); + if (map.contains(scale)) { + qDebug() << "Scale found:" << map[scale].toReal(); + output->setScale(map[scale].toReal()); + map.remove(scale); + } + + //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; +} diff --git a/libkscreen-5.15.5/backends/fake/parser.h b/libkscreen-5.15.5/backends/fake/parser.h new file mode 100644 index 0000000000000000000000000000000000000000..2b8ae45a4c8720e005d7b8720d07bd22422b7563 --- /dev/null +++ b/libkscreen-5.15.5/backends/fake/parser.h @@ -0,0 +1,49 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include +#include +#include + +#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& data); + static KScreen::OutputPtr outputFromJson(QMap 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 diff --git a/libkscreen-5.15.5/backends/kwayland/CMakeLists.txt b/libkscreen-5.15.5/backends/kwayland/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d240c3603be2779d5a2a0f2a1fabd09c5530433 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/CMakeLists.txt @@ -0,0 +1,20 @@ + +set(wayland_SRCS + waylandbackend.cpp + waylandconfig.cpp + waylandoutput.cpp + waylandscreen.cpp + ../utils.cpp +) + +add_library(KSC_KWayland MODULE ${wayland_SRCS}) + +set_target_properties(KSC_KWayland PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kscreen") +set_target_properties(KSC_KWayland PROPERTIES PREFIX "") +target_link_libraries(KSC_KWayland Qt5::Core + Qt5::Gui + KF5::Screen + KF5::WaylandClient +) + +install(TARGETS KSC_KWayland DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kscreen/) diff --git a/libkscreen-5.15.5/backends/kwayland/README.md b/libkscreen-5.15.5/backends/kwayland/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0230e9bb99f2048a900efd65a6c3ba332a60be32 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/README.md @@ -0,0 +1,49 @@ +# 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. + + + diff --git a/libkscreen-5.15.5/backends/kwayland/waylandbackend.cpp b/libkscreen-5.15.5/backends/kwayland/waylandbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3fb6196856e97ec90e02ea034324dcbb35896178 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandbackend.cpp @@ -0,0 +1,96 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandbackend.h" +#include "waylandconfig.h" +#include "waylandoutput.h" + +#include +#include + +#include +#include + +using namespace KScreen; + +Q_LOGGING_CATEGORY(KSCREEN_WAYLAND, "kscreen.kwayland") + + +WaylandBackend::WaylandBackend() + : KScreen::AbstractBackend() + , m_isValid(true) + , m_config(nullptr) + , m_internalConfig(new WaylandConfig(this)) +{ + qCDebug(KSCREEN_WAYLAND) << "Loading Wayland backend."; + m_internalConfig = new WaylandConfig(this); + m_config = m_internalConfig->toKScreenConfig(); + connect(m_internalConfig, &WaylandConfig::configChanged, + this, &WaylandBackend::emitConfigChanged); +} + +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->toKScreenConfig(); +} + +void WaylandBackend::setConfig(const KScreen::ConfigPtr &newconfig) +{ + if (!newconfig) { + return; + } + m_internalConfig->applyConfig(newconfig); +} + +void WaylandBackend::emitConfigChanged(const KScreen::ConfigPtr &cfg) +{ + Q_EMIT configChanged(cfg); +} + +QByteArray WaylandBackend::edid(int outputId) const +{ + WaylandOutput *output = m_internalConfig->outputMap().value(outputId); + if (!output) { + return QByteArray(); + } + return output->outputDevice()->edid(); +} + +bool WaylandBackend::isValid() const +{ + return m_isValid; +} + +void WaylandBackend::updateConfig(ConfigPtr &config) +{ + Q_ASSERT(config != nullptr); + m_internalConfig->updateKScreenConfig(config); +} diff --git a/libkscreen-5.15.5/backends/kwayland/waylandbackend.h b/libkscreen-5.15.5/backends/kwayland/waylandbackend.h new file mode 100644 index 0000000000000000000000000000000000000000..180ba71feace584c517a4b50a9d6a4e13753ca90 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandbackend.h @@ -0,0 +1,61 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KWAYLAND_BACKEND_H +#define KWAYLAND_BACKEND_H + +#include "abstractbackend.h" + +#include + +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; + + void updateConfig(KScreen::ConfigPtr &config); + +private: + bool m_isValid; + KScreen::ConfigPtr m_config; + WaylandConfig *m_internalConfig; + void emitConfigChanged(const KScreen::ConfigPtr &cfg); +}; + +} // namespace + +Q_DECLARE_LOGGING_CATEGORY(KSCREEN_WAYLAND) + +#endif //KWAYLAND_BACKEND_H diff --git a/libkscreen-5.15.5/backends/kwayland/waylandconfig.cpp b/libkscreen-5.15.5/backends/kwayland/waylandconfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77e5ffe0f109b4be86db632909e3e3bc2d0c31df --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandconfig.cpp @@ -0,0 +1,364 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * Copyright 2013 Martin Gräßlin * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandconfig.h" +#include "waylandoutput.h" +#include "waylandscreen.h" +#include "waylandbackend.h" + +// KWayland +#include +#include +#include +#include +#include +#include + + +// Qt +#include + +#include +#include + + +using namespace KScreen; + + +WaylandConfig::WaylandConfig(QObject *parent) + : QObject(parent) + , m_outputManagement(nullptr) + , m_registryInitialized(false) + , m_blockSignals(true) + , m_newOutputId(0) + , m_kscreenConfig(nullptr) + , m_kscreenPendingConfig(nullptr) + , m_screen(new WaylandScreen(this)) +{ + connect(this, &WaylandConfig::initialized, &m_syncLoop, &QEventLoop::quit); + QTimer::singleShot(1000, this, [this] { + if (m_syncLoop.isRunning()) { + qCWarning(KSCREEN_WAYLAND) << "Connection to Wayland server at socket:" << m_connection->socketName() << "timed out."; + m_syncLoop.quit(); + m_thread->quit(); + m_thread->wait(); + } + }); + initConnection(); + m_syncLoop.exec(); +} + +WaylandConfig::~WaylandConfig() +{ + m_thread->quit(); + m_thread->wait(); + m_syncLoop.quit(); +} + +void WaylandConfig::initConnection() +{ + m_thread = new QThread(this); + //m_queue = new KWayland::Client::EventQueue(this); + m_connection = new KWayland::Client::ConnectionThread; + + connect(m_connection, &KWayland::Client::ConnectionThread::connected, + this, &WaylandConfig::setupRegistry, Qt::QueuedConnection); + + connect(m_connection, &KWayland::Client::ConnectionThread::connectionDied, + this, &WaylandConfig::disconnected, Qt::QueuedConnection); + connect(m_connection, &KWayland::Client::ConnectionThread::failed, this, [this] { + qCWarning(KSCREEN_WAYLAND) << "Failed to connect to Wayland server at socket:" << m_connection->socketName(); + m_syncLoop.quit(); + m_thread->quit(); + m_thread->wait(); + }); + + m_thread->start(); + m_connection->moveToThread(m_thread); + m_connection->initConnection(); + +} + +void WaylandConfig::blockSignals() +{ + Q_ASSERT(m_blockSignals == false); + m_blockSignals = true; +} + +void WaylandConfig::unblockSignals() +{ + Q_ASSERT(m_blockSignals == true); + m_blockSignals = false; +} + +void WaylandConfig::disconnected() +{ + qCWarning(KSCREEN_WAYLAND) << "Wayland disconnected, cleaning up."; + qDeleteAll(m_outputMap); + m_outputMap.clear(); + + // Clean up + if (m_queue) { + delete m_queue; + m_queue = nullptr; + } + + m_connection->deleteLater(); + m_connection = nullptr; + + if (m_thread) { + m_thread->quit(); + if (!m_thread->wait(3000)) { + m_thread->terminate(); + m_thread->wait(); + } + delete m_thread; + m_thread = nullptr; + } + + Q_EMIT configChanged(toKScreenConfig()); + Q_EMIT gone(); +} + +void WaylandConfig::setupRegistry() +{ + m_queue = new KWayland::Client::EventQueue(this); + m_queue->setup(m_connection); + + m_registry = new KWayland::Client::Registry(this); + + connect(m_registry, &KWayland::Client::Registry::outputDeviceAnnounced, + this, &WaylandConfig::addOutput); + connect(m_registry, &KWayland::Client::Registry::outputDeviceRemoved, + this, &WaylandConfig::removeOutput); + + connect(m_registry, &KWayland::Client::Registry::outputManagementAnnounced, + this, [this](quint32 name, quint32 version) { + m_outputManagement = m_registry->createOutputManagement(name, version, m_registry); + checkInitialized(); + } + ); + + connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, + this, [this] { + m_registryInitialized = true; + unblockSignals(); + checkInitialized(); + } + ); + + m_registry->create(m_connection); + m_registry->setEventQueue(m_queue); + m_registry->setup(); +} + +void WaylandConfig::addOutput(quint32 name, quint32 version) +{ + ++m_newOutputId; + quint32 new_id = m_newOutputId; + m_outputIds[name] = new_id; + if (m_outputMap.contains(new_id)) { + return; + } + if (!m_initializingOutputs.contains(name)) { + m_initializingOutputs << name; + } + + auto op = new KWayland::Client::OutputDevice(this); + WaylandOutput *waylandoutput = new WaylandOutput(new_id, this); + waylandoutput->bindOutputDevice(m_registry, op, name, version); + + // finalize: when the output is done, we put it in the known outputs map, + // remove if from the list of initializing outputs, and emit configChanged() + connect(waylandoutput, &WaylandOutput::complete, this, [this, waylandoutput, name]{ + + m_outputMap.insert(waylandoutput->id(), waylandoutput); + m_initializingOutputs.removeAll(name); + checkInitialized(); + + if (!m_blockSignals && m_initializingOutputs.empty()) { + m_screen->setOutputs(m_outputMap.values()); + Q_EMIT configChanged(toKScreenConfig()); + } + connect(waylandoutput, &WaylandOutput::changed, this, [this]() { + if (!m_blockSignals) { + Q_EMIT configChanged(toKScreenConfig()); + } + }); + }); +} + +void WaylandConfig::checkInitialized() +{ + if (!m_blockSignals && m_registryInitialized && + m_initializingOutputs.isEmpty() && m_outputMap.count() && m_outputManagement != nullptr) { + m_screen->setOutputs(m_outputMap.values()); + Q_EMIT initialized(); + } +} + +KScreen::ConfigPtr WaylandConfig::toKScreenConfig() +{ + if (m_kscreenConfig == nullptr) { + m_kscreenConfig = KScreen::ConfigPtr(new Config); + } + m_kscreenConfig->setScreen(m_screen->toKScreenScreen(m_kscreenConfig)); + + updateKScreenConfig(m_kscreenConfig); + return m_kscreenConfig; +} + +void WaylandConfig::removeOutput(quint32 name) +{ + const int kscreen_id = m_outputIds[name]; + auto output = m_outputMap.take(kscreen_id); + m_screen->setOutputs(m_outputMap.values()); + delete output; + if (!m_blockSignals) { + Q_EMIT configChanged(toKScreenConfig()); + } +} + +void WaylandConfig::updateKScreenConfig(KScreen::ConfigPtr &config) const +{ + auto features = Config::Feature::Writable | Config::Feature::PerOutputScaling; + config->setSupportedFeatures(features); + config->setValid(m_connection->display()); + KScreen::ScreenPtr screen = config->screen(); + m_screen->updateKScreenScreen(screen); + + //Removing removed outputs + const KScreen::OutputList outputs = config->outputs(); + Q_FOREACH (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 + KScreen::OutputList kscreenOutputs = config->outputs(); + Q_FOREACH (const auto &output, m_outputMap) { + KScreen::OutputPtr kscreenOutput = kscreenOutputs[output->id()]; + if (!kscreenOutput) { + kscreenOutput = output->toKScreenOutput(); + kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); + } + if (kscreenOutput && m_outputMap.count() == 1) { + kscreenOutput->setPrimary(true); + } else if (m_outputMap.count() > 1) { + // primaryScreen concept doesn't exist in kwayland, so we don't set one + //qCWarning(KSCREEN_WAYLAND) << "Multiple outputs, but no way to figure out the primary one. :/"; + } + output->updateKScreenOutput(kscreenOutput); + } + config->setOutputs(kscreenOutputs); +} + +QMap WaylandConfig::outputMap() const +{ + return m_outputMap; +} + +void WaylandConfig::tryPendingConfig() +{ + if (!m_kscreenPendingConfig) { + return; + } + applyConfig(m_kscreenPendingConfig); + m_kscreenPendingConfig = nullptr; +} + +void WaylandConfig::applyConfig(const KScreen::ConfigPtr &newConfig) +{ + using namespace KWayland::Client; + // Create a new configuration object + auto wlOutputConfiguration = m_outputManagement->createConfiguration(); + bool changed = false; + + if (m_blockSignals) { + /* Last apply still pending, remember new changes and apply afterwards */ + m_kscreenPendingConfig = newConfig; + return; + } + + Q_FOREACH (auto output, newConfig->outputs()) { + auto o_old = m_outputMap[output->id()]; + auto device = o_old->outputDevice(); + Q_ASSERT(o_old != nullptr); + + // enabled? + bool old_enabled = (o_old->outputDevice()->enabled() == OutputDevice::Enablement::Enabled); + if (old_enabled != output->isEnabled()) { + changed = true; + auto _enablement = output->isEnabled() ? OutputDevice::Enablement::Enabled : OutputDevice::Enablement::Disabled; + wlOutputConfiguration->setEnabled(o_old->outputDevice(), _enablement); + } + + // position + if (device->globalPosition() != output->pos()) { + changed = true; + wlOutputConfiguration->setPosition(o_old->outputDevice(), output->pos()); + } + + if (!qFuzzyCompare(device->scaleF(), output->scale())) { + changed = true; + wlOutputConfiguration->setScaleF(o_old->outputDevice(), output->scale()); + } + + // rotation + auto r_current = o_old->toKScreenRotation(device->transform()); + auto r_new = output->rotation(); + if (r_current != r_new) { + changed = true; + wlOutputConfiguration->setTransform(device, o_old->toKWaylandTransform(r_new)); + } + + // mode + int w_currentmodeid = device->currentMode().id; + QString l_newmodeid = output->currentModeId(); + int w_newmodeid = o_old->toKWaylandModeId(l_newmodeid); + if (w_newmodeid != w_currentmodeid) { + changed = true; + wlOutputConfiguration->setMode(device, w_newmodeid); + } + } + + 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(wlOutputConfiguration, &OutputConfiguration::applied, this, [this, wlOutputConfiguration] { + wlOutputConfiguration->deleteLater(); + unblockSignals(); + Q_EMIT configChanged(toKScreenConfig()); + tryPendingConfig(); + }); + connect(wlOutputConfiguration, &OutputConfiguration::failed, this, [this, wlOutputConfiguration] { + wlOutputConfiguration->deleteLater(); + unblockSignals(); + Q_EMIT configChanged(toKScreenConfig()); + tryPendingConfig(); + }); + blockSignals(); + // Now ask the compositor to apply the changes + wlOutputConfiguration->apply(); +} diff --git a/libkscreen-5.15.5/backends/kwayland/waylandconfig.h b/libkscreen-5.15.5/backends/kwayland/waylandconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..eb9e8681b7a2e252c1343ed6233c84c938901dd8 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandconfig.h @@ -0,0 +1,122 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_WAYLAND_CONFIG_H +#define KSCREEN_WAYLAND_CONFIG_H + +#include "abstractbackend.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + + +namespace KWayland { + namespace Client { + class ConnectionThread; + class EventQueue; + class OutputDevice; + class Registry; + class OutputManagement; + } +} + +namespace KScreen +{ +class Output; +class WaylandOutput; +class WaylandScreen; + +/** + * @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 toKScreenConfig(); + void updateKScreenConfig(KScreen::ConfigPtr &config) const; + + QMap outputMap() const; + + void addOutput(quint32 name, quint32 version); + void removeOutput(quint32 name); + + void applyConfig(const KScreen::ConfigPtr &newConfig); + +Q_SIGNALS: + void configChanged(const KScreen::ConfigPtr &config); + void initialized(); + void gone(); + +private Q_SLOTS: + void setupRegistry(); + void checkInitialized(); + void disconnected(); + +private: + void initConnection(); + void blockSignals(); + void unblockSignals(); + void tryPendingConfig(); + + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::EventQueue *m_queue; + QThread *m_thread; + + KWayland::Client::Registry *m_registry; + KWayland::Client::OutputManagement *m_outputManagement; + + QMap m_outputMap; + // Map between kwayland's outputdevice names and kscreen output ids + // key: wayland's name, value: kscreen id + QMap m_outputIds; + QList m_initializingOutputs; + bool m_registryInitialized; + int m_lastOutputId = -1; + bool m_blockSignals; + QEventLoop m_syncLoop; + int m_newOutputId; + KScreen::ConfigPtr m_kscreenConfig; + KScreen::ConfigPtr m_kscreenPendingConfig; + WaylandScreen *m_screen; + +}; + +} // namespace + +#endif // KSCREEN_WAYLAND_CONFIG_H diff --git a/libkscreen-5.15.5/backends/kwayland/waylandoutput.cpp b/libkscreen-5.15.5/backends/kwayland/waylandoutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c205e94d3e71849533557560d78b8ca361469a4f --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandoutput.cpp @@ -0,0 +1,198 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandoutput.h" +#include "waylandbackend.h" +#include "waylandconfig.h" +#include "../utils.h" + +#include +#include + +#include +#include + + +using namespace KScreen; + +WaylandOutput::WaylandOutput(quint32 id, WaylandConfig *parent) + : QObject(parent) + , m_id(id) + , m_output(nullptr) +{ + m_rotationMap = { + {KWayland::Client::OutputDevice::Transform::Normal, KScreen::Output::None}, + {KWayland::Client::OutputDevice::Transform::Rotated90, KScreen::Output::Right}, + {KWayland::Client::OutputDevice::Transform::Rotated180, KScreen::Output::Inverted}, + {KWayland::Client::OutputDevice::Transform::Rotated270, KScreen::Output::Left}, + {KWayland::Client::OutputDevice::Transform::Flipped, KScreen::Output::None}, + {KWayland::Client::OutputDevice::Transform::Flipped90, KScreen::Output::Right}, + {KWayland::Client::OutputDevice::Transform::Flipped180, KScreen::Output::Inverted}, + {KWayland::Client::OutputDevice::Transform::Flipped270, KScreen::Output::Left} + }; +} + +KScreen::Output::Rotation WaylandOutput::toKScreenRotation(const KWayland::Client::OutputDevice::Transform transform) const +{ + auto it = m_rotationMap.constFind(transform); + return it.value(); +} + +KWayland::Client::OutputDevice::Transform WaylandOutput::toKWaylandTransform(const KScreen::Output::Rotation rotation) const +{ + return m_rotationMap.key(rotation); +} + +QString WaylandOutput::toKScreenModeId(int kwaylandmodeid) const +{ + auto it = std::find(m_modeIdMap.constBegin(), m_modeIdMap.constEnd(), kwaylandmodeid); + if (it == m_modeIdMap.constEnd()) { + qCWarning(KSCREEN_WAYLAND) << "Invalid kwayland mode id:" << kwaylandmodeid << m_modeIdMap; + return QStringLiteral("invalid_mode_id"); + } + return it.key(); +} + +int WaylandOutput::toKWaylandModeId(const QString &kscreenmodeid) const +{ + if (!m_modeIdMap.contains(kscreenmodeid)) { + qCWarning(KSCREEN_WAYLAND) << "Invalid kscreen mode id:" << kscreenmodeid << m_modeIdMap; + } + return m_modeIdMap.value(kscreenmodeid, -1); +} + +WaylandOutput::~WaylandOutput() +{ +} + +quint32 WaylandOutput::id() const +{ + Q_ASSERT(m_output); + return m_id; +} + +bool WaylandOutput::enabled() const +{ + return m_output != nullptr; +} + +KWayland::Client::OutputDevice* WaylandOutput::outputDevice() const +{ + return m_output; +} + +void WaylandOutput::bindOutputDevice(KWayland::Client::Registry* registry, KWayland::Client::OutputDevice* op, quint32 name, quint32 version) +{ + if (m_output == op) { + return; + } + m_output = op; + + connect(m_output, &KWayland::Client::OutputDevice::done, this, [this]() { + Q_EMIT complete(); + connect(m_output, &KWayland::Client::OutputDevice::changed, + this, &WaylandOutput::changed); + + }); + + m_output->setup(registry->bindOutputDevice(name, version)); +} + +KScreen::OutputPtr WaylandOutput::toKScreenOutput() +{ + KScreen::OutputPtr output(new KScreen::Output()); + output->setId(m_id); + updateKScreenOutput(output); + return output; +} + +void WaylandOutput::updateKScreenOutput(KScreen::OutputPtr &output) +{ + // Initialize primary output + output->setId(m_id); + output->setEnabled(m_output->enabled() == KWayland::Client::OutputDevice::Enablement::Enabled); + output->setConnected(true); + output->setPrimary(true); // FIXME: wayland doesn't have the concept of a primary display + output->setName(name()); + // Physical size + output->setSizeMm(m_output->physicalSize()); + output->setPos(m_output->globalPosition()); + output->setRotation(m_rotationMap[m_output->transform()]); + KScreen::ModeList modeList; + QStringList preferredModeIds; + m_modeIdMap.clear(); + QString currentModeId = QStringLiteral("-1"); + Q_FOREACH (const KWayland::Client::OutputDevice::Mode &m, m_output->modes()) { + KScreen::ModePtr mode(new KScreen::Mode()); + const QString modename = modeName(m); + QString modeid = QString::number(m.id); + if (modeid.isEmpty()) { + qCDebug(KSCREEN_WAYLAND) << "Could not create mode id from" << m.id << ", using" << modename << "instead."; + modeid = modename; + } + if (m_modeIdMap.contains(modeid)) { + qCWarning(KSCREEN_WAYLAND) << "Mode id already in use:" << modeid; + } + + mode->setId(modeid); + // KWayland gives the refresh rate as int in mHz + mode->setRefreshRate(m.refreshRate / 1000.0); + mode->setSize(m.size); + mode->setName(modename); + if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Current)) { + currentModeId = modeid; + } + if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Preferred)) { + preferredModeIds << modeid; + } + // Update the kscreen => kwayland mode id translation map + m_modeIdMap.insert(modeid, m.id); + // Add to the modelist which gets set on the output + modeList[modeid] = mode; + } + if (currentModeId == QLatin1String("-1")) { + qCWarning(KSCREEN_WAYLAND) << "Could not find the current mode id" << modeList; + } + output->setCurrentModeId(currentModeId); + + output->setPreferredModes(preferredModeIds); + output->setModes(modeList); + output->setScale(m_output->scale()); + output->setType(Utils::guessOutputType(m_output->model(), m_output->model())); +} + +QString WaylandOutput::modeName(const KWayland::Client::OutputDevice::Mode &m) const +{ + return QString::number(m.size.width()) + QLatin1Char('x') + + QString::number(m.size.height()) + QLatin1Char('@') + + QString::number(qRound(m.refreshRate/1000.0)); +} + +QString WaylandOutput::name() const +{ + Q_ASSERT(m_output); + return QStringLiteral("%1 %2").arg(m_output->manufacturer(), m_output->model()); +} + +QDebug operator<<(QDebug dbg, const WaylandOutput *output) +{ + dbg << "WaylandOutput(Id:" << output->id() <<", Name:" << \ + QString(output->outputDevice()->manufacturer() + QLatin1Char(' ') + \ + output->outputDevice()->model()) << ")"; + return dbg; +} diff --git a/libkscreen-5.15.5/backends/kwayland/waylandoutput.h b/libkscreen-5.15.5/backends/kwayland/waylandoutput.h new file mode 100644 index 0000000000000000000000000000000000000000..75d31642ba56037cc2b6dcdcb3ed855739a01cd8 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandoutput.h @@ -0,0 +1,90 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_WAYLAND_OUTPUT_H +#define KSCREEN_WAYLAND_OUTPUT_H + +// libkscreen +#include "abstractbackend.h" +#include "config.h" +#include "output.h" + +// Own +#include "waylandconfig.h" + +#include +#include +#include +#include +#include + + +namespace KScreen +{ + +class WaylandOutput : public QObject +{ + Q_OBJECT + +public: + ~WaylandOutput() override; + + KScreen::OutputPtr toKScreenOutput(); + void updateKScreenOutput(KScreen::OutputPtr &output); + + quint32 id() const; + + bool enabled() const; + + KWayland::Client::OutputDevice* outputDevice() const; + void bindOutputDevice(KWayland::Client::Registry *registry, KWayland::Client::OutputDevice *op, quint32 name, quint32 version); + + QString name() const; + + // translation methods + KScreen::Output::Rotation toKScreenRotation(const KWayland::Client::OutputDevice::Transform transform) const; + KWayland::Client::OutputDevice::Transform toKWaylandTransform(const KScreen::Output::Rotation rotation) const; + + QString toKScreenModeId(int kwaylandmodeid) const; + int toKWaylandModeId(const QString &kscreenmodeid) const; + +Q_SIGNALS: + void complete(); + + // only emitted after complete() + void changed(); + +private: + friend WaylandConfig; + explicit WaylandOutput(quint32 id, WaylandConfig *parent = nullptr); + void showOutput(); + QString modeName(const KWayland::Client::OutputDevice::Mode &m) const; + + quint32 m_id; + KWayland::Client::OutputDevice* m_output; + KWayland::Client::Registry* m_registry; + + QMap m_rotationMap; + QMap m_modeIdMap; // left-hand-side: KScreen::Mode, right-hand-side: kwayland's mode.id +}; + +} // namespace + +KSCREEN_EXPORT QDebug operator<<(QDebug dbg, const KScreen::WaylandOutput *output); + +#endif diff --git a/libkscreen-5.15.5/backends/kwayland/waylandscreen.cpp b/libkscreen-5.15.5/backends/kwayland/waylandscreen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e64beedf7c389845618dd1d37f5a30d29292446 --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandscreen.cpp @@ -0,0 +1,65 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandconfig.h" +#include "waylandscreen.h" +#include "waylandoutput.h" + +#include + + +using namespace KScreen; + +WaylandScreen::WaylandScreen(WaylandConfig *config) + : QObject(config) + , m_outputCount(0) +{ +} + +WaylandScreen::~WaylandScreen() +{ +} + +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 &outputs) +{ + m_outputCount = outputs.count(); + QRect r; + Q_FOREACH (auto o, outputs) { + if (o->enabled()) { + r |= QRect(o->outputDevice()->globalPosition(), o->outputDevice()->pixelSize() / o->outputDevice()->scale()); + } + } + m_size = r.size(); +} + +void WaylandScreen::updateKScreenScreen(KScreen::ScreenPtr &screen) const +{ + screen->setMinSize(QSize(0, 0)); + screen->setMaxSize(QSize(64000, 64000)); // 64000^2 should be enough for everyone. + screen->setCurrentSize(m_size); + screen->setMaxActiveOutputsCount(m_outputCount); +} + diff --git a/libkscreen-5.15.5/backends/kwayland/waylandscreen.h b/libkscreen-5.15.5/backends/kwayland/waylandscreen.h new file mode 100644 index 0000000000000000000000000000000000000000..33ce9251c01a98ae4aa1a66da49725d32921954f --- /dev/null +++ b/libkscreen-5.15.5/backends/kwayland/waylandscreen.h @@ -0,0 +1,56 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_WAYLAND_SCREEN_H +#define KSCREEN_WAYLAND_SCREEN_H + +#include "abstractbackend.h" +#include "config.h" +#include "screen.h" + +#include +#include + +namespace KScreen +{ +class WaylandConfig; +class WaylandOutput; + +class WaylandScreen : public QObject +{ + Q_OBJECT + +public: + explicit WaylandScreen(WaylandConfig *config); + ~WaylandScreen() override; + + KScreen::ScreenPtr toKScreenScreen(KScreen::ConfigPtr &parent) const; + void updateKScreenScreen(KScreen::ScreenPtr &screen) const; + void setOutputs(const QList &outputs); + + void setSize(const QSize &size); + void setOutputCount(int count); + +private: + QSize m_size; + int m_outputCount; +}; + +} // namespace + +#endif // KSCREEN_WAYLAND_SCREEN_H diff --git a/libkscreen-5.15.5/backends/qscreen/CMakeLists.txt b/libkscreen-5.15.5/backends/qscreen/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..acd52f411cc6e51dd45e0d6553f8f9081d362f96 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories(${CMAKE_SOURCE_DIR}/src + ${CMAKE_BUILD_DIR} +) + +set(qscreen_SRCS + qscreenbackend.cpp + qscreenconfig.cpp + qscreenscreen.cpp + qscreenoutput.cpp +) + +add_library(KSC_QScreen MODULE ${qscreen_SRCS}) + +set_target_properties(KSC_QScreen PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kscreen") +set_target_properties(KSC_QScreen PROPERTIES PREFIX "") +target_link_libraries(KSC_QScreen Qt5::Core + Qt5::Gui + Qt5::X11Extras + KF5::Screen +) + +install(TARGETS KSC_QScreen DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kscreen/) diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenbackend.cpp b/libkscreen-5.15.5/backends/qscreen/qscreenbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0de6fc5a561ec8642ed698c35cb0d411c91389e9 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenbackend.cpp @@ -0,0 +1,76 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "qscreenbackend.h" +#include "qscreenconfig.h" +#include "qscreenoutput.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; +} diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenbackend.h b/libkscreen-5.15.5/backends/qscreen/qscreenbackend.h new file mode 100644 index 0000000000000000000000000000000000000000..18f1fec8e5214af5f8b092530b27b5b6c4977dee --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenbackend.h @@ -0,0 +1,56 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef QSCREEN_BACKEND_H +#define QSCREEN_BACKEND_H + +#include "abstractbackend.h" + +#include + +namespace KScreen +{ +class Output; +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 diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenconfig.cpp b/libkscreen-5.15.5/backends/qscreen/qscreenconfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b689cf2e5364a7dd2bc30719d21fa466e10d0aee --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenconfig.cpp @@ -0,0 +1,131 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "qscreenconfig.h" +#include "qscreenoutput.h" +#include "qscreenscreen.h" +#include "qscreenbackend.h" + +#include + +#include +#include +#include + +using namespace KScreen; + +QScreenConfig::QScreenConfig(QObject *parent) + : QObject(parent) + , m_screen(new QScreenScreen(this)) + , m_blockSignals(true) +{ + foreach(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) +{ + QList ids; + foreach(auto output, 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; + foreach(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(); + Q_FOREACH(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 + KScreen::OutputList kscreenOutputs = config->outputs(); + foreach(QScreenOutput *output, m_outputMap) { + KScreen::OutputPtr kscreenOutput = kscreenOutputs[output->id()]; + + if (!kscreenOutput) { + kscreenOutput = output->toKScreenOutput(); + kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); + } + output->updateKScreenOutput(kscreenOutput); + if (QGuiApplication::primaryScreen() == output->qscreen()) { + config->setPrimaryOutput(kscreenOutput); + } + } + config->setOutputs(kscreenOutputs); +} + +QMap< int, QScreenOutput * > QScreenConfig::outputMap() const +{ + return m_outputMap; +} diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenconfig.h b/libkscreen-5.15.5/backends/qscreen/qscreenconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..f66011004775ea9fcd97faf5e8b90ecc0dfd24d1 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenconfig.h @@ -0,0 +1,62 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef QSCREEN_CONFIG_H +#define QSCREEN_CONFIG_H + +#include "config.h" + +#include + +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 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 m_outputMap; + QScreenScreen *m_screen; + int m_lastOutputId = -1; + bool m_blockSignals; +}; + +} // namespace + +#endif // QSCREEN_CONFIG_H diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenoutput.cpp b/libkscreen-5.15.5/backends/qscreen/qscreenoutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0db0a0ea3aaa5f8f4bf3cb70f0878b358ccdd862 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenoutput.cpp @@ -0,0 +1,111 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "qscreenoutput.h" +#include "qscreenbackend.h" + +#include +#include + +#include +#include + +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); + output->setPrimary(QGuiApplication::primaryScreen() == m_qscreen); + + // 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); +} diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenoutput.h b/libkscreen-5.15.5/backends/qscreen/qscreenoutput.h new file mode 100644 index 0000000000000000000000000000000000000000..11ef619e58815f7ad815daaa43f67cf84ae132e7 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenoutput.h @@ -0,0 +1,57 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef QSCREEN_OUTPUT_H +#define QSCREEN_OUTPUT_H + +#include "qscreenconfig.h" + +#include "config.h" +#include "output.h" +#include "edid.h" + +#include + +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: + void updateFromQScreen(const QScreen *qscreen); + const QScreen *m_qscreen; + int m_id; +}; + +} // namespace + +#endif diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenscreen.cpp b/libkscreen-5.15.5/backends/qscreen/qscreenscreen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..961c7cc84cff5a2b23330a0154add19b3f82c60f --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenscreen.cpp @@ -0,0 +1,65 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "qscreenbackend.h" +#include "qscreenscreen.h" +#include "qscreenoutput.h" + +#include +#include + +#include +#include + +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()); + } +} diff --git a/libkscreen-5.15.5/backends/qscreen/qscreenscreen.h b/libkscreen-5.15.5/backends/qscreen/qscreenscreen.h new file mode 100644 index 0000000000000000000000000000000000000000..38dd54bf3665a93a07728ccca1de4dda5601d400 --- /dev/null +++ b/libkscreen-5.15.5/backends/qscreen/qscreenscreen.h @@ -0,0 +1,48 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef QSCREEN_SCREEN_H +#define QSCREEN_SCREEN_H + +#include "config.h" +#include "screen.h" + +#include +#include +#include + +namespace KScreen +{ +class Output; +class QScreenOutput; + +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 diff --git a/libkscreen-5.15.5/backends/utils.cpp b/libkscreen-5.15.5/backends/utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..80cb0f384bd5de289eb4735df9cf224dc7e2438b --- /dev/null +++ b/libkscreen-5.15.5/backends/utils.cpp @@ -0,0 +1,70 @@ +/************************************************************************************* + * Copyright 2018 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "utils.h" + +#include + +KScreen::Output::Type Utils::guessOutputType(const QString &type, const QString &name) +{ + static const auto embedded = { QLatin1String("LVDS"), + QLatin1String("IDP"), + QLatin1String("EDP"), + QLatin1String("LCD") }; + + 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; + } +} + diff --git a/libkscreen-5.15.5/backends/utils.h b/libkscreen-5.15.5/backends/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..791cf41b328ee3ba3af913bf12651b37e72fcdfc --- /dev/null +++ b/libkscreen-5.15.5/backends/utils.h @@ -0,0 +1,30 @@ +/************************************************************************************* + * Copyright 2018 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_BACKEND_UTILS_H_ +#define KSCREEN_BACKEND_UTILS_H_ + +#include + +namespace Utils { + +KScreen::Output::Type guessOutputType(const QString &type, const QString &name); + +} + +#endif diff --git a/libkscreen-5.15.5/backends/xcbeventlistener.cpp b/libkscreen-5.15.5/backends/xcbeventlistener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3930b784f0c19d414cf6ae6b9e75d30f90e7fe7d --- /dev/null +++ b/libkscreen-5.15.5/backends/xcbeventlistener.cpp @@ -0,0 +1,207 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xcbeventlistener.h" + +#include +#include + +#include + +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); +} + +bool XCBEventListener::nativeEventFilter(const QByteArray& eventType, void* message, long int* result) +{ + Q_UNUSED(result); + + if (eventType != "xcb_generic_event_t") { + return false; + } + + auto *e = static_cast(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(e); + + // Only accept notifications for our window + if (e2->request_window != m_window) { + return; + } + + qCDebug(KSCREEN_XCB_HELPER) << "RRScreenChangeNotify"; + 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(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) << "\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)); + + } 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) << "\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 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) << "\tOutput: " << property.output; + qCDebug(KSCREEN_XCB_HELPER) << "\tProperty: " << xcb_get_atom_name_name(reply.data()); + qCDebug(KSCREEN_XCB_HELPER) << "\tState (newValue, Deleted): " << property.status; + } +} diff --git a/libkscreen-5.15.5/backends/xcbeventlistener.h b/libkscreen-5.15.5/backends/xcbeventlistener.h new file mode 100644 index 0000000000000000000000000000000000000000..7df4a38eca7d067ce46ae0161394073ee46dd160 --- /dev/null +++ b/libkscreen-5.15.5/backends/xcbeventlistener.h @@ -0,0 +1,77 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDRX11HELPER_H +#define XRANDRX11HELPER_H + +#include +#include +#include +#include + +#include "xcbwrapper.h" + +class XCBEventListener : public QObject, + public QAbstractNativeEventFilter +{ + Q_OBJECT + + public: + XCBEventListener(); + ~XCBEventListener() override; + + bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override; + + 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); + 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 diff --git a/libkscreen-5.15.5/backends/xcbwrapper.cpp b/libkscreen-5.15.5/backends/xcbwrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b65ec9107a1dd0e209708080ec8ba1d31ad16920 --- /dev/null +++ b/libkscreen-5.15.5/backends/xcbwrapper.cpp @@ -0,0 +1,67 @@ +/******************************************************************** + K Win - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2012, 2013 Martin Gräßlin +Copyright (C) 2015 Daniel Vrátil + +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, see . +*********************************************************************/ + +#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()); +} diff --git a/libkscreen-5.15.5/backends/xcbwrapper.h b/libkscreen-5.15.5/backends/xcbwrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..150c58f9d3188a7496874ecf26a86052aaca0ca4 --- /dev/null +++ b/libkscreen-5.15.5/backends/xcbwrapper.h @@ -0,0 +1,211 @@ +/******************************************************************** + K Win - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2012, 2013 Martin Gräßlin +Copyright (C) 2015 Daniel Vrátil + +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, see . +*********************************************************************/ + +#ifndef XCB_WRAPPER_H +#define XCB_WRAPPER_H + +#include +#include + +#include +#include + +#include +#include + +namespace XCB +{ + +template +using ScopedPointer = QScopedPointer; + +xcb_connection_t *connection(); +void closeConnection(); +xcb_screen_t *screenOfDisplay(xcb_connection_t *c, int screen); + +struct GrabServer +{ + GrabServer(); + ~GrabServer(); +}; + +template +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(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(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(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 + constexpr xcb_window_t requestWindow(const Args & ... args) const { + return std::is_same>::type, xcb_window_t>::value + ? std::get<0>(std::tuple(args ...)) + : static_cast(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 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 diff --git a/libkscreen-5.15.5/backends/xrandr/CMakeLists.txt b/libkscreen-5.15.5/backends/xrandr/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..226861f7f0e84541b8c722e7e3f03c23e2e9bb21 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/CMakeLists.txt @@ -0,0 +1,28 @@ +include_directories(${CMAKE_SOURCE_DIR}/src + ${CMAKE_BUILD_DIR} +) + +set(xrandr_SRCS + xrandr.cpp + xrandrconfig.cpp + xrandrcrtc.cpp + xrandroutput.cpp + xrandrmode.cpp + xrandrscreen.cpp + ../xcbwrapper.cpp + ../xcbeventlistener.cpp + ../utils.cpp +) + +add_library(KSC_XRandR MODULE ${xrandr_SRCS}) + +set_target_properties(KSC_XRandR PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kscreen") +set_target_properties(KSC_XRandR PROPERTIES PREFIX "") +target_link_libraries(KSC_XRandR Qt5::Core + Qt5::Gui + Qt5::X11Extras + ${XCB_LIBRARIES} + KF5::Screen +) + +install(TARGETS KSC_XRandR DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kscreen/) diff --git a/libkscreen-5.15.5/backends/xrandr/xrandr.cpp b/libkscreen-5.15.5/backends/xrandr/xrandr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..955024aa8a29dc96a47a4fe54bb2f7b73cde1f83 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandr.cpp @@ -0,0 +1,323 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandr.h" +#include "xrandrconfig.h" +#include "xrandrscreen.h" +#include "../xcbwrapper.h" +#include "../xcbeventlistener.h" + +#include "config.h" +#include "output.h" +#include "edid.h" + +#include +#include +#include +#include +#include + +#include +#include + +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"); + qRegisterMetaType("xcb_randr_crtc_t"); + qRegisterMetaType("xcb_randr_mode_t"); + qRegisterMetaType("xcb_randr_connection_t"); + qRegisterMetaType("xcb_randr_rotation_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) +{ + XRandROutput *xOutput = s_internalConfig->output(output); + XCB::PrimaryOutput primary(XRandR::rootWindow()); + if (!xOutput) { + s_internalConfig->addNewOutput(output); + } else { + switch (crtc == XCB_NONE && mode == XCB_NONE && connection == XCB_RANDR_CONNECTION_DISCONNECTED) { + case true: { + XCB::OutputInfo info(output, XCB_TIME_CURRENT_TIME); + if (info.isNull()) { + s_internalConfig->removeOutput(output); + qCDebug(KSCREEN_XRANDR) << "Output" << output << " removed"; + break; + } + // info is valid: fall-through + Q_FALLTHROUGH(); + } + case false: { + xOutput->update(crtc, mode, connection, (primary->output == output)); + qCDebug(KSCREEN_XRANDR) << "Output" << xOutput->id() << ": connected =" << xOutput->isConnected() << ", enabled =" << xOutput->isEnabled(); + break; + } + } // switch + } + + m_configChangeCompressor->start(); +} + +void XRandR::crtcChanged(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, + xcb_randr_rotation_t rotation, const QRect& geom) +{ + XRandRCrtc *xCrtc = s_internalConfig->crtc(crtc); + if (!xCrtc) { + s_internalConfig->addNewCrtc(crtc); + } else { + xCrtc->update(mode, rotation, geom); + } + + 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(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_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; +} diff --git a/libkscreen-5.15.5/backends/xrandr/xrandr.h b/libkscreen-5.15.5/backends/xrandr/xrandr.h new file mode 100644 index 0000000000000000000000000000000000000000..213f541d03184b06a55f7d3866cd11f51dc7de15 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandr.h @@ -0,0 +1,96 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDR_BACKEND_H +#define XRANDR_BACKEND_H + +#include "abstractbackend.h" + +#include +#include + +#include "../xcbwrapper.h" + +class QRect; +class QTimer; + +class XCBEventListener; +class XRandRConfig; +namespace KScreen { + class Output; +} + +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 Q_SLOTS: + 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); + void screenChanged(xcb_randr_rotation_t rotation, + const QSize &sizePx, + const QSize &sizeMm); + + private: + 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) +#endif //XRandR_BACKEND_H diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrconfig.cpp b/libkscreen-5.15.5/backends/xrandr/xrandrconfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccc8ac83aa1fff093f803731d923d4e89056d314 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrconfig.cpp @@ -0,0 +1,586 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012 - 2015 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandrconfig.h" +#include "xrandrscreen.h" +#include "xrandr.h" +#include "xrandrmode.h" +#include "xrandroutput.h" +#include "xrandrcrtc.h" +#include "config.h" +#include "output.h" +#include "edid.h" + +#include "../xcbwrapper.h" + +#include +#include +#include + +using namespace KScreen; + +XRandRConfig::XRandRConfig() + : QObject() + , m_screen(nullptr) +{ + m_screen = new XRandRScreen(this); + + XCB::ScopedPointer resources(XRandR::screenResources()); + xcb_randr_crtc_t *crtcs = xcb_randr_get_screen_resources_crtcs(resources.data()); + for (int i = 0, c = xcb_randr_get_screen_resources_crtcs_length(resources.data()); i < c; ++i) { + addNewCrtc(crtcs[i]); + } + + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources.data()); + for (int i = 0, c = xcb_randr_get_screen_resources_outputs_length(resources.data()); i < c; ++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); + auto features = Config::Feature::Writable | Config::Feature::PrimaryDisplay; + config->setSupportedFeatures(features); + KScreen::OutputList kscreenOutputs; + + for (auto iter = m_outputs.constBegin(); iter != m_outputs.constEnd(); ++iter) { + KScreen::OutputPtr kscreenOutput = (*iter)->toKScreenOutput(); + kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); + } + config->setOutputs(kscreenOutputs); + config->setScreen(m_screen->toKScreenScreen()); + + return config; +} + +void XRandRConfig::applyKScreenConfig(const KScreen::ConfigPtr &config) +{ + 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; + xcb_randr_output_t primaryOutput = 0; + xcb_randr_output_t oldPrimaryOutput = 0; + + Q_FOREACH (const XRandROutput *xrandrOutput, m_outputs) { + if (xrandrOutput->isPrimary()) { + oldPrimaryOutput = xrandrOutput->id(); + break; + } + } + + KScreen::OutputList toDisable, toEnable, toChange; + Q_FOREACH(const KScreen::OutputPtr &kscreenOutput, kscreenOutputs) { + xcb_randr_output_t outputId = kscreenOutput->id(); + XRandROutput *currentOutput = output(outputId); + //Only set the output as primary if it is enabled. + if (kscreenOutput->isPrimary() && kscreenOutput->isEnabled()) { + primaryOutput = 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); + } + } + + 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(); +// qDebug() << kRealBacktrace(256); + printConfig(config); + printInternalCond(); + continue; + } + + // If the output would not fit into new screen size, we need to disable + // it 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); + } + } + } // Q_FOREACH (const KScreen::OutputPtr &kscreenOutput, kscreenOutputs) + + 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 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:"; + qCDebug(KSCREEN_XRANDR) << "\tPrimary Output:" << (primaryOutput != oldPrimaryOutput); + if (primaryOutput != oldPrimaryOutput) { + qCDebug(KSCREEN_XRANDR) << "\t\tOld:" << oldPrimaryOutput; + qCDebug(KSCREEN_XRANDR) << "\t\tNew:" << primaryOutput; + } + qCDebug(KSCREEN_XRANDR) << "\tChange Screen Size:" << (newScreenSize != currentScreenSize); + if (newScreenSize != currentScreenSize) { + qCDebug(KSCREEN_XRANDR) << "\t\tOld:" << currentScreenSize; + qCDebug(KSCREEN_XRANDR) << "\t\tIntermediate:" << intermediateScreenSize; + qCDebug(KSCREEN_XRANDR) << "\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 (oldPrimaryOutput == primaryOutput && toDisable.isEmpty() && toEnable.isEmpty() && toChange.isEmpty()) { + if (newScreenSize != currentScreenSize) { + setScreenSize(newScreenSize); + } + return; + } + + Q_FOREACH(const KScreen::OutputPtr &output, toDisable) { + disableOutput(output); + } + + if (intermediateScreenSize != currentScreenSize) { + setScreenSize(intermediateScreenSize); + } + + bool forceScreenSizeUpdate = false; + Q_FOREACH(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; + } + } + } + + Q_FOREACH(const KScreen::OutputPtr &output, toEnable) { + if (!enableOutput(output)) { + //output->setEnabled(false); + qCDebug(KSCREEN_XRANDR) << "Output failed to be Enabled: " << output->name(); + forceScreenSizeUpdate = true; + } + } + + if (oldPrimaryOutput != primaryOutput) { + setPrimaryOutput(primaryOutput); + } + + 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:"; + qCDebug(KSCREEN_XRANDR) << "\tmaxSize:" << config->screen()->maxSize(); + qCDebug(KSCREEN_XRANDR) << "\tminSize:" << config->screen()->minSize(); + qCDebug(KSCREEN_XRANDR) << "\tcurrentSize:" << config->screen()->currentSize(); + + OutputList outputs = config->outputs(); + Q_FOREACH(const OutputPtr &output, outputs) { + qCDebug(KSCREEN_XRANDR) << "\n-----------------------------------------------------\n"; + qCDebug(KSCREEN_XRANDR) << "Id: " << output->id(); + qCDebug(KSCREEN_XRANDR) << "Name: " << output->name(); + qCDebug(KSCREEN_XRANDR) << "Type: " << output->type(); + qCDebug(KSCREEN_XRANDR) << "Connected: " << output->isConnected(); + if (!output->isConnected()) { + continue; + } + qCDebug(KSCREEN_XRANDR) << "Enabled: " << output->isEnabled(); + qCDebug(KSCREEN_XRANDR) << "Primary: " << output->isPrimary(); + qCDebug(KSCREEN_XRANDR) << "Rotation: " << output->rotation(); + qCDebug(KSCREEN_XRANDR) << "Pos: " << output->pos(); + qCDebug(KSCREEN_XRANDR) << "MMSize: " << output->sizeMm(); + if (output->currentMode()) { + qCDebug(KSCREEN_XRANDR) << "Size: " << output->currentMode()->size(); + } + if (output->clones().isEmpty()) { + qCDebug(KSCREEN_XRANDR) << "Clones: " << "None"; + } else { + qCDebug(KSCREEN_XRANDR) << "Clones: " << output->clones().count(); + } + qCDebug(KSCREEN_XRANDR) << "Mode: " << output->currentModeId(); + qCDebug(KSCREEN_XRANDR) << "Preferred Mode: " << output->preferredModeId(); + qCDebug(KSCREEN_XRANDR) << "Preferred modes: " << output->preferredModes(); + qCDebug(KSCREEN_XRANDR) << "Modes: "; + + ModeList modes = output->modes(); + Q_FOREACH(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(); + qCDebug(KSCREEN_XRANDR) << "\tName: " << edid->name(); + qCDebug(KSCREEN_XRANDR) << "\tVendor: " << edid->vendor(); + qCDebug(KSCREEN_XRANDR) << "\tSerial: " << edid->serial(); + qCDebug(KSCREEN_XRANDR) << "\tEISA ID: " << edid->eisaId(); + qCDebug(KSCREEN_XRANDR) << "\tHash: " << edid->hash(); + qCDebug(KSCREEN_XRANDR) << "\tWidth: " << edid->width(); + qCDebug(KSCREEN_XRANDR) << "\tHeight: " << edid->height(); + qCDebug(KSCREEN_XRANDR) << "\tGamma: " << edid->gamma(); + qCDebug(KSCREEN_XRANDR) << "\tRed: " << edid->red(); + qCDebug(KSCREEN_XRANDR) << "\tGreen: " << edid->green(); + qCDebug(KSCREEN_XRANDR) << "\tBlue: " << edid->blue(); + qCDebug(KSCREEN_XRANDR) << "\tWhite: " << edid->white(); + } else { + qCDebug(KSCREEN_XRANDR) << "\tUnavailable"; + } + } +} + +void XRandRConfig::printInternalCond() const +{ + qCDebug(KSCREEN_XRANDR) << "Internal config in xrandr"; + Q_FOREACH(XRandROutput *output, m_outputs) { + qCDebug(KSCREEN_XRANDR) << "Id: " << output->id(); + qCDebug(KSCREEN_XRANDR) << "Current Mode: " << output->currentMode(); + qCDebug(KSCREEN_XRANDR) << "Current mode id: " << output->currentModeId(); + qCDebug(KSCREEN_XRANDR) << "Connected: " << output->isConnected(); + qCDebug(KSCREEN_XRANDR) << "Enabled: " << output->isEnabled(); + qCDebug(KSCREEN_XRANDR) << "Primary: " << output->isPrimary(); + if (!output->isEnabled()) { + continue; + } + XRandRMode::Map modes = output->modes(); + Q_FOREACH(XRandRMode *mode, modes) { + qCDebug(KSCREEN_XRANDR) << "\t" << mode->id(); + qCDebug(KSCREEN_XRANDR) << "\t" << mode->name(); + qCDebug(KSCREEN_XRANDR) << "\t" << mode->size() << mode->refreshRate(); + } + } +} + +QSize XRandRConfig::screenSize(const KScreen::ConfigPtr &config) const +{ + QRect rect; + Q_FOREACH(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"; + qCDebug(KSCREEN_XRANDR) << "\tDPI:" << dpi; + qCDebug(KSCREEN_XRANDR) << "\tSize:" << size; + qCDebug(KSCREEN_XRANDR) << "\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::setPrimaryOutput(xcb_randr_output_t outputId) const +{ + qCDebug(KSCREEN_XRANDR) << "RRSetOutputPrimary"; + qCDebug(KSCREEN_XRANDR) << "\tNew primary:" << outputId; + xcb_randr_set_output_primary(XCB::connection(), XRandR::rootWindow(), outputId); + + for (XRandROutput *output : m_outputs) { + output->setIsPrimary(output->id() == outputId); + } +} + +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)"; + qCDebug(KSCREEN_XRANDR) << "\tCRTC:" << crtc; + + 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 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, + kscreenOutput->isPrimary()); + } + return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); +} + +bool XRandRConfig::enableOutput(const OutputPtr &kscreenOutput) const +{ + xcb_randr_output_t outputs[1] { static_cast(kscreenOutput->id()) }; + + XRandRCrtc *freeCrtc = nullptr; + qCDebug(KSCREEN_XRANDR) << m_crtcs; + Q_FOREACH (XRandRCrtc *crtc, m_crtcs) { + crtc->update(); + qCDebug(KSCREEN_XRANDR) << "Testing CRTC" << crtc->crtc(); + qCDebug(KSCREEN_XRANDR) << "\tFree:" << crtc->isFree(); + qCDebug(KSCREEN_XRANDR) << "\tMode:" << crtc->mode(); + qCDebug(KSCREEN_XRANDR) << "\tPossible outputs:" << crtc->possibleOutputs(); + qCDebug(KSCREEN_XRANDR) << "\tConnected outputs:" << crtc->outputs(); + qCDebug(KSCREEN_XRANDR) << "\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; + } + + const int modeId = kscreenOutput->currentMode() ? kscreenOutput->currentModeId().toInt() : kscreenOutput->preferredModeId().toInt(); + + qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (enable output)"; + qCDebug(KSCREEN_XRANDR) << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")"; + qCDebug(KSCREEN_XRANDR) << "\tNew CRTC:" << freeCrtc->crtc(); + qCDebug(KSCREEN_XRANDR) << "\tPos:" << kscreenOutput->pos(); + qCDebug(KSCREEN_XRANDR) << "\tMode:" << kscreenOutput->currentMode() << "Preferred:" << kscreenOutput->preferredModeId(); + qCDebug(KSCREEN_XRANDR) << "\tRotation:" << kscreenOutput->rotation(); + + auto cookie = xcb_randr_set_crtc_config(XCB::connection(), freeCrtc->crtc(), + XCB_CURRENT_TIME, XCB_CURRENT_TIME, + kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), + modeId, + kscreenOutput->rotation(), + 1, outputs); + XCB::ScopedPointer reply(xcb_randr_set_crtc_config_reply(XCB::connection(), cookie, nullptr)); + if (!reply) { + qCDebug(KSCREEN_XRANDR) << "Result: unknown (error)"; + return false; + } + qCDebug(KSCREEN_XRANDR) << "\tResult:" << reply->status; + + if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) { + XRandROutput *xOutput = output(kscreenOutput->id()); + xOutput->update(freeCrtc->crtc(), modeId, + XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); + } + return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); +} + +bool XRandRConfig::changeOutput(const 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(); + + qCDebug(KSCREEN_XRANDR) << "RRSetCrtcConfig (change output)"; + qCDebug(KSCREEN_XRANDR) << "\tOutput:" << kscreenOutput->id() << "(" << kscreenOutput->name() << ")"; + qCDebug(KSCREEN_XRANDR) << "\tCRTC:" << xOutput->crtc()->crtc(); + qCDebug(KSCREEN_XRANDR) << "\tPos:" << kscreenOutput->pos(); + qCDebug(KSCREEN_XRANDR) << "\tMode:" << modeId << kscreenOutput->currentMode(); + qCDebug(KSCREEN_XRANDR) << "\tRotation:" << kscreenOutput->rotation(); + + xcb_randr_output_t outputs[1] { static_cast(kscreenOutput->id()) }; + + auto cookie = xcb_randr_set_crtc_config(XCB::connection(), xOutput->crtc()->crtc(), + XCB_CURRENT_TIME, XCB_CURRENT_TIME, + kscreenOutput->pos().rx(), kscreenOutput->pos().ry(), + modeId, + kscreenOutput->rotation(), + 1, outputs); + XCB::ScopedPointer 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; + + if (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS) { + xOutput->update(xOutput->crtc()->crtc(), modeId, + XCB_RANDR_CONNECTION_CONNECTED, kscreenOutput->isPrimary()); + } + return (reply->status == XCB_RANDR_SET_CONFIG_SUCCESS); +} diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrconfig.h b/libkscreen-5.15.5/backends/xrandr/xrandrconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..3d7ec45556379b8d8036a2d3e1a1cd9e982d4603 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrconfig.h @@ -0,0 +1,75 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDRCONFIG_H +#define XRANDRCONFIG_H + +#include + +#include "xrandr.h" +#include "xrandrcrtc.h" +#include "xrandroutput.h" + +class XRandRScreen; +namespace KScreen { +class Config; +} + +class XRandRConfig : public QObject +{ + Q_OBJECT + +public: + explicit 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: + /** + * We need to print stuff to discover the damn bug + * where currentMode is null + */ + void printConfig(const KScreen::ConfigPtr &config) const; + void printInternalCond() const; + QSize screenSize(const KScreen::ConfigPtr &config) const; + bool setScreenSize(const QSize &size) const; + void setPrimaryOutput(xcb_randr_output_t outputId) const; + bool disableOutput(const KScreen::OutputPtr &output) const; + bool enableOutput(const KScreen::OutputPtr &output) const; + bool changeOutput(const KScreen::OutputPtr &output) const; + + XRandROutput::Map m_outputs; + XRandRCrtc::Map m_crtcs; + XRandRScreen *m_screen; +}; + +#endif // XRANDRCONFIG_H diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.cpp b/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b5ff41c7f0bebb3068f7305b5f9149d8138a064 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2015 Daniel Vrátil + * + * 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) version 3 or any later version + * 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 + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "xrandrcrtc.h" +#include "xrandrconfig.h" +#include "xrandr.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) +{ + 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 XRandRCrtc::possibleOutputs() +{ + return m_possibleOutputs; +} + +QVector 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_rotation = (xcb_randr_rotation_t) crtcInfo->rotation; + m_geometry = QRect(crtcInfo->x, crtcInfo->y, crtcInfo->width, crtcInfo->height); + 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_rotation = rotation; + m_geometry = geom; +} + diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.h b/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.h new file mode 100644 index 0000000000000000000000000000000000000000..e08b684b61b8d1454dfe2a89b07a62c293dbdac2 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrcrtc.h @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Daniel Vrátil + * + * 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) version 3 or any later version + * 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 + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef XRANDRCRTC_H +#define XRANDRCRTC_H + +#include +#include +#include +#include + +#include + +class XRandRConfig; + +class XRandRCrtc : public QObject +{ + Q_OBJECT + +public: + typedef QMap Map; + + + XRandRCrtc(xcb_randr_crtc_t crtc, XRandRConfig *config); + + xcb_randr_crtc_t crtc() const; + xcb_randr_mode_t mode() const; + xcb_randr_rotation_t rotation() const; + QRect geometry() const; + QVector possibleOutputs(); + QVector 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); + +private: + xcb_randr_crtc_t m_crtc; + xcb_randr_mode_t m_mode; + xcb_randr_rotation_t m_rotation; + QRect m_geometry; + QVector m_possibleOutputs; + QVector m_outputs; +}; + +#endif // XRANDRCRTC_H diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrmode.cpp b/libkscreen-5.15.5/backends/xrandr/xrandrmode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f7fca9890f07ecd14f0d86f1671e6cd4b122620 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrmode.cpp @@ -0,0 +1,69 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandrmode.h" +#include "xrandroutput.h" +#include "mode.h" +#include "output.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); + m_refreshRate = ((float) modeInfo.dot_clock / ((float) modeInfo.htotal * (float) modeInfo.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; +} diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrmode.h b/libkscreen-5.15.5/backends/xrandr/xrandrmode.h new file mode 100644 index 0000000000000000000000000000000000000000..1ed98b9643b40fc9220bbe77ad0620d469df2e58 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrmode.h @@ -0,0 +1,63 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDRMODE_H +#define XRANDRMODE_H + +#include +#include +#include +#include + +#include "types.h" +#include "../xcbwrapper.h" + +class XRandROutput; +namespace KScreen +{ +class Output; +class Mode; +} + +class XRandRMode : public QObject +{ + Q_OBJECT + +public: + typedef QMap 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) + +#endif // XRANDRMODE_H diff --git a/libkscreen-5.15.5/backends/xrandr/xrandroutput.cpp b/libkscreen-5.15.5/backends/xrandr/xrandroutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b1bb4a79339c1fa50bd7b9006d3ca60dc3c81c9 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandroutput.cpp @@ -0,0 +1,343 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandroutput.h" +#include "xrandrmode.h" +#include "xrandrconfig.h" +#include "xrandr.h" +#include "output.h" +#include "config.h" +#include "../utils.h" + +#include + +Q_DECLARE_METATYPE(QList) + +XRandROutput::XRandROutput(xcb_randr_output_t id, XRandRConfig *config) + : QObject(config) + , m_config(config) + , m_id(id) + , m_type(KScreen::Output::Unknown) + , m_primary(false) + , 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 m_primary; +} + +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(m_crtc ? m_crtc->rotation() : XCB_RANDR_ROTATION_ROTATE_0); +} + +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() +{ + init(); +} + +void XRandROutput::update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn, bool primary) +{ + qCDebug(KSCREEN_XRANDR) << "XRandROutput" << m_id << "update"; + qCDebug(KSCREEN_XRANDR) << "\tm_connected:" << m_connected; + qCDebug(KSCREEN_XRANDR) << "\tm_crtc" << m_crtc; + qCDebug(KSCREEN_XRANDR) << "\tCRTC:" << crtc; + qCDebug(KSCREEN_XRANDR) << "\tMODE:" << mode; + qCDebug(KSCREEN_XRANDR) << "\tConnection:" << conn; + qCDebug(KSCREEN_XRANDR) << "\tPrimary:" << primary; + + // 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"); + } + + // 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); + } + } + + // Primary has changed + m_primary = primary; +} + +void XRandROutput::setIsPrimary(bool primary) +{ + m_primary = primary; +} + + +void XRandROutput::init() +{ + XCB::OutputInfo outputInfo(m_id, XCB_TIME_CURRENT_TIME); + Q_ASSERT(outputInfo); + if (!outputInfo) { + return; + } + + XCB::PrimaryOutput primary(XRandR::rootWindow()); + + 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; + m_primary = (primary->output == m_id); + 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 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; + } + + char *connectorType; + + auto cookie = xcb_randr_get_output_property(XCB::connection(), outputId, atomType->atom, + XCB_ATOM_ANY, 0, 100, false, false); + XCB::ScopedPointer 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(prop)); + if (!atomName) { + return type; + } + + connectorType = xcb_get_atom_name_name(atomName); + if (!connectorType) { + return type; + } + + type = connectorType; + return type; +} + +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); + + //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); + + 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->setPrimary(m_primary); + kscreenOutput->setClones([](const QList &clones) { + QList kclones; + kclones.reserve(clones.size()); + for (xcb_randr_output_t o : clones) { + kclones.append(static_cast(o)); + } + return kclones; + }(m_clones)); + kscreenOutput->setEnabled(isEnabled()); + if (isEnabled()) { + kscreenOutput->setSize(size()); + kscreenOutput->setPos(position()); + kscreenOutput->setRotation(rotation()); + kscreenOutput->setCurrentModeId(currentModeId()); + } + } + + + kscreenOutput->blockSignals(signalsBlocked); + return kscreenOutput; +} diff --git a/libkscreen-5.15.5/backends/xrandr/xrandroutput.h b/libkscreen-5.15.5/backends/xrandr/xrandroutput.h new file mode 100644 index 0000000000000000000000000000000000000000..7b07e88e75bb1ad7b382a8e69e4ff595f368357d --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandroutput.h @@ -0,0 +1,98 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDROUTPUT_H +#define XRANDROUTPUT_H + +#include +#include +#include + +#include "xrandrmode.h" +#include "output.h" +#include "../xcbwrapper.h" + +class XRandRConfig; +class XRandRCrtc; +namespace KScreen +{ +class Config; +class Output; +} + +class XRandROutput : public QObject +{ + Q_OBJECT + +public: + typedef QMap Map; + + explicit XRandROutput(xcb_randr_output_t id, XRandRConfig *config); + ~XRandROutput() override; + + void disabled(); + void disconnected(); + + void update(); + void update(xcb_randr_crtc_t crtc, xcb_randr_mode_t mode, xcb_randr_connection_t conn, bool primary); + + void setIsPrimary(bool primary); + + xcb_randr_output_t id() const; + bool isEnabled() const; + bool isConnected() const; + bool isPrimary() const; + QPoint position() const; + QSize size() 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; + +private: + void init(); + void updateModes(const XCB::OutputInfo &outputInfo); + + static KScreen::Output::Type fetchOutputType(xcb_randr_output_t outputId, const QString &name); + static QByteArray typeFromProperty(xcb_randr_output_t outputId); + + XRandRConfig *m_config; + xcb_randr_output_t m_id; + QString m_name; + xcb_randr_connection_t m_connected; + KScreen::Output::Type m_type; + QString m_icon; + XRandRMode::Map m_modes; + QStringList m_preferredModes; + bool m_primary; + QList m_clones; + mutable QByteArray m_edid; + unsigned int m_widthMm; + unsigned int m_heightMm; + bool m_hotplugModeUpdate = false; + XRandRCrtc *m_crtc; +}; + +Q_DECLARE_METATYPE(XRandROutput::Map) + +#endif // XRANDROUTPUT_H diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrscreen.cpp b/libkscreen-5.15.5/backends/xrandr/xrandrscreen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af3690876153d5f3a2e44d7f4e1aad7dca4791ad --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrscreen.cpp @@ -0,0 +1,75 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2012, 2013 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandrscreen.h" +#include "xrandrconfig.h" +#include "screen.h" +#include "config.h" + +#include + +#include "../xcbwrapper.h" + +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() +{ + xcb_screen_t *screen = XCB::screenOfDisplay(XCB::connection(), QX11Info::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 screenResources(XRandR::screenResources()); + kscreenScreen->setMaxActiveOutputsCount(screenResources->num_crtcs); + + return kscreenScreen; +} + +void XRandRScreen::updateKScreenScreen(KScreen::ScreenPtr &screen) const +{ + screen->setCurrentSize(m_currentSize); +} diff --git a/libkscreen-5.15.5/backends/xrandr/xrandrscreen.h b/libkscreen-5.15.5/backends/xrandr/xrandrscreen.h new file mode 100644 index 0000000000000000000000000000000000000000..c2dcde7b56963d00f02ece8ba23505abf5b1d846 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr/xrandrscreen.h @@ -0,0 +1,56 @@ +/************************************************************************************* + * Copyright 2012, 2013 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDRSCREEN_H +#define XRANDRSCREEN_H + +#include +#include + +#include "types.h" + +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; +}; + +#endif // XRANDRSCREEN_H diff --git a/libkscreen-5.15.5/backends/xrandr1.1/CMakeLists.txt b/libkscreen-5.15.5/backends/xrandr1.1/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a96225ec91b51803adcb0c68f508d49a9abcc40 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr1.1/CMakeLists.txt @@ -0,0 +1,26 @@ +find_package(XCB REQUIRED COMPONENTS XCB RANDR) + +include_directories(${CMAKE_SOURCE_DIR}/src + ${KDE4_INCLUDES} + ${CMAKE_BUILD_DIR} + ${QT_INCLUDES} +) + +set(xrandr11_SRCS + xrandr11.cpp + ../xcbeventlistener.cpp + ../xcbwrapper.cpp +) + +add_library(KSC_XRandR11 MODULE ${xrandr11_SRCS}) + +set_target_properties(KSC_XRandR11 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/kf5/kscreen") +set_target_properties(KSC_XRandR11 PROPERTIES PREFIX "") +target_link_libraries(KSC_XRandR11 Qt5::Core + Qt5::Gui + Qt5::X11Extras + ${XCB_LIBRARIES} + KF5::Screen +) + +install(TARGETS KSC_XRandR11 DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kscreen/) diff --git a/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.cpp b/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c352acc468a4fb9128f716655ebabff0ed76958 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.cpp @@ -0,0 +1,189 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "xrandr11.h" +#include "../xcbeventlistener.h" + +#include "config.h" +#include "edid.h" +#include "output.h" + +#include +#include + +#include +#include + +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->setPrimary(true); + 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 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); +} diff --git a/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.h b/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.h new file mode 100644 index 0000000000000000000000000000000000000000..c8def5757b48e9603bc5edabbe3ae01e31ab7ce5 --- /dev/null +++ b/libkscreen-5.15.5/backends/xrandr1.1/xrandr11.h @@ -0,0 +1,57 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef XRANDR11_BACKEND_H +#define XRANDR11_BACKEND_H + +#include "abstractbackend.h" +#include "../xcbwrapper.h" + +#include +#include + +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 diff --git a/libkscreen-5.15.5/interfaces/org.kde.KScreen.Backend.xml b/libkscreen-5.15.5/interfaces/org.kde.KScreen.Backend.xml new file mode 100644 index 0000000000000000000000000000000000000000..dabb77645d9a371fb36243700839ccfbe394463b --- /dev/null +++ b/libkscreen-5.15.5/interfaces/org.kde.KScreen.Backend.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkscreen-5.15.5/interfaces/org.kde.KScreen.FakeBackend.xml b/libkscreen-5.15.5/interfaces/org.kde.KScreen.FakeBackend.xml new file mode 100644 index 0000000000000000000000000000000000000000..5bd35f707bb3f96513e9ac3ef65924479a9697ce --- /dev/null +++ b/libkscreen-5.15.5/interfaces/org.kde.KScreen.FakeBackend.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkscreen-5.15.5/interfaces/org.kde.KScreen.xml b/libkscreen-5.15.5/interfaces/org.kde.KScreen.xml new file mode 100644 index 0000000000000000000000000000000000000000..397fdfd515602c82ad12f0e9307d7a1eabbb300f --- /dev/null +++ b/libkscreen-5.15.5/interfaces/org.kde.KScreen.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/libkscreen-5.15.5/libkscreen.categories b/libkscreen-5.15.5/libkscreen.categories new file mode 100644 index 0000000000000000000000000000000000000000..a6f98b6d8e36a5e0fdeb30882a289aab3ddaca87 --- /dev/null +++ b/libkscreen-5.15.5/libkscreen.categories @@ -0,0 +1,11 @@ +org.kde.kscreen kscreen IDENTIFIER [KSCREEN] +org.kde.kscreen.edid kscreen (edid) IDENTIFIER [KSCREEN_EDID] +org.kde.kscreen.backendLauncher kscreen (backendlauncher) IDENTIFIER [KSCREEN_BACKEND_LAUNCHER] +kscreen.xcb.helper libkscreen (xcb helper) IDENTIFIER [KSCREEN_XCB_HELPER] +kscreen.qscreen libkscreen (qscreen backend) IDENTIFIER [KSCREEN_QSCREEN] +kscreen.kwayland libkscreen (kwayland backend) IDENTIFIER [KSCREEN_WAYLAND] +kscreen.fake libkscreen (fake backend) IDENTIFIER [KSCREEN_FAKE] +kscreen.kwayland.testserver libkscreen (kwayland test server) IDENTIFIER [KSCREEN_WAYLAND_TESTSERVER] +kscreen.doctor libkscreen (kscreen-doctor utility) IDENTIFIER [KSCREEN_DOCTOR] +kscreen.xrandr libkscreen (xrandr backend) IDENTIFIER [KSCREEN_XRANDR] +kscreen.xrandr11 libkscreen (xrandr1.1 backend) IDENTIFIER [KSCREEN_XRANDR11] diff --git a/libkscreen-5.15.5/src/CMakeLists.txt b/libkscreen-5.15.5/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f0cf4fb345f39b3c5a38546fe9b6f610a4420de3 --- /dev/null +++ b/libkscreen-5.15.5/src/CMakeLists.txt @@ -0,0 +1,114 @@ +include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${QT_INCLUDES}) + +add_subdirectory(backendlauncher) +add_subdirectory(doctor) +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 +) + +qt5_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) + +target_link_libraries(KF5Screen + PUBLIC + Qt5::Core + Qt5::Gui + PRIVATE + Qt5::DBus + Qt5::X11Extras +) + +set_target_properties(KF5Screen PROPERTIES + VERSION "${KSCREEN_VERSION_STRING}" + SOVERSION "${KSCREEN_SOVERSION}" + EXPORT_NAME Screen +) + +target_include_directories(KF5Screen PUBLIC "$") + +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 ${KF5_INCLUDE_INSTALL_DIR}/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 ${KF5_INCLUDE_INSTALL_DIR}/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 ${KF5_MIN_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() diff --git a/libkscreen-5.15.5/src/abstractbackend.cpp b/libkscreen-5.15.5/src/abstractbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a60dc48a03739454f44af01a63d620a75bb216b --- /dev/null +++ b/libkscreen-5.15.5/src/abstractbackend.cpp @@ -0,0 +1,31 @@ +/************************************************************************************* + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + + +#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(); +} diff --git a/libkscreen-5.15.5/src/abstractbackend.h b/libkscreen-5.15.5/src/abstractbackend.h new file mode 100644 index 0000000000000000000000000000000000000000..269396845401d2c091b712081fb64249b3fb5f64 --- /dev/null +++ b/libkscreen-5.15.5/src/abstractbackend.h @@ -0,0 +1,115 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef ABSTRACT_BACKEND_H +#define ABSTRACT_BACKEND_H + +#include "kscreen_export.h" +#include "types.h" + +#include +#include + +namespace KScreen { + class Config; + class Edid; + +/** + * 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 diff --git a/libkscreen-5.15.5/src/backendlauncher/CMakeLists.txt b/libkscreen-5.15.5/src/backendlauncher/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e1c2002e6b15e9bebd23f1d6e9a1886aac3489b2 --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/CMakeLists.txt @@ -0,0 +1,42 @@ +include_directories(${CMAKE_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +set(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 +) + +qt5_add_dbus_adaptor(backendlauncher_SRCS ${CMAKE_SOURCE_DIR}/interfaces/org.kde.KScreen.Backend.xml + backenddbuswrapper.h BackendDBusWrapper backendadaptor BackendAdaptor) +qt5_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 + Qt5::Core + Qt5::Gui + Qt5::X11Extras + Qt5::DBus +) + +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} +) diff --git a/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.cpp b/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a3c408572398fbf855e63efd90544e3bcc5a009 --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include "backenddbuswrapper.h" +#include "backendloader.h" +#include "backendadaptor.h" +#include "kscreen_backendLauncher_debug.h" + +#include "src/configserializer_p.h" +#include "src/config.h" +#include "src/abstractbackend.h" + +#include +#include + +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(); +} + diff --git a/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.h b/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..41afceee430256ffe7bd7ba9af01a5ac880e7af4 --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/backenddbuswrapper.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef BACKENDDBUSWRAPPER_H +#define BACKENDDBUSWRAPPER_H + +#include +#include + +#include "src/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 diff --git a/libkscreen-5.15.5/src/backendlauncher/backendloader.cpp b/libkscreen-5.15.5/src/backendlauncher/backendloader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8a6ad61131350a95d46664b54a83803863946eb --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/backendloader.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include "backendloader.h" +#include "backendloaderadaptor.h" +#include "backenddbuswrapper.h" +#include "kscreen_backendLauncher_debug.h" +#include "src/abstractbackend.h" +#include "src/backendmanager_p.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +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 loader(new QPluginLoader(), pluginDeleter); + mLoader = loader.release(); + } + return KScreen::BackendManager::loadBackendPlugin(mLoader, name, arguments); +} + +void BackendLoader::quit() +{ + qApp->quit(); +} diff --git a/libkscreen-5.15.5/src/backendlauncher/backendloader.h b/libkscreen-5.15.5/src/backendlauncher/backendloader.h new file mode 100644 index 0000000000000000000000000000000000000000..6227a91ada317347305ecd35f38eef915bba8b0e --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/backendloader.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef BACKENDLAUNCHER_H +#define BACKENDLAUNCHER_H + +#include +#include + +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 diff --git a/libkscreen-5.15.5/src/backendlauncher/main.cpp b/libkscreen-5.15.5/src/backendlauncher/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4aa8630ffd3f51149ebe45fb5444afebf9ee62aa --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/main.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include +#include +#include + +#include "kscreen_backendLauncher_debug.h" +#include "backendloader.h" +#include "log.h" + +int main(int argc, char **argv) +{ + KScreen::Log::instance(); + QGuiApplication::setDesktopSettingsAware(false); + QGuiApplication app(argc, argv); + + auto disableSessionManagement = [](QSessionManager &sm) { + sm.setRestartHint(QSessionManager::RestartNever); + }; + QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); + QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); + + 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; +} diff --git a/libkscreen-5.15.5/src/backendlauncher/org.kde.kscreen.service.cmake b/libkscreen-5.15.5/src/backendlauncher/org.kde.kscreen.service.cmake new file mode 100644 index 0000000000000000000000000000000000000000..d2c801043d77b7b91cda167cb6279d760ebe8985 --- /dev/null +++ b/libkscreen-5.15.5/src/backendlauncher/org.kde.kscreen.service.cmake @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.kde.KScreen +Exec=@CMAKE_INSTALL_FULL_LIBEXECDIR_KF5@/kscreen_backend_launcher diff --git a/libkscreen-5.15.5/src/backendmanager.cpp b/libkscreen-5.15.5/src/backendmanager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f856abbe5cc6fb2dba21d05e25808fd5a6c2468 --- /dev/null +++ b/libkscreen-5.15.5/src/backendmanager.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * Copyright 2015 Sebastian Kügler + * + * 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 + * + */ + + +#include "backendmanager_p.h" + +#include "abstractbackend.h" +#include "config.h" +#include "configmonitor.h" +#include "backendinterface.h" +#include "kscreen_debug.h" +#include "getconfigoperation.h" +#include "configserializer_p.h" +#include "log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +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) + , 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("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; + Q_FOREACH (const QFileInfo &f, listBackends()) { + // 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 + QLatin1String("/kf5/kscreen/"), + backendFilter, + QDir::SortFlags(QDir::QDir::Name), + QDir::NoDotAndDotDot | QDir::Files); + finfos.append(dir.entryInfoList()); + } + return finfos; +} + +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(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 (m_inProcessBackend.first != nullptr && (name.isEmpty() || m_inProcessBackend.first->name() == name)) { + return m_inProcessBackend.first; + } else if (m_inProcessBackend.first != nullptr && m_inProcessBackend.first->name() != name) { + shutdownBackend(); + } + + if (mLoader == nullptr) { + mLoader = new QPluginLoader(this); + } + auto test_data_equals = QStringLiteral("TEST_DATA="); + QVariantMap arguments; + auto beargs = QString::fromLocal8Bit(qgetenv("KSCREEN_BACKEND_ARGS")); + if (beargs.startsWith(test_data_equals)) { + arguments[QStringLiteral("TEST_DATA")] = beargs.remove(test_data_equals); + } + auto backend = BackendManager::loadBackendPlugin(mLoader, name, arguments); + if (!backend) { + return nullptr; + } + //qCDebug(KSCREEN) << "Connecting ConfigMonitor to backend."; + ConfigMonitor::instance()->connectInProcessBackend(backend); + m_inProcessBackend = qMakePair(backend, arguments); + 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; + + const QByteArray args = qgetenv("KSCREEN_BACKEND_ARGS"); + QVariantMap arguments; + if (!args.isEmpty()) { + QList arglist = args.split(';'); + Q_FOREACH (const QByteArray &arg, arglist) { + const int pos = arg.indexOf('='); + if (pos == -1) { + continue; + } + arguments.insert(QString::fromUtf8(arg.left(pos)), arg.mid(pos + 1)); + } + } + + startBackend(QString::fromLatin1(qgetenv("KSCREEN_BACKEND")), arguments); +} + +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 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(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; + m_inProcessBackend.second.clear(); + delete m_inProcessBackend.first; + m_inProcessBackend.first = 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); + } + } +} diff --git a/libkscreen-5.15.5/src/backendmanager_p.h b/libkscreen-5.15.5/src/backendmanager_p.h new file mode 100644 index 0000000000000000000000000000000000000000..7fc38574906bfeb69d1937da3723511a8aa49b91 --- /dev/null +++ b/libkscreen-5.15.5/src/backendmanager_p.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * Copyright 2015 Sebastian Kügler + * + * 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 + * + */ + +/** + * 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 +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "kscreen_export.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(); + + /** 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; + QTimer mResetCrashCountTimer; + bool mShuttingDown; + int mRequestsCounter; + QEventLoop mShutdownLoop; + + // For in-process operation + QPluginLoader *mLoader; + QPair m_inProcessBackend; + + Method mMethod; +}; + +} + +#endif // KSCREEN_BACKENDMANAGER_H diff --git a/libkscreen-5.15.5/src/config.cpp b/libkscreen-5.15.5/src/config.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8cb7751908a296dd3964e7dae45330b894627e48 --- /dev/null +++ b/libkscreen-5.15.5/src/config.cpp @@ -0,0 +1,399 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "config.h" +#include "output.h" +#include "backendmanager_p.h" +#include "abstractbackend.h" +#include "kscreen_debug.h" + +#include +#include +#include +#include + +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) + , 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(); + } + + void onPrimaryOutputChanged() + { + const KScreen::OutputPtr output(qobject_cast(sender()), [](void *) {}); + Q_ASSERT(output); + if (output->isPrimary()) { + q->setPrimaryOutput(output); + } else { + q->setPrimaryOutput(findPrimaryOutput()); + } + } + + OutputList::Iterator removeOutput(OutputList::Iterator iter) + { + if (iter == outputs.end()) { + return iter; + } + + OutputPtr output = iter.value(); + if (!output) { + return outputs.erase(iter); + } + + const int outputId = iter.key(); + iter = outputs.erase(iter); + + if (primaryOutput == output) { + q->setPrimaryOutput(OutputPtr()); + } + output->disconnect(q); + + Q_EMIT q->outputRemoved(outputId); + + return iter; + } + + bool valid; + ScreenPtr screen; + OutputPtr primaryOutput; + OutputList outputs; + Features supportedFeatures; + +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; + Q_FOREACH(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) << "canBeApplied: Too many active screens. Requested: " << enabledOutputsCount << ", Max: " << maxEnabledOutputsCount; + return false; + } + + if (rect.width() > config->screen()->maxSize().width()) { + qCDebug(KSCREEN) << "canBeApplied: The configuration is too wide:" << rect.width(); + return false; + } + if (rect.height() > config->screen()->maxSize().height()) { + qCDebug(KSCREEN) << "canBeApplied: The configuration is too high:" << rect.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(); + for (const OutputPtr &ourOutput : d->outputs) { + newConfig->addOutput(ourOutput->clone()); + } + newConfig->d->primaryOutput = newConfig->d->findPrimaryOutput(); + newConfig->setSupportedFeatures(supportedFeatures()); + return newConfig; +} + +QString Config::connectedOutputsHash() const +{ + QStringList hashedOutputs; + + const auto outputs = connectedOutputs(); + 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; +} + +OutputList Config::outputs() const +{ + return d->outputs; +} + +OutputList Config::connectedOutputs() const +{ + OutputList outputs; + Q_FOREACH(const OutputPtr &output, d->outputs) { + if (!output->isConnected()) { + continue; + } + outputs.insert(output->id(), output); + } + + return outputs; +} + +OutputPtr Config::primaryOutput() const +{ + if (d->primaryOutput) { + return d->primaryOutput; + } + + d->primaryOutput = d->findPrimaryOutput(); + return d->primaryOutput; +} + +void Config::setPrimaryOutput(const OutputPtr &newPrimary) +{ + // Don't call primaryOutput(): at this point d->primaryOutput is either + // initialized, or we need to look for the primary anyway + if (d->primaryOutput == newPrimary) { + return; + } + +// qCDebug(KSCREEN) << "Primary output changed from" << primaryOutput() +// << "(" << (primaryOutput().isNull() ? "none" : primaryOutput()->name()) << ") to" +// << newPrimary << "(" << (newPrimary.isNull() ? "none" : newPrimary->name()) << ")"; + + for (OutputPtr &output : d->outputs) { + disconnect(output.data(), &KScreen::Output::isPrimaryChanged, + d, &KScreen::Config::Private::onPrimaryOutputChanged); + output->setPrimary(output == newPrimary); + connect(output.data(), &KScreen::Output::isPrimaryChanged, + d, &KScreen::Config::Private::onPrimaryOutputChanged); + } + + d->primaryOutput = newPrimary; + Q_EMIT primaryOutputChanged(newPrimary); +} + +void Config::addOutput(const OutputPtr &output) +{ + d->outputs.insert(output->id(), output); + connect(output.data(), &KScreen::Output::isPrimaryChanged, + d, &KScreen::Config::Private::onPrimaryOutputChanged); + + Q_EMIT outputAdded(output); + + if (output->isPrimary()) { + setPrimaryOutput(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); + } +} + +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()); + + // Remove removed outputs + Q_FOREACH (const OutputPtr &output, d->outputs) { + if (!other->d->outputs.contains(output->id())) { + removeOutput(output->id()); + } + } + + Q_FOREACH (const OutputPtr &otherOutput, 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); + } + } + + // Update validity + setValid(other->isValid()); +} + + +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 << endl << output; + } + } + dbg << ")"; + } else { + dbg << "KScreen::Config(NULL)"; + } + return dbg; +} + + +#include "config.moc" diff --git a/libkscreen-5.15.5/src/config.h b/libkscreen-5.15.5/src/config.h new file mode 100644 index 0000000000000000000000000000000000000000..06a6d3df69016cfee4449477f37b97c2c73d879d --- /dev/null +++ b/libkscreen-5.15.5/src/config.h @@ -0,0 +1,187 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_CONFIG_H +#define KSCREEN_CONFIG_H + +#include "screen.h" +#include "types.h" +#include "kscreen_export.h" + +#include +#include +#include + + +namespace KScreen { + +/** + * 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_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, this is mostly limited to X11. + Writable = 1 << 1, ///< The backend supports setting the config, it's not read-only. + PerOutputScaling = 1 << 2 ///< The backend supports scaling each output individually. + }; + 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; + OutputPtr primaryOutput() const; + void setPrimaryOutput(const OutputPtr &output); + void addOutput(const OutputPtr &output); + void removeOutput(int outputId); + void setOutputs(const OutputList &outputs); + + 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); + + Q_SIGNALS: + void outputAdded(const KScreen::OutputPtr &output); + void outputRemoved(int outputId); + void primaryOutputChanged(const KScreen::OutputPtr &output); + + 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 diff --git a/libkscreen-5.15.5/src/configmonitor.cpp b/libkscreen-5.15.5/src/configmonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a95becb1e91f6a5afd0b40bdccc392659b8b340 --- /dev/null +++ b/libkscreen-5.15.5/src/configmonitor.cpp @@ -0,0 +1,255 @@ +/************************************************************************************* + * Copyright 2012 - 2014 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "configmonitor.h" +#include "backendmanager_p.h" +#include "backendinterface.h" +#include "abstractbackend.h" +#include "configserializer_p.h" +#include "getconfigoperation.h" +#include "kscreen_debug.h" +#include "output.h" + +#include + +using namespace KScreen; + + +class Q_DECL_HIDDEN ConfigMonitor::Private : public QObject +{ + Q_OBJECT + +public: + Private(ConfigMonitor *q); + + void updateConfigs(); + 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> watchedConfigs; + + QPointer mBackend; + bool mFirstBackend; + + QMap> 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(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(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; + } + + Q_FOREACH (OutputPtr output, newConfig->connectedOutputs()) { + if (!output->edid() && output->isConnected()) { + QDBusPendingReply 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(); + Q_ASSERT(mPendingEDIDRequests.contains(config)); + Q_ASSERT(mPendingEDIDRequests[config].contains(outputId)); + + watcher->deleteLater(); + + mPendingEDIDRequests[config].removeOne(outputId); + + const QDBusPendingReply 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> 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(); ++iter) { + if (iter->data() == removedConfig) { + iter = watchedConfigs.erase(iter); + // Iterate over the entire list in case there are duplicates + } + } +} + +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 weakConfig = config.toWeakRef(); + if (!d->watchedConfigs.contains(weakConfig)) { + connect(weakConfig.data(), &QObject::destroyed, + d, &Private::configDestroyed); + d->watchedConfigs << weakConfig; + } +} + +void ConfigMonitor::removeConfig(const ConfigPtr &config) +{ + const QWeakPointer weakConfig = config.toWeakRef(); + if (d->watchedConfigs.contains(config)) { + disconnect(weakConfig.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" diff --git a/libkscreen-5.15.5/src/configmonitor.h b/libkscreen-5.15.5/src/configmonitor.h new file mode 100644 index 0000000000000000000000000000000000000000..96c3adb028f47d39520e6e2f033eafbfe72a6fdb --- /dev/null +++ b/libkscreen-5.15.5/src/configmonitor.h @@ -0,0 +1,63 @@ +/************************************************************************************* + * Copyright 2012 - 2014 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_CONFIGMONITOR_H +#define KSCREEN_CONFIGMONITOR_H + +#include +#include + +#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 diff --git a/libkscreen-5.15.5/src/configoperation.cpp b/libkscreen-5.15.5/src/configoperation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6342a5f0b27c5f3df0315b06b9e3fbcbd873eb39 --- /dev/null +++ b/libkscreen-5.15.5/src/configoperation.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * Copyright 2015 Sebastian Kügler + * + * 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 + * + */ + +#include "configoperation.h" +#include "configoperation_p.h" +#include "backendmanager_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; +} diff --git a/libkscreen-5.15.5/src/configoperation.h b/libkscreen-5.15.5/src/configoperation.h new file mode 100644 index 0000000000000000000000000000000000000000..dccf8a4fde7ee035eda6ab0d9035137081cb09d1 --- /dev/null +++ b/libkscreen-5.15.5/src/configoperation.h @@ -0,0 +1,71 @@ +/* + * + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef KSCREEN_CONFIGOPERATION_H +#define KSCREEN_CONFIGOPERATION_H + +#include + +#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 diff --git a/libkscreen-5.15.5/src/configoperation_p.h b/libkscreen-5.15.5/src/configoperation_p.h new file mode 100644 index 0000000000000000000000000000000000000000..1769c9b2d1ad3d51d398fd0bdb0bd9ce41943fb5 --- /dev/null +++ b/libkscreen-5.15.5/src/configoperation_p.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include + +#include "configoperation.h" +#include "abstractbackend.h" +#include "backendinterface.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) + +}; + +} diff --git a/libkscreen-5.15.5/src/configserializer.cpp b/libkscreen-5.15.5/src/configserializer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ccba999f0c376be9837d000d67f18533062f1541 --- /dev/null +++ b/libkscreen-5.15.5/src/configserializer.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include "configserializer_p.h" + +#include "config.h" +#include "mode.h" +#include "output.h" +#include "screen.h" +#include "edid.h" +#include "kscreen_debug.h" + +#include +#include +#include +#include + +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(config->supportedFeatures()); + + QJsonArray outputs; + Q_FOREACH (const OutputPtr &output, config->outputs()) { + outputs.append(serializeOutput(output)); + } + obj[QLatin1String("outputs")] = outputs; + if (config->screen()) { + obj[QLatin1String("screen")] = serializeScreen(config->screen()); + } + + 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(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(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("primary")] = output->isPrimary(); + obj[QLatin1String("clones")] = serializeList(output->clones()); + //obj[QLatin1String("edid")] = output->edid()->raw(); + obj[QLatin1String("sizeMM")] = serializeSize(output->sizeMm()); + + QJsonArray modes; + Q_FOREACH (const ModePtr &mode, output->modes()) { + modes.append(serializeMode(mode)); + } + obj[QLatin1String("modes")] = modes; + + 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 == QLatin1String("x")) { + x = value.toInt(); + } else if (key == QLatin1String("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(QStringLiteral("features"))) { + config->setSupportedFeatures(static_cast(map[QStringLiteral("features")].toInt())); + } + + if (map.contains(QStringLiteral("outputs"))) { + const QDBusArgument &outputsArg = map[QStringLiteral("outputs")].value(); + outputsArg.beginArray(); + OutputList outputs; + while (!outputsArg.atEnd()) { + QVariant value; + outputsArg >> value; + const KScreen::OutputPtr output = deserializeOutput(value.value()); + if (!output) { + return ConfigPtr(); + } + outputs.insert(output->id(), output); + } + outputsArg.endArray(); + config->setOutputs(outputs); + } + + if (map.contains(QStringLiteral("screen"))) { + const QDBusArgument &screenArg = map[QStringLiteral("screen")].value(); + 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); + + 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(value.toInt())); + } else if (key == QLatin1String("icon")) { + output->setIcon(value.toString()); + } else if (key == QLatin1String("pos")) { + output->setPos(deserializePoint(value.value())); + } else if (key == QLatin1String("scale")) { + output->setScale(value.toDouble()); + } else if (key == QLatin1String("size")) { + output->setSize(deserializeSize(value.value())); + } else if (key == QLatin1String("rotation")) { + output->setRotation(static_cast(value.toInt())); + } else if (key == QLatin1String("currentModeId")) { + output->setCurrentModeId(value.toString()); + } else if (key == QLatin1String("preferredModes")) { + output->setPreferredModes(deserializeList(value.value())); + } 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")) { + output->setPrimary(value.toBool()); + } else if (key == QLatin1String("clones")) { + output->setClones(deserializeList(value.value())); + } else if (key == QLatin1String("sizeMM")) { + output->setSizeMm(deserializeSize(value.value())); + } else if (key == QLatin1String("modes")) { + const QDBusArgument arg = value.value(); + ModeList modes; + arg.beginArray(); + while (!arg.atEnd()) { + QVariant value; + arg >> value; + const KScreen::ModePtr mode = deserializeMode(value.value()); + if (!mode) { + return OutputPtr(); + } + modes.insert(mode->id(), mode); + } + arg.endArray(); + output->setModes(modes); + } else { + qCWarning(KSCREEN) << "Invalid key in Output map: " << key; + return OutputPtr(); + } + arg.endMapEntry(); + } + arg.endMap(); + 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())); + } 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())); + } else if (key == QLatin1String("maxSize")) { + screen->setMaxSize(deserializeSize(value.value())); + } else if (key == QLatin1String("minSize")) { + screen->setMinSize(deserializeSize(value.value())); + } else { + qCWarning(KSCREEN) << "Invalid key in Screen map:" << key; + return ScreenPtr(); + } + arg.endMapEntry(); + } + arg.endMap(); + return screen; +} diff --git a/libkscreen-5.15.5/src/configserializer_p.h b/libkscreen-5.15.5/src/configserializer_p.h new file mode 100644 index 0000000000000000000000000000000000000000..8e39f74db154074794773c7d999b5f6004b87fa8 --- /dev/null +++ b/libkscreen-5.15.5/src/configserializer_p.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef CONFIGSERIALIZER_H +#define CONFIGSERIALIZER_H + +#include +#include +#include +#include + +#include "types.h" +#include "kscreen_export.h" + +namespace KScreen +{ + +namespace ConfigSerializer +{ + +KSCREEN_EXPORT QJsonObject serializePoint(const QPoint &point); +KSCREEN_EXPORT QJsonObject serializeSize(const QSize &size); +template +KSCREEN_EXPORT QJsonArray serializeList(const QList &list) +{ + QJsonArray arr; + Q_FOREACH (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 +KSCREEN_EXPORT QList deserializeList(const QDBusArgument &arg) +{ + QList list; + arg.beginArray(); + while (!arg.atEnd()) { + QVariant v; + arg >> v; + list.append(v.value()); + } + 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 diff --git a/libkscreen-5.15.5/src/doctor/CMakeLists.txt b/libkscreen-5.15.5/src/doctor/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8871a797867863870cf6f70e13c96defcc97c807 --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_executable(kscreen-doctor main.cpp doctor.cpp dpmsclient.cpp) +target_link_libraries(kscreen-doctor Qt5::DBus KF5::Screen KF5::WaylandClient) +install(TARGETS kscreen-doctor ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/libkscreen-5.15.5/src/doctor/doctor.cpp b/libkscreen-5.15.5/src/doctor/doctor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a2fe8d27fd4447f4e4905e8dd46146995fe5e2b --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/doctor.cpp @@ -0,0 +1,494 @@ +/************************************************************************************* + * Copyright 2014-2016 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "doctor.h" +#include "dpmsclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../backendmanager_p.h" +#include "../config.h" +#include "../configoperation.h" +#include "../getconfigoperation.h" +#include "../setconfigoperation.h" +#include "../edid.h" +#include "../log.h" +#include "../output.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_positionalArgs.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." << endl; + // We need to kick the event loop, otherwise .quit() hangs + QTimer::singleShot(0, qApp->quit); + return; + + } + m_dpmsClient = new DpmsClient(this); + connect(m_dpmsClient, &DpmsClient::finished, qApp, &QCoreApplication::quit); + + const QString dpmsArg = m_parser->value(QStringLiteral("dpms")); + if (dpmsArg == QStringLiteral("show")) { + showDpms(); + } else { + setDpms(dpmsArg); + } + 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 KScreen::Doctor::setDpms(const QString& dpmsArg) +{ + qDebug() << "SetDpms: " << dpmsArg; + connect(m_dpmsClient, &DpmsClient::ready, this, [this, dpmsArg]() { + cout << "DPMS.ready()"; + if (dpmsArg == QStringLiteral("off")) { + m_dpmsClient->off(); + } else if (dpmsArg == QStringLiteral("on")) { + m_dpmsClient->on(); + } else { + cout << "--dpms argument not understood (" << dpmsArg << ")"; + } + }); + + m_dpmsClient->connect(); +} + + +void Doctor::showDpms() +{ + m_dpmsClient = new DpmsClient(this); + + connect(m_dpmsClient, &DpmsClient::ready, this, []() { + cout << "DPMS.ready()"; + }); + + m_dpmsClient->connect(); +} + +void Doctor::showBackends() const +{ + cout << "Environment: " << endl; + auto env_kscreen_backend = (qgetenv("KSCREEN_BACKEND").isEmpty()) ? QStringLiteral("[not set]") : QString::fromUtf8(qgetenv("KSCREEN_BACKEND")); + cout << " * KSCREEN_BACKEND : " << env_kscreen_backend << endl; + auto env_kscreen_backend_inprocess = (qgetenv("KSCREEN_BACKEND_INPROCESS").isEmpty()) ? QStringLiteral("[not set]") : QString::fromUtf8(qgetenv("KSCREEN_BACKEND_INPROCESS")); + cout << " * KSCREEN_BACKEND_INPROCESS : " << env_kscreen_backend_inprocess << endl; + auto env_kscreen_logging = (qgetenv("KSCREEN_LOGGING").isEmpty()) ? QStringLiteral("[not set]") : QString::fromUtf8(qgetenv("KSCREEN_LOGGING")); + cout << " * KSCREEN_LOGGING : " << env_kscreen_logging << endl; + + cout << "Logging to : " << (Log::instance()->enabled() ? Log::instance()->logFile() : QStringLiteral("[logging disabled]")) << endl; + auto backends = BackendManager::instance()->listBackends(); + auto preferred = BackendManager::instance()->preferredBackend(); + cout << "Preferred KScreen backend : " << green << preferred.fileName() << cr << endl; + cout << "Available KScreen backends:" << endl; + Q_FOREACH(const QFileInfo f, backends) { + auto c = blue; + if (preferred == f) { + c = green; + } + cout << " * " << c << f.fileName() << cr << ": " << f.absoluteFilePath() << endl; + } + cout << endl; +} + +void Doctor::setOptionList(const QStringList &positionalArgs) +{ + m_positionalArgs = positionalArgs; +} + +void Doctor::parsePositionalArgs() +{ + //qCDebug(KSCREEN_DOCTOR) << "POSARGS" << m_positionalArgs; + Q_FOREACH(const QString &op, m_positionalArgs) { + auto ops = op.split(QLatin1Char('.')); + if (ops.count() > 2) { + bool ok; + int output_id = -1; + if (ops[0] == QStringLiteral("output")) { + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->name() == ops[1]) { + output_id = output->id(); + } + } + if (output_id == -1) { + output_id = ops[1].toInt(&ok); + if (!ok) { + cerr << "Unable to parse output id: " << ops[1] << endl; + qApp->exit(3); + return; + } + } + if (ops.count() == 3 && ops[2] == QStringLiteral("enable")) { + if (!setEnabled(output_id, true)) { + qApp->exit(1); + return; + }; + } else if (ops.count() == 3 && ops[2] == QStringLiteral("disable")) { + if (!setEnabled(output_id, false)) { + qApp->exit(1); + return; + }; + } else if (ops.count() == 4 && ops[2] == QStringLiteral("mode")) { + QString mode_id = ops[3]; + // set mode + if (!setMode(output_id, mode_id)) { + qApp->exit(9); + return; + } + qCDebug(KSCREEN_DOCTOR) << "Output" << output_id << "set mode" << mode_id; + + } else if (ops.count() == 4 && ops[2] == QStringLiteral("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] << endl; + qApp->exit(5); + return; + } + + QPoint p(x, y); + qCDebug(KSCREEN_DOCTOR) << "Output position" << p; + if (!setPosition(output_id, p)) { + qApp->exit(1); + return; + } + } else if ((ops.count() == 4 || ops.count() == 5) && ops[2] == QStringLiteral("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] + QLatin1String(".") + ops[4]; + scale = dbl.toDouble(&ok); + }; + // set scale + if (!ok || qFuzzyCompare(scale, 0.0) || !setScale(output_id, scale)) { + qCDebug(KSCREEN_DOCTOR) << "Could not set scale " << scale << " to output " << output_id; + qApp->exit(9); + return; + } + } else if ((ops.count() == 4) && (ops[2] == QStringLiteral("orientation") || ops[2] == QStringLiteral("rotation"))) { + const QString _rotation = ops[3].toLower(); + bool ok = false; + const QHash 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 || !setRotation(output_id, rot)) { + qCDebug(KSCREEN_DOCTOR) << "Could not set orientation " << _rotation << " to output " << output_id; + qApp->exit(9); + return; + } + } else { + cerr << "Unable to parse arguments: " << op << endl; + qApp->exit(2); + return; + } + } + } + } +} + +void Doctor::configReceived(KScreen::ConfigOperation *op) +{ + m_config = op->config(); + + if (m_parser->isSet(QStringLiteral("json"))) { + showJson(); + qApp->quit(); + } + if (m_parser->isSet(QStringLiteral("outputs"))) { + showOutputs(); + qApp->quit(); + } + + parsePositionalArgs(); + + if (m_changed) { + applyConfig(); + m_changed = false; + } +} + +int Doctor::outputCount() const +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return 0; + } + return m_config->outputs().count(); +} + +void Doctor::showOutputs() const +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return; + } + + QHash 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"); + + Q_FOREACH (const auto &output, m_config->outputs()) { + cout << green << "Output: " << cr << output->id() << " " << output->name(); + cout << " " << (output->isEnabled() ? green + QLatin1String("enabled") : red + QLatin1String("disabled")); + cout << " " << (output->isConnected() ? green + QLatin1String("connected") : red + QLatin1String("disconnected")); + cout << " " << (output->isPrimary() ? green + QLatin1String("primary") : QString()); + auto _type = typeString[output->type()]; + cout << " " << yellow << (_type.isEmpty() ? QStringLiteral("UnmappedOutputType") : _type); + cout << blue << " Modes: " << cr; + Q_FOREACH (auto 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 == 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() << " "; + if (output->isPrimary()) { + cout << blue << "primary"; + } + cout << endl; + } +} + +void Doctor::showJson() const +{ + QJsonDocument doc(KScreen::ConfigSerializer::serializeConfig(m_config)); + cout << doc.toJson(QJsonDocument::Indented); +} + +bool Doctor::setEnabled(int id, bool enabled = true) +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return false; + } + + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->id() == id) { + cout << (enabled ? "Enabling " : "Disabling ") << "output " << id << endl; + output->setEnabled(enabled); + m_changed = true; + return true; + } + } + cerr << "Output with id " << id << " not found." << endl; + qApp->exit(8); + return false; +} + +bool Doctor::setPosition(int id, const QPoint &pos) +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return false; + } + + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->id() == id) { + qCDebug(KSCREEN_DOCTOR) << "Set output position" << pos; + output->setPos(pos); + m_changed = true; + return true; + } + } + cout << "Output with id " << id << " not found." << endl; + return false; +} + +bool Doctor::setMode(int id, const QString &mode_id) +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return false; + } + + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->id() == id) { + // find mode + Q_FOREACH (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() == mode_id || name == mode_id) { + qCDebug(KSCREEN_DOCTOR) << "Taddaaa! Found mode" << mode->id() << name; + output->setCurrentModeId(mode->id()); + m_changed = true; + return true; + } + } + } + } + cout << "Output mode " << mode_id << " not found." << endl; + return false; +} + +bool Doctor::setScale(int id, qreal scale) +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return false; + } + + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->id() == id) { + output->setScale(scale); + m_changed = true; + return true; + } + } + cout << "Output scale " << id << " invalid." << endl; + return false; +} + +bool Doctor::setRotation(int id, KScreen::Output::Rotation rot) +{ + if (!m_config) { + qCWarning(KSCREEN_DOCTOR) << "Invalid config."; + return false; + } + + Q_FOREACH (const auto &output, m_config->outputs()) { + if (output->id() == id) { + output->setRotation(rot); + m_changed = true; + return true; + } + } + cout << "Output rotation " << id << " invalid." << endl; + return false; +} + +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); +} diff --git a/libkscreen-5.15.5/src/doctor/doctor.h b/libkscreen-5.15.5/src/doctor/doctor.h new file mode 100644 index 0000000000000000000000000000000000000000..64cde2474c36fae1a96e33dcc78d32751100e43a --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/doctor.h @@ -0,0 +1,78 @@ +/************************************************************************************* + * Copyright 2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_DOCTOR_H +#define KSCREEN_DOCTOR_H + +#include +#include +#include "../config.h" + +#include + +namespace KScreen +{ +class ConfigOperation; +class DpmsClient; + +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); + + void showDpms(); + + void showBackends() const; + void showOutputs() const; + void showJson() const; + int outputCount() const; + void setDpms(const QString &dpmsArg); + + bool setEnabled(int id, bool enabled); + bool setPosition(int id, const QPoint &pos); + bool setMode(int id, const QString &mode_id); + bool setScale(int id, qreal scale); + bool setRotation(int id, KScreen::Output::Rotation rot); + +Q_SIGNALS: + void outputsChanged(); + void started(); + void configChanged(); + +private: + //static QString modeString(KWayland::Server::OutputDeviceInterface* outputdevice, int mid); + void applyConfig(); + void parsePositionalArgs(); + int parseInt(const QString &str, bool &ok) const; + KScreen::ConfigPtr m_config; + QCommandLineParser* m_parser; + bool m_changed; + QStringList m_positionalArgs; + DpmsClient *m_dpmsClient; +}; + +} // namespace + +#endif // KSCREEN_WAYLAND_SCREEN_H diff --git a/libkscreen-5.15.5/src/doctor/dpmsclient.cpp b/libkscreen-5.15.5/src/doctor/dpmsclient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9183b2ea9b9cfaad2415d35cdd37dc8590043bf --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/dpmsclient.cpp @@ -0,0 +1,157 @@ +/************************************************************************************* + * Copyright 2016 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "dpmsclient.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//static const QString s_socketName = QStringLiteral("libkscreen-test-wayland-backend-0"); +static const QString s_socketName = QStringLiteral("wayland-0"); + +Q_LOGGING_CATEGORY(KSCREEN_DPMS, "kscreen.dpms") + +using namespace KScreen; + +using namespace KWayland::Client; + +DpmsClient::DpmsClient(QObject *parent) + : QObject(parent) + , m_thread(nullptr) + , m_connection(nullptr) + , m_dpmsManager(nullptr) +{ + +} + +DpmsClient::~DpmsClient() +{ + m_thread->exit(); + m_thread->wait(); + delete m_thread; + delete m_connection; + +} + +void DpmsClient::connect() +{ + // setup connection + m_connection = new ConnectionThread; + m_connection->setSocketName(s_socketName); + QObject::connect(m_connection, &ConnectionThread::connected, this, &DpmsClient::connected); + QObject::connect(m_connection, &ConnectionThread::failed, this, [=]() { + qCDebug(KSCREEN_DPMS) << "Connection failed"; + }); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + + qDebug() << "init"; +} + +void DpmsClient::connected() +{ + qDebug() << "Connected!"; + m_registry.create(m_connection); + QObject::connect(&m_registry, &Registry::interfacesAnnounced, this, + [this] { + const bool hasDpms = m_registry.hasInterface(Registry::Interface::Dpms); + // QLabel *hasDpmsLabel = new QLabel(&window); + if (hasDpms) { + qDebug() << QStringLiteral("Compositor provides a DpmsManager"); + } else { + qDebug() << QStringLiteral("Compositor does not provid a DpmsManager"); + } + + if (hasDpms) { + const auto dpmsData = m_registry.interface(Registry::Interface::Dpms); + m_dpmsManager = m_registry.createDpmsManager(dpmsData.name, dpmsData.version); + } + + + emit this->ready(); + }); + m_registry.setup(); + + //QVERIFY(dpmsSpy.wait(100)); + +} + +void KScreen::DpmsClient::changeMode(KWayland::Client::Dpms::Mode mode) +{ + const auto outputs = m_registry.interfaces(Registry::Interface::Output); + for (auto outputInterface : outputs) { + + KWayland::Client::Output *output = m_registry.createOutput(outputInterface.name, outputInterface.version, &m_registry); + qDebug() << "OUTPUT!" << output->model() << output->manufacturer() << output->geometry(); + + Dpms *dpms = nullptr; + if (m_dpmsManager) { + dpms = m_dpmsManager->getDpms(output, output); + } + + if (dpms) { + QObject::connect(dpms, &Dpms::supportedChanged, this, + [dpms, mode, this] { + if (dpms->isSupported()) { + QObject::connect(dpms, &Dpms::modeChanged, this, + &DpmsClient::modeChanged, Qt::QueuedConnection); + qDebug() << "Switching " << (mode == Dpms::Mode::On ? "on" : "off"); + m_modeChanges++; + dpms->requestMode(mode); + } + + }, Qt::QueuedConnection + ); + } + + qDebug() << "dpms->isSupported()" << dpms->isSupported(); + } +} + +void DpmsClient::modeChanged() +{ + m_modeChanges = m_modeChanges - 1; + if (m_modeChanges <= 0) { + emit finished(); + m_modeChanges = 0; + } +} + +void DpmsClient::on() +{ + changeMode(Dpms::Mode::On); + //emit finished(); +} + +void KScreen::DpmsClient::off() +{ + changeMode(Dpms::Mode::Off); + //emit finished(); +} diff --git a/libkscreen-5.15.5/src/doctor/dpmsclient.h b/libkscreen-5.15.5/src/doctor/dpmsclient.h new file mode 100644 index 0000000000000000000000000000000000000000..48be41c620cbcca89488d4fe143cfcc5190c272e --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/dpmsclient.h @@ -0,0 +1,77 @@ +/************************************************************************************* + * Copyright 2016 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_DPMSCLIENT_H +#define KSCREEN_DPMSCLIENT_H + +#include +#include +#include "../config.h" + +#include +#include + +class QThread; + +namespace KWayland +{ + namespace Client { + class ConnectionThread; + } +} + +namespace KScreen +{ +class ConfigOperation; + +class DpmsClient : public QObject +{ + Q_OBJECT + +public: + explicit DpmsClient(QObject *parent = nullptr); + ~DpmsClient() override; + + void connect(); + void off(); + void on(); + +Q_SIGNALS: + void ready(); + void finished(); + +private Q_SLOTS: + void connected(); + void modeChanged(); + +private: + void changeMode(KWayland::Client::Dpms::Mode mode); + QThread *m_thread; + KWayland::Client::ConnectionThread *m_connection = nullptr; + KWayland::Client::DpmsManager *m_dpmsManager = nullptr; + KWayland::Client::Registry m_registry; + bool m_setOff = true; + bool m_setOn = false; + + bool m_supportedOututCount = 0; + int m_modeChanges = 0; +}; + +} // namespace + +#endif // KSCREEN_DPSMCLIENT_H diff --git a/libkscreen-5.15.5/src/doctor/main.cpp b/libkscreen-5.15.5/src/doctor/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5cd2e8be6384fd90344f510d737474a70bc295b --- /dev/null +++ b/libkscreen-5.15.5/src/doctor/main.cpp @@ -0,0 +1,115 @@ +/************************************************************************************* + * Copyright 2014-2015 by Sebastian Kügler * + * * + * 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 * + *************************************************************************************/ + +#include "doctor.h" + +#include +#include + +#include + +/** 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.0,1280 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..[.]\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 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.. output..setting [...]]")); + parser.addHelpOption(); + parser.addOption(info); + parser.addOption(json); + parser.addOption(outputs); + parser.addOption(dpms); + parser.addOption(log); + parser.process(app); + + if (!parser.positionalArguments().isEmpty()) { + server.setOptionList(parser.positionalArguments()); + } + + server.start(&parser); + return app.exec(); +} diff --git a/libkscreen-5.15.5/src/edid.cpp b/libkscreen-5.15.5/src/edid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a99fab0677a59327278a5216c8c57cb6d1470ef --- /dev/null +++ b/libkscreen-5.15.5/src/edid.cpp @@ -0,0 +1,418 @@ +/************************************************************************************* + * Copyright (C) 2012 by Daniel Nicoletti * + * (C) 2012 - 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "edid.h" +#include "kscreen_debug_edid.h" + +#include + +#include +#include +#include +#include + +#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(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[0] = 'A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1; + pnpId[1] = 'A' + ((data[GCM_EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[GCM_EDID_OFFSET_PNPID+1] & 0xe0) / 32) - 1; + pnpId[2] = 'A' + (data[GCM_EDID_OFFSET_PNPID + 1] & 0x1f) - 1; + + // load the PNP_IDS file and load the vendor name + if (!pnpId.isEmpty()) { + QFile pnpIds(QStringLiteral(PNP_IDS)); + if (pnpIds.open(QIODevice::ReadOnly)) { + while (!pnpIds.atEnd()) { + QString line = QString::fromUtf8(pnpIds.readLine()); + if (line.startsWith(pnpId)) { + QStringList parts = line.split(QLatin1Char('\t')); + if (parts.size() == 2) { + vendorName = line.split(QLatin1Char('\t')).at(1).simplified(); + } + break; + } + } + } + } + + /* maybe there isn't a ASCII serial number descriptor, so use this instead */ + serial = static_cast(data[GCM_EDID_OFFSET_SERIAL + 0]); + serial += static_cast(data[GCM_EDID_OFFSET_SERIAL + 1] * 0x100); + serial += static_cast(data[GCM_EDID_OFFSET_SERIAL + 2] * 0x10000); + serial += static_cast(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(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(data), 13).simplified(); + + for (int i = 0; i < text.length(); ++i) { + if (!text.at(i).isPrint()) { + text[i] = '-'; + } + } + return text; +} diff --git a/libkscreen-5.15.5/src/edid.h b/libkscreen-5.15.5/src/edid.h new file mode 100644 index 0000000000000000000000000000000000000000..a21585f0d253d9a7a8fe053afe31a031816ea3e9 --- /dev/null +++ b/libkscreen-5.15.5/src/edid.h @@ -0,0 +1,88 @@ +/************************************************************************************* + * Copyright (C) 2012 by Daniel Nicoletti * + * (C) 2012, 2013 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + + +#ifndef KSCREEN_EDID_H +#define KSCREEN_EDID_H + +#include "kscreen_export.h" + +#include +#include +#include + +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 diff --git a/libkscreen-5.15.5/src/getconfigoperation.cpp b/libkscreen-5.15.5/src/getconfigoperation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4cb9afb0b78bed2a409d3f86db3bfc27f2e51ce2 --- /dev/null +++ b/libkscreen-5.15.5/src/getconfigoperation.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include "getconfigoperation.h" +#include "configoperation_p.h" +#include "config.h" +#include "output.h" +#include "log.h" +#include "backendmanager_p.h" +#include "configserializer_p.h" +#include "backendinterface.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 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 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; + } + Q_FOREACH (const OutputPtr &output, config->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 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(); + 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; + } + Q_FOREACH (auto output, config->outputs()) { + if (output->edid() == nullptr) { + const QByteArray edidData = backend->edid(output->id()); + output->setEdid(edidData); + } + } +} + + +#include "getconfigoperation.moc" diff --git a/libkscreen-5.15.5/src/getconfigoperation.h b/libkscreen-5.15.5/src/getconfigoperation.h new file mode 100644 index 0000000000000000000000000000000000000000..9be449bda3284db6b7db4cdf6c34cc06dd75e52f --- /dev/null +++ b/libkscreen-5.15.5/src/getconfigoperation.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef KSCREEN_GETCONFIGOPERATION_H +#define KSCREEN_GETCONFIGOPERATION_H + +#include "configoperation.h" +#include "types.h" +#include "kscreen_export.h" + +class QDBusPendingCallWatcher; +class OrgKdeKscreenBackend; + +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 diff --git a/libkscreen-5.15.5/src/kscreen2.pc.in b/libkscreen-5.15.5/src/kscreen2.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..c266d7689efd86a0556edf2bd709ee9fa8818470 --- /dev/null +++ b/libkscreen-5.15.5/src/kscreen2.pc.in @@ -0,0 +1,10 @@ +prefix=@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_STRING@ + +Libs: -L${libdir} -lKF5Screen +Cflags: -I${includedir} diff --git a/libkscreen-5.15.5/src/log.cpp b/libkscreen-5.15.5/src/log.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76edfacae095ec3895985fe566de8269b4556b7f --- /dev/null +++ b/libkscreen-5.15.5/src/log.cpp @@ -0,0 +1,138 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "log.h" + +#include +#include +#include +#include +#include + +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() != QStringLiteral("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 diff --git a/libkscreen-5.15.5/src/log.h b/libkscreen-5.15.5/src/log.h new file mode 100644 index 0000000000000000000000000000000000000000..cceba0570d9d2ef6a70491b04b3958478c121435 --- /dev/null +++ b/libkscreen-5.15.5/src/log.h @@ -0,0 +1,117 @@ +/************************************************************************************* + * Copyright 2016 by Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_LOG_H +#define KSCREEN_LOG_H + +#include "kscreen_export.h" +#include "types.h" + +#include +#include + +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 diff --git a/libkscreen-5.15.5/src/mode.cpp b/libkscreen-5.15.5/src/mode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d55b87d77dfb0bbd9f204d17451557a09d8e784a --- /dev/null +++ b/libkscreen-5.15.5/src/mode.cpp @@ -0,0 +1,140 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#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; +} diff --git a/libkscreen-5.15.5/src/mode.h b/libkscreen-5.15.5/src/mode.h new file mode 100644 index 0000000000000000000000000000000000000000..7d96ae6477e90a767e97c4a5634ee904b2185b32 --- /dev/null +++ b/libkscreen-5.15.5/src/mode.h @@ -0,0 +1,77 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef MODE_CONFIG_H +#define MODE_CONFIG_H + +#include "kscreen_export.h" +#include "types.h" + +#include +#include +#include +#include + +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 diff --git a/libkscreen-5.15.5/src/output.cpp b/libkscreen-5.15.5/src/output.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1cc03ff84942c4a719a6196ce1e731587a702a40 --- /dev/null +++ b/libkscreen-5.15.5/src/output.cpp @@ -0,0 +1,625 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "output.h" +#include "mode.h" +#include "edid.h" +#include "abstractbackend.h" +#include "backendmanager_p.h" +#include "kscreen_debug.h" + +#include +#include +#include + +using namespace KScreen; + +class Q_DECL_HIDDEN Output::Private +{ + public: + Private(): + id(0), + type(Unknown), + rotation(None), + scale(1.0), + connected(false), + enabled(false), + primary(false), + edid(nullptr) + {} + + Private(const Private &other): + id(other.id), + name(other.name), + type(other.type), + icon(other.icon), + clones(other.clones), + currentMode(other.currentMode), + preferredMode(other.preferredMode), + preferredModes(other.preferredModes), + sizeMm(other.sizeMm), + pos(other.pos), + size(other.size), + rotation(other.rotation), + scale(other.scale), + connected(other.connected), + enabled(other.enabled), + primary(other.primary), + followPreferredMode(other.followPreferredMode) + { + Q_FOREACH (const ModePtr &otherMode, other.modeList) { + 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); + + int id; + QString name; + Type type; + QString icon; + ModeList modeList; + QList clones; + QString currentMode; + QString preferredMode; + QStringList preferredModes; + QSize sizeMm; + QPoint pos; + QSize size; + Rotation rotation; + qreal scale; + bool connected; + bool enabled; + bool primary; + bool followPreferredMode = false; + + QScopedPointer edid; +}; + +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; + Q_FOREACH(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(); +} + +QString Output::hash() const +{ + if (edid() && edid()->isValid()) { + return edid()->hash(); + } + return name(); +} + +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::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) { + emit modesChanged(); + 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 area, total = 0; + KScreen::ModePtr biggest; + KScreen::ModePtr candidateMode; + Q_FOREACH(const QString &modeId, d->preferredModes) { + candidateMode = mode(modeId); + 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(); +} + +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; + emit scaleChanged(); +} + +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->primary; +} + +void Output::setPrimary(bool primary) +{ + if (d->primary == primary) { + return; + } + + d->primary = primary; + + Q_EMIT isPrimaryChanged(); +} + +QList Output::clones() const +{ + return d->clones; +} + +void Output::setClones(const QList &outputlist) +{ + if (d->clones == outputlist) { + return; + } + + d->clones = outputlist; + + Q_EMIT clonesChanged(); +} + +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) { + d->followPreferredMode = follow; + Q_EMIT followPreferredModeChanged(follow); + } +} + +QRect Output::geometry() const +{ + if (!currentMode()) { + return QRect(); + } + + // We can't use QRect(d->pos, d->size), because d->size does not reflect the + // actual rotation() set by caller, it's only updated when we get update from + // KScreen, but not when user changes mode or rotation manually + + QSize size = currentMode()->size() / d->scale; + if (!isHorizontal()) { + size = size.transposed(); + } + + return QRect(d->pos, size); +} + +void Output::apply(const OutputPtr& other) +{ + typedef void (KScreen::Output::*ChangeSignal)(); + QList 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 = signalsBlocked(); + 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->primary != other->d->primary) { + changes << &Output::isPrimaryChanged; + setPrimary(other->d->primary); + } + if (d->clones != other->d->clones) { + changes << &Output::clonesChanged; + setClones(other->d->clones);; + } + if (!d->compareModeList(d->modeList, other->d->modeList)) { + changes << &Output::outputChanged; + changes << &Output::modesChanged; + } + + setPreferredModes(other->d->preferredModes); + ModeList modes; + Q_FOREACH (const ModePtr &otherMode, other->modes()) { + modes.insert(otherMode->id(), otherMode->clone()); + } + setModes(modes); + + // 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) +{ + if(output) { + dbg << "KScreen::Output(" << output->id() << " " + << output->name() + << (output->isConnected() ? "connected" : "disconnected") + << (output->isEnabled() ? "enabled" : "disabled") + << (output->isPrimary() ? "primary" : "") + << "pos:" << output->pos() << "res:" << output->size() + << "modeId:" << output->currentModeId() + << "scale:" << output->scale() + << "followPreferredMode:" << output->followPreferredMode() + << ")"; + } else { + dbg << "KScreen::Output(NULL)"; + } + return dbg; +} diff --git a/libkscreen-5.15.5/src/output.h b/libkscreen-5.15.5/src/output.h new file mode 100644 index 0000000000000000000000000000000000000000..b95139f1a09c6cd2f4c50d105bb3ccc81b845a18 --- /dev/null +++ b/libkscreen-5.15.5/src/output.h @@ -0,0 +1,287 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef OUTPUT_CONFIG_H +#define OUTPUT_CONFIG_H + +#include "mode.h" +#include "types.h" +#include "kscreen_export.h" + +#include +#include +#include +#include +#include +#include + +namespace KScreen { + +class Edid; + +class KSCREEN_EXPORT Output : public QObject +{ + Q_OBJECT + + public: + Q_ENUMS(Rotation) + Q_ENUMS(Type) + 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 isPrimaryChanged) + Q_PROPERTY(QList clones READ clones WRITE setClones NOTIFY clonesChanged) + 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) + + + enum Type { + Unknown, + VGA, + DVI, + DVII, + DVIA, + DVID, + HDMI, + Panel, + TV, + TVComposite, + TVSVideo, + TVComponent, + TVSCART, + TVC4, + DisplayPort + }; + + enum Rotation { + None = 1, + Left = 2, + Inverted = 4, + Right = 8 + }; + + 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 + */ + QString hash() const; + + Type type() 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); + + 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); + + QList clones() const; + void setClones(const QList &outputlist); + + 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 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); + + /** + * @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); + + void apply(const OutputPtr &other); + Q_SIGNALS: + void outputChanged(); + void posChanged(); + void sizeChanged(); + void currentModeIdChanged(); + void rotationChanged(); + void isConnectedChanged(); + void isEnabledChanged(); + void isPrimaryChanged(); + void clonesChanged(); + void scaleChanged(); + void followPreferredModeChanged(bool followPreferredMode); + + /** 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; + + 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 diff --git a/libkscreen-5.15.5/src/screen.cpp b/libkscreen-5.15.5/src/screen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d22a268601ad5e1a9cad17baa81dc5bfaa4b3491 --- /dev/null +++ b/libkscreen-5.15.5/src/screen.cpp @@ -0,0 +1,133 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2014 by Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#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& currentSize) +{ + 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); +} diff --git a/libkscreen-5.15.5/src/screen.h b/libkscreen-5.15.5/src/screen.h new file mode 100644 index 0000000000000000000000000000000000000000..f44757ef65a82b9563ec322e42b330e83300cb7c --- /dev/null +++ b/libkscreen-5.15.5/src/screen.h @@ -0,0 +1,108 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef SCREEN_CONFIG_H +#define SCREEN_CONFIG_H + +#include "types.h" +#include "kscreen_export.h" + +#include +#include + +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& currentSize); + + /** + * 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 diff --git a/libkscreen-5.15.5/src/setconfigoperation.cpp b/libkscreen-5.15.5/src/setconfigoperation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..26e4b24cb6ee25bd8f53163b48e69d932022c523 --- /dev/null +++ b/libkscreen-5.15.5/src/setconfigoperation.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#include "setconfigoperation.h" + +#include "abstractbackend.h" +#include "backendmanager_p.h" +#include "configoperation_p.h" +#include "config.h" +#include "configserializer_p.h" +#include "kscreen_debug.h" +#include "output.h" + +#include +#include + +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(); + + 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 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(); + 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; + Q_FOREACH (const KScreen::OutputPtr &output, config->outputs()) { + if (!output->isConnected() || !output->isEnabled()) { + 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); + Q_FOREACH (const KScreen::OutputPtr &output, config->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); + } +} + +#include "setconfigoperation.moc" diff --git a/libkscreen-5.15.5/src/setconfigoperation.h b/libkscreen-5.15.5/src/setconfigoperation.h new file mode 100644 index 0000000000000000000000000000000000000000..00adc290a4a7656c72ce76f8131803156335cf86 --- /dev/null +++ b/libkscreen-5.15.5/src/setconfigoperation.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Daniel Vratil + * + * 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 + * + */ + +#ifndef KSCREEN_SETCONFIGOPERATION_H +#define KSCREEN_SETCONFIGOPERATION_H + +#include "configoperation.h" +#include "types.h" +#include "kscreen_export.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 diff --git a/libkscreen-5.15.5/src/types.h b/libkscreen-5.15.5/src/types.h new file mode 100644 index 0000000000000000000000000000000000000000..f801ce89cf8b745bbba2c98c7a19c3cd9fb4ae76 --- /dev/null +++ b/libkscreen-5.15.5/src/types.h @@ -0,0 +1,42 @@ +/************************************************************************************* + * Copyright 2014 Daniel Vrátil * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_TYPES_H +#define KSCREEN_TYPES_H + +#include +#include + +namespace KScreen +{ + +class Config; +typedef QSharedPointer ConfigPtr; +class Screen; +typedef QSharedPointer ScreenPtr; +class Output; +typedef QSharedPointer OutputPtr; +typedef QMap OutputList; + +class Mode; +typedef QSharedPointer ModePtr; +typedef QMap ModeList; + +} + +#endif diff --git a/libkscreen-5.15.5/tests/CMakeLists.txt b/libkscreen-5.15.5/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1489d21383e13cd1431a81d48b4a272c28302041 --- /dev/null +++ b/libkscreen-5.15.5/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(printconfig testplugandplay.cpp testpnp.cpp) +target_link_libraries(printconfig Qt5::Gui KF5::Screen) + +add_subdirectory(kwayland) diff --git a/libkscreen-5.15.5/tests/kwayland/CMakeLists.txt b/libkscreen-5.15.5/tests/kwayland/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..050473e8ac3b2ba6512169c0cb17256fc196bc8b --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/CMakeLists.txt @@ -0,0 +1,6 @@ + +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) + diff --git a/libkscreen-5.15.5/tests/kwayland/main.cpp b/libkscreen-5.15.5/tests/kwayland/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3047b5f24dc6519695dae29d021fb74354f4ba1 --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/main.cpp @@ -0,0 +1,44 @@ +/************************************************************************************* + * Copyright 2014-2016 by Sebastian Kügler * + * * + * 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 * + *************************************************************************************/ + +#include "waylandtestserver.h" + +#include +#include + +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(); +} diff --git a/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.cpp b/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a8fdb8b868b9e60a706394784c46b5800d99b39 --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.cpp @@ -0,0 +1,255 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandconfigreader.h" + +#include + +#include +#include +#include +#include +#include + +#include "edid.h" + +using namespace KScreen; + +static QList 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(); + Q_FOREACH(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 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; + Q_FOREACH (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 KScreen::WaylandConfigReader::createOutputs(KWayland::Server::Display* display, QList& outputdevices) +{ + const QMap 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 outputs; + Q_FOREACH (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()); + + + Q_FOREACH (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; +} + diff --git a/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.h b/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.h new file mode 100644 index 0000000000000000000000000000000000000000..a0bf2a2b3522d69a49c7279d5e4e82e17ae40cbe --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/waylandconfigreader.h @@ -0,0 +1,51 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_WAYLAND_CONFIGREADER_H +#define KSCREEN_WAYLAND_CONFIGREADER_H + +#include +#include + +// KWayland +#include +#include +#include + +namespace KScreen +{ + +using namespace KWayland::Server; + +class WaylandConfigReader +{ + +public: + //static QList outputsFromConfig(const QString &configfile, KWayland::Server::Display *display); + static void outputsFromConfig(const QString &configfile, KWayland::Server::Display *display, QList& outputs); + static OutputDeviceInterface* createOutputDevice(const QVariantMap &outputConfig, KWayland::Server::Display *display); + static QList createOutputs(KWayland::Server::Display *display, QList& 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 diff --git a/libkscreen-5.15.5/tests/kwayland/waylandtestserver.cpp b/libkscreen-5.15.5/tests/kwayland/waylandtestserver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..019250e6a34a4474187a2297548a9d8f592c5aef --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/waylandtestserver.cpp @@ -0,0 +1,201 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "waylandtestserver.h" + +#include "waylandconfigreader.h" + +#include +#include +#include +#include +#include +#include + +#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() +{ + Q_FOREACH (const auto &o, m_outputs) { + delete o; + } + 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 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->scale(); + outputdevice->setScale(c->scale()); + } + } + + 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. ********"; + foreach (auto o, 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; + Q_FOREACH (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())); + +} diff --git a/libkscreen-5.15.5/tests/kwayland/waylandtestserver.h b/libkscreen-5.15.5/tests/kwayland/waylandtestserver.h new file mode 100644 index 0000000000000000000000000000000000000000..51e8fa65af01cdb1fed13a605339be18d2813d6b --- /dev/null +++ b/libkscreen-5.15.5/tests/kwayland/waylandtestserver.h @@ -0,0 +1,88 @@ +/************************************************************************************* + * Copyright 2014-2015 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_WAYLAND_TESTSERVER_H +#define KSCREEN_WAYLAND_TESTSERVER_H + +#include + +// KWayland +#include +#include +#include +#include +#include +#include +#include +#include + +namespace KScreen +{ +class WaylandConfig; +class WaylandOutput; + +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 pickupConfigFile(const QString &configfile); + + void showOutputs(); + KWayland::Server::Display* display(); + QList 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 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 diff --git a/libkscreen-5.15.5/tests/testplugandplay.cpp b/libkscreen-5.15.5/tests/testplugandplay.cpp new file mode 100644 index 0000000000000000000000000000000000000000..46a7f971a11ae2f999ecfa26bf3128e017a4e8cf --- /dev/null +++ b/libkscreen-5.15.5/tests/testplugandplay.cpp @@ -0,0 +1,39 @@ +/************************************************************************************* + * Copyright 2014 by Sebastian Kügler * + * * + * 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 * + *************************************************************************************/ + +#include "testpnp.h" + +#include +#include + +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(); +} diff --git a/libkscreen-5.15.5/tests/testpnp.cpp b/libkscreen-5.15.5/tests/testpnp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79a8396a36c7ad0191434497c251406406e343b0 --- /dev/null +++ b/libkscreen-5.15.5/tests/testpnp.cpp @@ -0,0 +1,173 @@ +/************************************************************************************* + * Copyright 2013 Alejandro Fiestas Olivares * + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#include "testpnp.h" + +#include "../src/config.h" +#include "../src/configmonitor.h" +#include "../src/edid.h" +#include "../src/mode.h" +#include "../src/output.h" +#include "../src/screen.h" +#include "../src/getconfigoperation.h" + +#include +#include +#include +//#include + +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(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(); + Q_FOREACH(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() << "Primary: " << output->isPrimary(); + 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(); + Q_FOREACH(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"; + } + } +} diff --git a/libkscreen-5.15.5/tests/testpnp.h b/libkscreen-5.15.5/tests/testpnp.h new file mode 100644 index 0000000000000000000000000000000000000000..3e76598ad174acbfdbd159e67b263d23625f7d92 --- /dev/null +++ b/libkscreen-5.15.5/tests/testpnp.h @@ -0,0 +1,53 @@ +/************************************************************************************* + * Copyright 2014 Sebastian Kügler * + * * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA * + *************************************************************************************/ + +#ifndef KSCREEN_TESTPNP_H +#define KSCREEN_TESTPNP_H + +#include "../src/config.h" +#include "../src/screen.h" + +#include + +namespace KScreen +{ +class Output; +class QScreenOutput; +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 diff --git a/libkscreen-5.6.4-rhel-nowayland.patch b/libkscreen-5.6.4-rhel-nowayland.patch new file mode 100644 index 0000000000000000000000000000000000000000..7c0bb8113f33f916f70495b1cc798802a8ae1fe0 --- /dev/null +++ b/libkscreen-5.6.4-rhel-nowayland.patch @@ -0,0 +1,60 @@ +diff -up libkscreen-5.6.4/autotests/CMakeLists.txt.than libkscreen-5.6.4/autotests/CMakeLists.txt +--- libkscreen-5.6.4/autotests/CMakeLists.txt.than 2016-05-24 17:08:06.186036831 +0200 ++++ libkscreen-5.6.4/autotests/CMakeLists.txt 2016-05-24 17:08:28.144385812 +0200 +@@ -1,7 +1,5 @@ + add_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}) +@@ -25,16 +23,6 @@ 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) +- + set(KSCREEN_WAYLAND_LIBS "") + set(KSCREEN_WAYLAND_SRCS "") + +diff -up libkscreen-5.6.4/backends/CMakeLists.txt.than libkscreen-5.6.4/backends/CMakeLists.txt +--- libkscreen-5.6.4/backends/CMakeLists.txt.than 2016-05-24 17:02:59.142120753 +0200 ++++ libkscreen-5.6.4/backends/CMakeLists.txt 2016-05-24 17:03:05.579930820 +0200 +@@ -1,6 +1,5 @@ + add_subdirectory(fake) + add_subdirectory(qscreen) +-add_subdirectory(kwayland) + + if(${XCB_RANDR_FOUND}) + message(STATUS "Will build xrandr backend.") +diff -up libkscreen-5.6.4/CMakeLists.txt.than libkscreen-5.6.4/CMakeLists.txt +--- libkscreen-5.6.4/CMakeLists.txt.than 2016-05-24 17:03:58.179375794 +0200 ++++ libkscreen-5.6.4/CMakeLists.txt 2016-05-24 17:04:13.841911809 +0200 +@@ -19,10 +19,6 @@ include(CheckCXXCompilerFlag) + set(REQUIRED_QT_VERSION 5.2.0) + find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Core DBus Gui Test X11Extras) + +-# Wayland backend +-find_package(KF5Wayland CONFIG REQUIRED) +-add_feature_info("KF5Wayland" KF5Wayland_FOUND "Required for building libkscreen's KWayland backend") +- + # xrandr backend + + find_package(XCB COMPONENTS XCB RANDR) +diff -up libkscreen-5.6.4/tests/CMakeLists.txt.than libkscreen-5.6.4/tests/CMakeLists.txt +--- libkscreen-5.6.4/tests/CMakeLists.txt.than 2016-05-24 17:07:03.588887505 +0200 ++++ libkscreen-5.6.4/tests/CMakeLists.txt 2016-05-24 17:07:11.074666792 +0200 +@@ -1,4 +1,2 @@ + add_executable(printconfig testplugandplay.cpp testpnp.cpp) + target_link_libraries(printconfig Qt5::Gui KF5::Screen) +- +-add_subdirectory(kwayland) diff --git a/libkscreen-qt5.spec b/libkscreen-qt5.spec new file mode 100644 index 0000000000000000000000000000000000000000..e47fd80cf46ed280d60f2575a2903e77e8ce7ba1 --- /dev/null +++ b/libkscreen-qt5.spec @@ -0,0 +1,392 @@ +%global base_name libkscreen + +Name: libkscreen-qt5 +Summary: KDE display configuration library +Version: 5.15.5 +Release: 1%{?dist} + +License: GPLv2+ +URL: https://cgit.kde.org/%{base_name}.git + +%global revision %(echo %{version} | cut -d. -f3) +%if %{revision} >= 50 +%global stable unstable +%else +%global stable stable +%endif +Source0: http://download.kde.org/%{stable}/plasma/%{version}/%{base_name}-%{version}.tar.xz +Patch1: libkscreen-5.6.4-rhel-nowayland.patch + +# filter plugin provides +%global __provides_exclude_from ^(%{_kf5_plugindir}/.*\\.so)$ + +BuildRequires: extra-cmake-modules +BuildRequires: kf5-rpm-macros +%if 0%{?fedora} || 0%{?rhel} > 7 +BuildRequires: kf5-kwayland-devel >= 5.22 +%endif +BuildRequires: libX11-devel +BuildRequires: libxcb-devel +BuildRequires: libXrandr-devel +BuildRequires: qt5-qtbase-devel +BuildRequires: qt5-qtx11extras-devel + +Requires: kf5-filesystem + +Provides: kf5-kscreen%{?_isa} = %{version}-%{release} +Provides: kf5-kscreen = %{version}-%{release} +Obsoletes: kf5-kscreen <= 1:5.2.0 + + +%description +LibKScreen is a library that provides access to current configuration +of connected displays and ways to change the configuration. + +%package devel +Summary: Development files for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Provides: kf5-kscreen-devel = %{version}-%{release} +Provides: kf5-kscreen-devel%{?_isa} = %{version}-%{release} +Obsoletes: kf5-kscreen-devel <= 1:5.2.0 +%description devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}. + + +%prep +%setup -q -n %{base_name}-%{version} +%if 0%{?rhel} && 0%{?rhel} <= 7 +%patch1 -p1 -b .nowayland +%endif + + +%build +mkdir %{_target_platform} +pushd %{_target_platform} +%{cmake_kf5} .. +popd + +make %{?_smp_mflags} -C %{_target_platform} + + +%install +make install/fast DESTDIR=%{buildroot} -C %{_target_platform} + + +%ldconfig_scriptlets + +%files +%license COPYING +%{_kf5_bindir}/kscreen-doctor +%{_kf5_libexecdir}/kscreen_backend_launcher +%{_kf5_libdir}/libKF5Screen.so.5.* +%{_kf5_libdir}/libKF5Screen.so.7* +%{_kf5_plugindir}/kscreen/ +%{_datadir}/dbus-1/services/org.kde.kscreen.service +%{_sysconfdir}/xdg/libkscreen.categories + +%files devel +%{_kf5_includedir}/KScreen/ +%{_kf5_includedir}/kscreen_version.h +%{_kf5_libdir}/libKF5Screen.so +%{_kf5_libdir}/cmake/KF5Screen/ +%{_libdir}/pkgconfig/kscreen2.pc +%{_kf5_archdatadir}/mkspecs/modules/qt_KScreen.pri + + +%changelog +* Thu May 09 2019 Martin Kyral - 5.15.5-1 +- 5.15.5 + +* Wed Apr 03 2019 Rex Dieter - 5.15.4-1 +- 5.15.4 + +* Tue Mar 12 2019 Martin Kyral - 5.15.3-1 +- 5.15.3 + +* Tue Feb 26 2019 Rex Dieter - 5.15.2-1 +- 5.15.2 + +* Tue Feb 19 2019 Rex Dieter - 5.15.1-1 +- 5.15.1 + +* Wed Feb 13 2019 Martin Kyral - 5.15.0-1 +- 5.15.0 + +* Fri Feb 01 2019 Fedora Release Engineering - 5.14.90-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Sun Jan 20 2019 Martin Kyral - 5.14.90-1 +- 5.14.90 + +* Tue Nov 27 2018 Rex Dieter - 5.14.4-1 +- 5.14.4 + +* Thu Nov 08 2018 Martin Kyral - 5.14.3-1 +- 5.14.3 + +* Wed Oct 24 2018 Rex Dieter - 5.14.2-1 +- 5.14.2 + +* Tue Oct 16 2018 Rex Dieter - 5.14.1-1 +- 5.14.1 + +* Fri Oct 05 2018 Rex Dieter - 5.14.0-1 +- 5.14.0 + +* Fri Sep 14 2018 Martin Kyral - 5.13.90-1 +- 5.13.90 + +* Tue Sep 04 2018 Rex Dieter - 5.13.5-1 +- 5.13.5 + +* Thu Aug 02 2018 Rex Dieter - 5.13.4-1 +- 5.13.4 + +* Fri Jul 13 2018 Fedora Release Engineering - 5.13.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed Jul 11 2018 Martin Kyral - 5.13.3-1 +- 5.13.3 + +* Mon Jul 09 2018 Martin Kyral - 5.13.2-1 +- 5.13.2 + +* Tue Jun 19 2018 Martin Kyral - 5.13.1-1 +- 5.13.1 + +* Sat Jun 09 2018 Rex Dieter - 5.13.0-1 +- 5.13.0 + +* Fri May 18 2018 Martin Kyral - 5.12.90-1 +- 5.12.90 + +* Tue May 01 2018 Rex Dieter - 5.12.5-1 +- 5.12.5 + +* Tue Mar 27 2018 Rex Dieter - 5.12.4-1 +- 5.12.4 + +* Tue Mar 06 2018 Rex Dieter - 5.12.3-1 +- 5.12.3 + +* Wed Feb 21 2018 Jan Grulich - 5.12.2-1 +- 5.12.2 + +* Tue Feb 13 2018 Jan Grulich - 5.12.1-1 +- 5.12.1 + +* Wed Feb 07 2018 Fedora Release Engineering - 5.12.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Fri Feb 02 2018 Jan Grulich - 5.12.0-1 +- 5.12.0 + +* Mon Jan 15 2018 Jan Grulich - 5.11.95-1 +- 5.11.95 + +* Tue Jan 02 2018 Rex Dieter - 5.11.5-1 +- 5.11.5 + +* Thu Nov 30 2017 Martin Kyral - 5.11.4-1 +- 5.11.4 + +* Wed Nov 08 2017 Rex Dieter - 5.11.3-1 +- 5.11.3 + +* Wed Oct 25 2017 Martin Kyral - 5.11.2-1 +- 5.11.2 + +* Tue Oct 17 2017 Rex Dieter - 5.11.1-1 +- 5.11.1 + +* Wed Oct 11 2017 Martin Kyral - 5.11.0-1 +- 5.11.0 + +* Thu Aug 24 2017 Rex Dieter - 5.10.5-1 +- 5.10.5 + +* Thu Aug 03 2017 Fedora Release Engineering - 5.10.4-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 5.10.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 21 2017 Rex Dieter - 5.10.4-1 +- 5.10.4 + +* Tue Jun 27 2017 Rex Dieter - 5.10.3-1 +- 5.10.3 + +* Thu Jun 15 2017 Rex Dieter - 5.10.2-1 +- 5.10.2 + +* Tue Jun 06 2017 Rex Dieter - 5.10.1-1 +- 5.10.1 + +* Wed May 31 2017 Jan Grulich - 5.10.0-1 +- 5.10.0 + +* Wed Apr 26 2017 Rex Dieter - 5.9.5-1 +- 5.9.5 + +* Thu Mar 23 2017 Rex Dieter - 5.9.4-1 +- 5.9.4 + +* Sat Mar 04 2017 Rex Dieter - 5.9.3-2 +- rebuild + +* Wed Mar 01 2017 Jan Grulich - 5.9.3-1 +- 5.9.3 + +* Tue Feb 21 2017 Rex Dieter - 5.8.6-1 +- 5.8.6 + +* Fri Feb 10 2017 Fedora Release Engineering - 5.8.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Jan 02 2017 Rex Dieter - 5.8.5-2 +- filter plugin provides + +* Wed Dec 28 2016 Rex Dieter - 5.8.5-1 +- 5.8.5 + +* Tue Nov 22 2016 Rex Dieter - 5.8.4-1 +- 5.8.4 + +* Tue Nov 01 2016 Rex Dieter - 5.8.3-1 +- 5.8.3 + +* Tue Oct 18 2016 Rex Dieter - 5.8.2-1 +- 5.8.2 + +* Tue Oct 11 2016 Rex Dieter - 5.8.1-1 +- 5.8.1 + +* Thu Sep 29 2016 Rex Dieter - 5.8.0-1 +- 5.8.0 + +* Thu Sep 22 2016 Rex Dieter - 5.7.95-1 +- 5.7.95 + +* Tue Sep 13 2016 Rex Dieter - 5.7.5-1 +- 5.7.5 + +* Tue Aug 23 2016 Rex Dieter - 5.7.4-1 +- 5.7.4 + +* Tue Aug 02 2016 Rex Dieter - 5.7.3-1 +- 5.7.3 + +* Tue Jul 19 2016 Rex Dieter - 5.7.2-1 +- 5.7.2 + +* Tue Jul 19 2016 Rex Dieter - 5.7.1-2 +- rebuild (qt5) + +* Tue Jul 12 2016 Rex Dieter - 5.7.1-1 +- 5.7.1 + +* Thu Jun 30 2016 Rex Dieter - 5.7.0-1 +- 5.7.0 + +* Sat Jun 25 2016 Rex Dieter - 5.6.95-1 +- 5.6.95 + +* Tue Jun 14 2016 Rex Dieter - 5.6.5-1 +- 5.6.5 + +* Wed May 25 2016 Than Ngo - 5.6.4-2 +- disable wayland for epel7 + +* Sat May 14 2016 Rex Dieter - 5.6.4-1 +- 5.6.4 + +* Tue Apr 19 2016 Rex Dieter - 5.6.3-1 +- 5.6.3 + +* Sat Apr 09 2016 Rex Dieter - 5.6.2-1 +- 5.6.2 + +* Sat Apr 09 2016 Rex Dieter - 5.6.1-2 +- relax BR: kf5-kwayland-devel dep +- track library soname + +* Fri Apr 08 2016 Rex Dieter - 5.6.1-1 +- 5.6.1 + +* Tue Mar 01 2016 Daniel Vrátil - 5.5.5-1 +- Plasma 5.5.5 + +* Thu Feb 04 2016 Fedora Release Engineering - 5.5.4-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jan 27 2016 Daniel Vrátil - 5.5.4-1 +- Plasma 5.5.4 + +* Thu Jan 07 2016 Daniel Vrátil - 5.5.3-1 +- Plasma 5.5.3 + +* Thu Dec 31 2015 Rex Dieter - 5.5.2-1 +- 5.5.2 + +* Fri Dec 18 2015 Daniel Vrátil - 5.5.1-1 +- Plasma 5.5.1 + +* Thu Dec 03 2015 Daniel Vrátil - 5.5.0-1 +- Plasma 5.5.0 + +* Wed Nov 25 2015 Daniel Vrátil - 5.4.95-1 +- Plasma 5.4.95 + +* Thu Nov 05 2015 Daniel Vrátil - 5.4.3-1 +- Plasma 5.4.3 + +* Thu Oct 01 2015 Rex Dieter - 5.4.2-1 +- 5.4.2 + +* Thu Oct 01 2015 Rex Dieter 5.4.1-2 +- .spec cosmetics + +* Wed Sep 09 2015 Rex Dieter - 5.4.1-1 +- 5.4.1 + +* Fri Aug 21 2015 Daniel Vrátil - 5.4.0-1 +- Plasma 5.4.0 + +* Thu Aug 13 2015 Daniel Vrátil - 5.3.95-1 +- Plasma 5.3.95 + +* Thu Jun 25 2015 Daniel Vrátil - 5.3.2-1 +- Plasma 5.3.2 + +* Wed Jun 17 2015 Fedora Release Engineering - 5.3.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Tue May 26 2015 Daniel Vrátil - 5.3.1-1 +- Plasma 5.3.1 + +* Thu Apr 30 2015 Daniel Vrátil - 5.3.0-2 +- Fix Obsoletes once more + +* Mon Apr 27 2015 Daniel Vrátil - 5.3.0-1 +- Plasma 5.3.0 + +* Wed Apr 22 2015 Daniel Vrátil - 5.2.95-1 +- Plasma 5.2.95 + +* Fri Mar 20 2015 Daniel Vrátil - 5.2.2-1 +- Plasma 5.2.2 + +* Fri Feb 27 2015 Daniel Vrátil - 5.2.1-2 +- Rebuild (GCC 5) + +* Tue Feb 24 2015 Daniel Vrátil - 5.2.1-1 +- Plasma 5.2.1 + +* Wed Jan 28 2015 Daniel Vrátil - 5.2.0-2 +- Fix Obsoletes + +* Mon Jan 26 2015 Daniel Vrátil - 5.2.0-1 +- Plasma 5.2.0 + (new package, forked from libkscreen)