diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt new file mode 100644 index 0000000..0741db7 --- /dev/null +++ b/LICENSES/BSD-3-Clause.txt @@ -0,0 +1,26 @@ +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/LGPL-2.1-only.txt b/LICENSES/LGPL-2.1-only.txt new file mode 100644 index 0000000..130dffb --- /dev/null +++ b/LICENSES/LGPL-2.1-only.txt @@ -0,0 +1,467 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software Foundation +and other authors who decide to use it. You can use it too, but we suggest +you first think carefully about whether this license or the ordinary General +Public License is the better strategy to use in any particular case, based +on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that +you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent holder. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order +to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It +also provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto standard. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables +many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a modified +version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in +order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Lesser General +Public License (also called "this License"). Each licensee is addressed as +"you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the library +already present on the user's computer system, rather than copying library +functions into the executable, and (2) will operate properly with a modified +version of the library, if the user installs one, as long as the modified +version is interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +e) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the materials to be distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +< one line to give the library's name and an idea of what it does. > + +Copyright (C) < year > < name of author > + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information +on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-2.1-or-later.txt b/LICENSES/LGPL-2.1-or-later.txt new file mode 100644 index 0000000..04bb156 --- /dev/null +++ b/LICENSES/LGPL-2.1-or-later.txt @@ -0,0 +1,468 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software Foundation +and other authors who decide to use it. You can use it too, but we suggest +you first think carefully about whether this license or the ordinary General +Public License is the better strategy to use in any particular case, based +on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. +Our General Public Licenses are designed to make sure that you have the freedom +to distribute copies of free software (and charge for this service if you +wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that +you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent holder. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order +to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It +also provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto standard. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables +many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a modified +version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in +order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Lesser General +Public License (also called "this License"). Each licensee is addressed as +"you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the library +already present on the user's computer system, rather than copying library +functions into the executable, and (2) will operate properly with a modified +version of the library, if the user installs one, as long as the modified +version is interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +e) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the materials to be distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), conditions are imposed +on you (whether by court order, agreement or otherwise) that contradict the +conditions of this License, they do not excuse you from the conditions of +this License. If you cannot distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE +OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA +OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES +OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + + + +Copyright (C) + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-3.0-only.txt b/LICENSES/LGPL-3.0-only.txt new file mode 100644 index 0000000..bd405af --- /dev/null +++ b/LICENSES/LGPL-3.0-only.txt @@ -0,0 +1,163 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates the terms +and conditions of version 3 of the GNU General Public License, supplemented +by the additional permissions listed below. + + 0. Additional Definitions. + + + +As used herein, "this License" refers to version 3 of the GNU Lesser General +Public License, and the "GNU GPL" refers to version 3 of the GNU General Public +License. + + + +"The Library" refers to a covered work governed by this License, other than +an Application or a Combined Work as defined below. + + + +An "Application" is any work that makes use of an interface provided by the +Library, but which is not otherwise based on the Library. Defining a subclass +of a class defined by the Library is deemed a mode of using an interface provided +by the Library. + + + +A "Combined Work" is a work produced by combining or linking an Application +with the Library. The particular version of the Library with which the Combined +Work was made is also called the "Linked Version". + + + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding +Source for the Combined Work, excluding any source code for portions of the +Combined Work that, considered in isolation, are based on the Application, +and not on the Linked Version. + + + +The "Corresponding Application Code" for a Combined Work means the object +code and/or source code for the Application, including any data and utility +programs needed for reproducing the Combined Work from the Application, but +excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License without +being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a facility +refers to a function or data to be supplied by an Application that uses the +facility (other than as an argument passed when the facility is invoked), +then you may convey a copy of the modified version: + +a) under this License, provided that you make a good faith effort to ensure +that, in the event an Application does not supply the function or data, the +facility still operates, and performs whatever part of its purpose remains +meaningful, or + +b) under the GNU GPL, with none of the additional permissions of this License +applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from a header +file that is part of the Library. You may convey such object code under terms +of your choice, provided that, if the incorporated material is not limited +to numerical parameters, data structure layouts and accessors, or small macros, +inline functions and templates (ten or fewer lines in length), you do both +of the following: + +a) Give prominent notice with each copy of the object code that the Library +is used in it and that the Library and its use are covered by this License. + +b) Accompany the object code with a copy of the GNU GPL and this license document. + + 4. Combined Works. + +You may convey a Combined Work under terms of your choice that, taken together, +effectively do not restrict modification of the portions of the Library contained +in the Combined Work and reverse engineering for debugging such modifications, +if you also do each of the following: + +a) Give prominent notice with each copy of the Combined Work that the Library +is used in it and that the Library and its use are covered by this License. + +b) Accompany the Combined Work with a copy of the GNU GPL and this license +document. + +c) For a Combined Work that displays copyright notices during execution, include +the copyright notice for the Library among these notices, as well as a reference +directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + +0) Convey the Minimal Corresponding Source under the terms of this License, +and the Corresponding Application Code in a form suitable for, and under terms +that permit, the user to recombine or relink the Application with a modified +version of the Linked Version to produce a modified Combined Work, in the +manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + +1) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (a) uses at run time a copy of the Library +already present on the user's computer system, and (b) will operate properly +with a modified version of the Library that is interface-compatible with the +Linked Version. + +e) Provide Installation Information, but only if you would otherwise be required +to provide such information under section 6 of the GNU GPL, and only to the +extent that such information is necessary to install and execute a modified +version of the Combined Work produced by recombining or relinking the Application +with a modified version of the Linked Version. (If you use option 4d0, the +Installation Information must accompany the Minimal Corresponding Source and +Corresponding Application Code. If you use option 4d1, you must provide the +Installation Information in the manner specified by section 6 of the GNU GPL +for conveying Corresponding Source.) + + 5. Combined Libraries. + +You may place library facilities that are a work based on the Library side +by side in a single library together with other library facilities that are +not Applications and are not covered by this License, and convey such a combined +library under terms of your choice, if you do both of the following: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities, conveyed under the +terms of this License. + +b) Give prominent notice with the combined library that part of it is a work +based on the Library, and explaining where to find the accompanying uncombined +form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions of the +GNU Lesser General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to address +new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you +received it specifies that a certain numbered version of the GNU Lesser General +Public License "or any later version" applies to it, you have the option of +following the terms and conditions either of that published version or of +any later version published by the Free Software Foundation. If the Library +as you received it does not specify a version number of the GNU Lesser General +Public License, you may choose any version of the GNU Lesser General Public +License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether +future versions of the GNU Lesser General Public License shall apply, that +proxy's public statement of acceptance of any version is permanent authorization +for you to choose that version for the Library. diff --git a/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt b/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt new file mode 100644 index 0000000..232b3c5 --- /dev/null +++ b/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt @@ -0,0 +1,12 @@ +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 3 of the license or (at your option) any later version +that is accepted by the membership of KDE e.V. (or its successor +approved by the membership of KDE e.V.), which shall act as a +proxy as defined in Section 6 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. diff --git a/LICENSES/MIT-CMU.txt b/LICENSES/MIT-CMU.txt new file mode 100644 index 0000000..81c351b --- /dev/null +++ b/LICENSES/MIT-CMU.txt @@ -0,0 +1,17 @@ + By obtaining, using, and/or copying this software and/or +its associated documentation, you agree that you have read, understood, and +will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its associated +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appears in all copies, and that both that +copyright notice and this permission notice appear in supporting documentation, +and that the name of the copyright holder not be used in advertising or publicity +pertaining to distribution of the software without specific, written permission. + +THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/autotests/client/test_compositor.cpp b/autotests/client/test_compositor.cpp index 9e6e6b4..67b5872 100644 --- a/autotests/client/test_compositor.cpp +++ b/autotests/client/test_compositor.cpp @@ -1,161 +1,147 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/surface_interface.h" class TestCompositor : public QObject { Q_OBJECT public: explicit TestCompositor(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testDestroy(); void testCast(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-compositor-0"); TestCompositor::TestCompositor(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_thread(nullptr) { } void TestCompositor::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); KWayland::Client::Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); // here we need a shm pool m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); } void TestCompositor::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestCompositor::testDestroy() { using namespace KWayland::Client; connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); QVERIFY(m_compositor->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!m_compositor->isValid()); // calling destroy again should not fail m_compositor->destroy(); } void TestCompositor::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(compositorSpy.wait()); Compositor c; auto wlComp = registry.bindCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value()); c.setup(wlComp); QCOMPARE((wl_compositor*)c, wlComp); const Compositor &c2(c); QCOMPARE((wl_compositor*)c2, wlComp); } QTEST_GUILESS_MAIN(TestCompositor) #include "test_compositor.moc" diff --git a/autotests/client/test_datadevice.cpp b/autotests/client/test_datadevice.cpp index bc813fd..7aec560 100644 --- a/autotests/client/test_datadevice.cpp +++ b/autotests/client/test_datadevice.cpp @@ -1,622 +1,608 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/compositor.h" #include "../../src/client/keyboard.h" #include "../../src/client/pointer.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/datasource_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/pointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include class TestDataDevice : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testDrag_data(); void testDrag(); void testDragInternally_data(); void testDragInternally(); void testSetSelection(); void testSendSelectionOnSeat(); void testReplaceSource(); void testDestroy(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::SeatInterface *m_seatInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::Seat *m_seat = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0"); void TestDataDevice::init() { qRegisterMetaType(); using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QVERIFY(seatSpy.isValid()); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); QVERIFY(dataDeviceManagerSpy.wait()); m_dataDeviceManager = registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value(), this); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasPointer(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); QVERIFY(seatSpy.wait()); m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); QVERIFY(m_seat->isValid()); QSignalSpy pointerChangedSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerChangedSpy.isValid()); QVERIFY(pointerChangedSpy.wait()); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); QVERIFY(m_compositor->isValid()); } void TestDataDevice::cleanup() { if (m_dataDeviceManager) { delete m_dataDeviceManager; m_dataDeviceManager = nullptr; } if (m_seat) { delete m_seat; m_seat = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestDataDevice::testCreate() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QCOMPARE(deviceInterface->seat(), m_seatInterface); QVERIFY(!deviceInterface->dragSource()); QVERIFY(!deviceInterface->origin()); QVERIFY(!deviceInterface->icon()); QVERIFY(!deviceInterface->selection()); QVERIFY(deviceInterface->parentResource()); QVERIFY(!m_seatInterface->selection()); m_seatInterface->setSelection(deviceInterface); QCOMPARE(m_seatInterface->selection(), deviceInterface); // and destroy QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); dataDevice.reset(); QVERIFY(destroyedSpy.wait()); QVERIFY(!m_seatInterface->selection()); } void TestDataDevice::testDrag_data() { QTest::addColumn("hasGrab"); QTest::addColumn("hasPointerFocus"); QTest::addColumn("success"); QTest::newRow("grab and focus") << true << true << true; QTest::newRow("no grab") << false << true << false; QTest::newRow("no focus") << true << false << false; QTest::newRow("no grab, no focus") << false << false << false; } void TestDataDevice::testDrag() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sourceInterface = dataSourceCreatedSpy.first().first().value(); QVERIFY(sourceInterface); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 1); auto surfaceInterface = surfaceCreatedSpy.first().first().value(); // now we have all we need to start a drag operation QSignalSpy dragStartedSpy(deviceInterface, SIGNAL(dragStarted())); QVERIFY(dragStartedSpy.isValid()); // first we need to fake the pointer enter QFETCH(bool, hasGrab); QFETCH(bool, hasPointerFocus); QFETCH(bool, success); if (!hasGrab) { // in case we don't have grab, still generate a pointer serial to make it more interesting m_seatInterface->pointerButtonPressed(Qt::LeftButton); } if (hasPointerFocus) { m_seatInterface->setFocusedPointerSurface(surfaceInterface); } if (hasGrab) { m_seatInterface->pointerButtonPressed(Qt::LeftButton); } // TODO: This test would be better, if it could also test that a client trying to guess // the last serial of a different client can't start a drag. const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0; QCoreApplication::processEvents(); // finally start the drag dataDevice->startDrag(pointerButtonSerial, dataSource.data(), surface.data()); QCOMPARE(dragStartedSpy.wait(500), success); QCOMPARE(!dragStartedSpy.isEmpty(), success); QCOMPARE(deviceInterface->dragSource(), success ? sourceInterface : nullptr); QCOMPARE(deviceInterface->origin(), success ? surfaceInterface : nullptr); QVERIFY(!deviceInterface->icon()); } void TestDataDevice::testDragInternally_data() { QTest::addColumn("hasGrab"); QTest::addColumn("hasPointerFocus"); QTest::addColumn("success"); QTest::newRow("grab and focus") << true << true << true; QTest::newRow("no grab") << false << true << false; QTest::newRow("no focus") << true << false << false; QTest::newRow("no grab, no focus") << false << false << false; } void TestDataDevice::testDragInternally() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 1); auto surfaceInterface = surfaceCreatedSpy.first().first().value(); QScopedPointer iconSurface(m_compositor->createSurface()); QVERIFY(iconSurface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 2); auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value(); // now we have all we need to start a drag operation QSignalSpy dragStartedSpy(deviceInterface, SIGNAL(dragStarted())); QVERIFY(dragStartedSpy.isValid()); // first we need to fake the pointer enter QFETCH(bool, hasGrab); QFETCH(bool, hasPointerFocus); QFETCH(bool, success); if (!hasGrab) { // in case we don't have grab, still generate a pointer serial to make it more interesting m_seatInterface->pointerButtonPressed(Qt::LeftButton); } if (hasPointerFocus) { m_seatInterface->setFocusedPointerSurface(surfaceInterface); } if (hasGrab) { m_seatInterface->pointerButtonPressed(Qt::LeftButton); } // TODO: This test would be better, if it could also test that a client trying to guess // the last serial of a different client can't start a drag. const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0; QCoreApplication::processEvents(); // finally start the internal drag dataDevice->startDragInternally(pointerButtonSerial, surface.data(), iconSurface.data()); QCOMPARE(dragStartedSpy.wait(500), success); QCOMPARE(!dragStartedSpy.isEmpty(), success); QVERIFY(!deviceInterface->dragSource()); QCOMPARE(deviceInterface->origin(), success ? surfaceInterface : nullptr); QCOMPARE(deviceInterface->icon(), success ? iconSurfaceInterface : nullptr); } void TestDataDevice::testSetSelection() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer pointer(m_seat->createPointer()); QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataDeviceCreated(KWayland::Server::DataDeviceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); QCOMPARE(dataDeviceCreatedSpy.count(), 1); auto deviceInterface = dataDeviceCreatedSpy.first().first().value(); QVERIFY(deviceInterface); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sourceInterface = dataSourceCreatedSpy.first().first().value(); QVERIFY(sourceInterface); // everything setup, now we can test setting the selection QSignalSpy selectionChangedSpy(deviceInterface, SIGNAL(selectionChanged(KWayland::Server::DataSourceInterface*))); QVERIFY(selectionChangedSpy.isValid()); QSignalSpy selectionClearedSpy(deviceInterface, SIGNAL(selectionCleared())); QVERIFY(selectionClearedSpy.isValid()); QVERIFY(!deviceInterface->selection()); dataDevice->setSelection(1, dataSource.data()); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QCOMPARE(selectionClearedSpy.count(), 0); QCOMPARE(selectionChangedSpy.first().first().value(), sourceInterface); QCOMPARE(deviceInterface->selection(), sourceInterface); // send selection to datadevice QSignalSpy selectionOfferedSpy(dataDevice.data(), SIGNAL(selectionOffered(KWayland::Client::DataOffer*))); QVERIFY(selectionOfferedSpy.isValid()); deviceInterface->sendSelection(deviceInterface); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); auto dataOffer = selectionOfferedSpy.first().first().value(); QVERIFY(dataOffer); QCOMPARE(dataOffer->offeredMimeTypes().count(), 1); QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); // sending a new mimetype to the selection, should be announced in the offer QSignalSpy mimeTypeAddedSpy(dataOffer, SIGNAL(mimeTypeOffered(QString))); QVERIFY(mimeTypeAddedSpy.isValid()); dataSource->offer(QStringLiteral("text/html")); QVERIFY(mimeTypeAddedSpy.wait()); QCOMPARE(mimeTypeAddedSpy.count(), 1); QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html")); QCOMPARE(dataOffer->offeredMimeTypes().count(), 2); QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html")); // now clear the selection dataDevice->clearSelection(1); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QCOMPARE(selectionClearedSpy.count(), 1); QVERIFY(!deviceInterface->selection()); // set another selection dataDevice->setSelection(2, dataSource.data()); QVERIFY(selectionChangedSpy.wait()); // now unbind the dataDevice QSignalSpy unboundSpy(deviceInterface, &DataDeviceInterface::unbound); QVERIFY(unboundSpy.isValid()); dataDevice.reset(); QVERIFY(unboundSpy.wait()); // send a selection to the unbound data device deviceInterface->sendSelection(deviceInterface); } void TestDataDevice::testSendSelectionOnSeat() { // this test verifies that the selection is sent when setting a focused keyboard using namespace KWayland::Client; using namespace KWayland::Server; // first add keyboard support to Seat QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(keyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardChangedSpy.wait()); // now create DataDevice, Keyboard and a Surface QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); auto serverDataDevice = dataDeviceCreatedSpy.first().first().value(); QVERIFY(serverDataDevice); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(keyboard->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // now set the selection QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); dataDevice->setSelection(1, dataSource.data()); // we should get a selection offered for that on the data device QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered); QVERIFY(selectionOfferedSpy.isValid()); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); // now unfocus the keyboard m_seatInterface->setFocusedKeyboardSurface(nullptr); // if setting the same surface again, we should get another offer m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 2); // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd m_seatInterface->setFocusedKeyboardSurface(nullptr); QSignalSpy unboundSpy(serverDataDevice, &Resource::unbound); QVERIFY(unboundSpy.isValid()); dataDevice.reset(); QVERIFY(unboundSpy.wait()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); } void TestDataDevice::testReplaceSource() { // this test verifies that replacing a data source cancels the previous source using namespace KWayland::Client; using namespace KWayland::Server; // first add keyboard support to Seat QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(keyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardChangedSpy.wait()); // now create DataDevice, Keyboard and a Surface QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(dataDeviceCreatedSpy.isValid()); QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); QVERIFY(dataDeviceCreatedSpy.wait()); auto serverDataDevice = dataDeviceCreatedSpy.first().first().value(); QVERIFY(serverDataDevice); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(keyboard->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // now set the selection QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); dataSource->offer(QStringLiteral("text/plain")); dataDevice->setSelection(1, dataSource.data()); QSignalSpy sourceCancelledSpy(dataSource.data(), &DataSource::cancelled); QVERIFY(sourceCancelledSpy.isValid()); // we should get a selection offered for that on the data device QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered); QVERIFY(selectionOfferedSpy.isValid()); QVERIFY(selectionOfferedSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 1); // create a second data source and replace previous one QScopedPointer dataSource2(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource2->isValid()); dataSource2->offer(QStringLiteral("text/plain")); QSignalSpy sourceCancelled2Spy(dataSource2.data(), &DataSource::cancelled); QVERIFY(sourceCancelled2Spy.isValid()); dataDevice->setSelection(1, dataSource2.data()); QCOMPARE(selectionOfferedSpy.count(), 1); QVERIFY(sourceCancelledSpy.wait()); QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); // replace the data source with itself, ensure that it did not get cancelled dataDevice->setSelection(1, dataSource2.data()); QVERIFY(!sourceCancelled2Spy.wait(500)); QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); // create a new DataDevice and replace previous one QScopedPointer dataDevice2(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice2->isValid()); QScopedPointer dataSource3(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource3->isValid()); dataSource3->offer(QStringLiteral("text/plain")); dataDevice2->setSelection(1, dataSource3.data()); QVERIFY(sourceCancelled2Spy.wait()); // try to crash by first destroying dataSource3 and setting a new DataSource QScopedPointer dataSource4(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource4->isValid()); dataSource4->offer(QStringLiteral("text/plain")); dataSource3.reset(); dataDevice2->setSelection(1, dataSource4.data()); QVERIFY(selectionOfferedSpy.wait()); } void TestDataDevice::testDestroy() { using namespace KWayland::Client; QScopedPointer dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, dataDevice.data(), &DataDevice::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the data device should be destroyed; QVERIFY(!dataDevice->isValid()); // calling destroy again should not fail dataDevice->destroy(); } QTEST_GUILESS_MAIN(TestDataDevice) #include "test_datadevice.moc" diff --git a/autotests/client/test_datasource.cpp b/autotests/client/test_datasource.cpp index b748734..f373d68 100644 --- a/autotests/client/test_datasource.cpp +++ b/autotests/client/test_datasource.cpp @@ -1,339 +1,325 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/datasource_interface.h" // Wayland #include class TestDataSource : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testOffer(); void testTargetAccepts_data(); void testTargetAccepts(); void testRequestSend(); void testRequestSendOnUnbound(); void testCancel(); void testServerGet(); void testDestroy(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datasource-0"); void TestDataSource::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); QVERIFY(dataDeviceManagerSpy.wait()); m_dataDeviceManager = registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value(), this); } void TestDataSource::cleanup() { if (m_dataDeviceManager) { delete m_dataDeviceManager; m_dataDeviceManager = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestDataSource::testOffer() { using namespace KWayland::Client; using namespace KWayland::Server; qRegisterMetaType(); QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QPointer serverDataSource = dataSourceCreatedSpy.first().first().value(); QVERIFY(!serverDataSource.isNull()); QCOMPARE(serverDataSource->mimeTypes().count(), 0); QVERIFY(serverDataSource->parentResource()); QSignalSpy offeredSpy(serverDataSource.data(), SIGNAL(mimeTypeOffered(QString))); QVERIFY(offeredSpy.isValid()); const QString plain = QStringLiteral("text/plain"); QMimeDatabase db; dataSource->offer(db.mimeTypeForName(plain)); QVERIFY(offeredSpy.wait()); QCOMPARE(offeredSpy.count(), 1); QCOMPARE(offeredSpy.last().first().toString(), plain); QCOMPARE(serverDataSource->mimeTypes().count(), 1); QCOMPARE(serverDataSource->mimeTypes().first(), plain); const QString html = QStringLiteral("text/html"); dataSource->offer(db.mimeTypeForName(html)); QVERIFY(offeredSpy.wait()); QCOMPARE(offeredSpy.count(), 2); QCOMPARE(offeredSpy.first().first().toString(), plain); QCOMPARE(offeredSpy.last().first().toString(), html); QCOMPARE(serverDataSource->mimeTypes().count(), 2); QCOMPARE(serverDataSource->mimeTypes().first(), plain); QCOMPARE(serverDataSource->mimeTypes().last(), html); // try destroying the client side, should trigger a destroy of server side dataSource.reset(); QVERIFY(!serverDataSource.isNull()); wl_display_flush(m_connection->display()); // after running the event loop the Wayland event should be delivered, but it uses delete later QCoreApplication::processEvents(); QVERIFY(!serverDataSource.isNull()); // so once more event loop QCoreApplication::processEvents(); QVERIFY(serverDataSource.isNull()); } void TestDataSource::testTargetAccepts_data() { QTest::addColumn("mimeType"); QTest::newRow("empty") << QString(); QTest::newRow("text/plain") << QStringLiteral("text/plain"); } void TestDataSource::testTargetAccepts() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy targetAcceptsSpy(dataSource.data(), SIGNAL(targetAccepts(QString))); QVERIFY(targetAcceptsSpy.isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QFETCH(QString, mimeType); dataSourceCreatedSpy.first().first().value()->accept(mimeType); QVERIFY(targetAcceptsSpy.wait()); QCOMPARE(targetAcceptsSpy.count(), 1); QCOMPARE(targetAcceptsSpy.first().first().toString(), mimeType); } void TestDataSource::testRequestSend() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy sendRequestedSpy(dataSource.data(), SIGNAL(sendDataRequested(QString,qint32))); QVERIFY(sendRequestedSpy.isValid()); const QString plain = QStringLiteral("text/plain"); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); QTemporaryFile file; QVERIFY(file.open()); dataSourceCreatedSpy.first().first().value()->requestData(plain, file.handle()); QVERIFY(sendRequestedSpy.wait()); QCOMPARE(sendRequestedSpy.count(), 1); QCOMPARE(sendRequestedSpy.first().first().toString(), plain); QCOMPARE(sendRequestedSpy.first().last().value(), file.handle()); QVERIFY(sendRequestedSpy.first().last().value() != -1); QFile writeFile; QVERIFY(writeFile.open(sendRequestedSpy.first().last().value(), QFile::WriteOnly, QFileDevice::AutoCloseHandle)); writeFile.close(); } void TestDataSource::testRequestSendOnUnbound() { // this test verifies that the server doesn't crash when requesting a send on an unbound DataSource using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataSourceCreated); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(dataSourceCreatedSpy.count(), 1); auto sds = dataSourceCreatedSpy.first().first().value(); QVERIFY(sds); QSignalSpy unboundSpy(sds, &Resource::unbound); QVERIFY(unboundSpy.isValid()); dataSource.reset(); QVERIFY(unboundSpy.wait()); sds->requestData(QStringLiteral("text/plain"), -1); } void TestDataSource::testCancel() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QSignalSpy cancelledSpy(dataSource.data(), SIGNAL(cancelled())); QVERIFY(cancelledSpy.isValid()); QVERIFY(dataSourceCreatedSpy.wait()); QCOMPARE(cancelledSpy.count(), 0); dataSourceCreatedSpy.first().first().value()->cancel(); QVERIFY(cancelledSpy.wait()); QCOMPARE(cancelledSpy.count(), 1); } void TestDataSource::testServerGet() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, SIGNAL(dataSourceCreated(KWayland::Server::DataSourceInterface*))); QVERIFY(dataSourceCreatedSpy.isValid()); QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); QVERIFY(!DataSourceInterface::get(nullptr)); QVERIFY(dataSourceCreatedSpy.wait()); auto d = dataSourceCreatedSpy.first().first().value(); QCOMPARE(DataSourceInterface::get(d->resource()), d); QVERIFY(!DataSourceInterface::get(nullptr)); } void TestDataSource::testDestroy() { using namespace KWayland::Client; QScopedPointer dataSource(m_dataDeviceManager->createDataSource()); QVERIFY(dataSource->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); connect(m_connection, &ConnectionThread::connectionDied, dataSource.data(), &DataSource::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!dataSource->isValid()); // calling destroy again should not fail dataSource->destroy(); } QTEST_GUILESS_MAIN(TestDataSource) #include "test_datasource.moc" diff --git a/autotests/client/test_drag_drop.cpp b/autotests/client/test_drag_drop.cpp index faf8e4a..164eaec 100644 --- a/autotests/client/test_drag_drop.cpp +++ b/autotests/client/test_drag_drop.cpp @@ -1,585 +1,571 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/pointer.h" #include "../../src/client/touch.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/shell.h" #include "../../src/client/shm_pool.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/datasource_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" class TestDragAndDrop : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testPointerDragAndDrop(); void testTouchDragAndDrop(); void testDragAndDropWithCancelByDestroyDataSource(); void testPointerEventsIgnored(); private: KWayland::Client::Surface *createSurface(); KWayland::Server::SurfaceInterface *getServerSurface(); KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWayland::Server::SeatInterface *m_seatInterface = nullptr; KWayland::Server::ShellInterface *m_shellInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::DataDevice *m_dataDevice = nullptr; KWayland::Client::DataSource *m_dataSource = nullptr; QThread *m_thread = nullptr; KWayland::Client::Registry *m_registry = nullptr; KWayland::Client::Seat *m_seat = nullptr; KWayland::Client::Pointer *m_pointer = nullptr; KWayland::Client::Touch *m_touch = nullptr; KWayland::Client::DataDeviceManager *m_ddm = nullptr; KWayland::Client::ShmPool *m_shm = nullptr; KWayland::Client::Shell *m_shell = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-drag-n-drop-0"); void TestDragAndDrop::init() { using namespace KWayland::Server; using namespace KWayland::Client; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); m_dataDeviceManagerInterface = m_display->createDataDeviceManager(m_display); m_dataDeviceManagerInterface->create(); QVERIFY(m_dataDeviceManagerInterface->isValid()); m_display->createShm(); m_shellInterface = m_display->createShell(m_display); m_shellInterface->create(); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy interfacesAnnouncedSpy(m_registry, &Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); #define CREATE(variable, factory, iface) \ variable = m_registry->create##factory(m_registry->interface(Registry::Interface::iface).name, m_registry->interface(Registry::Interface::iface).version, this); \ QVERIFY(variable); CREATE(m_compositor, Compositor, Compositor) CREATE(m_seat, Seat, Seat) CREATE(m_ddm, DataDeviceManager, DataDeviceManager) CREATE(m_shm, ShmPool, Shm) CREATE(m_shell, Shell, Shell) #undef CREATE QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); QVERIFY(pointerSpy.wait()); m_pointer = m_seat->createPointer(m_seat); QVERIFY(m_pointer->isValid()); m_touch = m_seat->createTouch(m_seat); QVERIFY(m_touch->isValid()); m_dataDevice = m_ddm->getDataDevice(m_seat, this); QVERIFY(m_dataDevice->isValid()); m_dataSource = m_ddm->createDataSource(this); QVERIFY(m_dataSource->isValid()); m_dataSource->offer(QStringLiteral("text/plain")); } void TestDragAndDrop::cleanup() { #define DELETE(name) \ if (name) { \ delete name; \ name = nullptr; \ } DELETE(m_dataSource) DELETE(m_dataDevice) DELETE(m_shell) DELETE(m_shm) DELETE(m_compositor) DELETE(m_ddm) DELETE(m_seat) DELETE(m_queue) DELETE(m_registry) #undef DELETE if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } KWayland::Client::Surface *TestDragAndDrop::createSurface() { using namespace KWayland::Client; auto s = m_compositor->createSurface(); QImage img(QSize(100, 200), QImage::Format_RGB32); img.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(img)); s->damage(QRect(0, 0, 100, 200)); s->commit(Surface::CommitFlag::None); return s; } KWayland::Server::SurfaceInterface *TestDragAndDrop::getServerSurface() { using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); if (!surfaceCreatedSpy.isValid()) { return nullptr; } if (!surfaceCreatedSpy.wait(500)) { return nullptr; } return surfaceCreatedSpy.first().first().value(); } void TestDragAndDrop::testPointerDragAndDrop() { // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &DataSource::selectedDragAndDropActionChanged); QVERIFY(dataSourceSelectedActionChangedSpy.isValid()); // now we need to pass pointer focus to the Surface and simulate a button press QSignalSpy buttonPressSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(buttonPressSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); m_seatInterface->setTimestamp(2); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonPressSpy.wait()); QCOMPARE(buttonPressSpy.first().at(1).value(), quint32(2)); // add some signal spies for client side QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); QSignalSpy dragMotionSpy(m_dataDevice, &DataDevice::dragMotion); QVERIFY(dragMotionSpy.isValid()); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); QSignalSpy sourceDropSpy(m_dataSource, &DataSource::dragAndDropPerformed); QVERIFY(sourceDropSpy.isValid()); // now we can start the drag and drop QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted); QVERIFY(dragStartedSpy.isValid()); m_dataSource->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); m_dataDevice->startDrag(buttonPressSpy.first().first().value(), m_dataSource, s.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QVERIFY(!m_seatInterface->dragSource()->icon()); QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), buttonPressSpy.first().first().value()); QVERIFY(dragEnteredSpy.wait()); QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.first().first().value(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0)); QCOMPARE(m_dataDevice->dragSurface().data(), s.data()); auto offer = m_dataDevice->dragOffer(); QVERIFY(offer); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::None); QSignalSpy offerActionChangedSpy(offer, &DataOffer::selectedDragAndDropActionChanged); QVERIFY(offerActionChangedSpy.isValid()); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); QTRY_COMPARE(offer->sourceDragAndDropActions(), DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); offer->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move, DataDeviceManager::DnDAction::Move); QVERIFY(offerActionChangedSpy.wait()); QCOMPARE(offerActionChangedSpy.count(), 1); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1); QCOMPARE(m_dataSource->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); // simulate motion m_seatInterface->setTimestamp(3); m_seatInterface->setPointerPos(QPointF(3, 3)); QVERIFY(dragMotionSpy.wait()); QCOMPARE(dragMotionSpy.count(), 1); QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3)); QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u); // simulate drop QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded); QVERIFY(serverDragEndedSpy.isValid()); QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->pointerButtonReleased(1); QVERIFY(sourceDropSpy.isEmpty()); QVERIFY(droppedSpy.wait()); QCOMPARE(sourceDropSpy.count(), 1); QCOMPARE(serverDragEndedSpy.count(), 1); QSignalSpy finishedSpy(m_dataSource, &DataSource::dragAndDropFinished); QVERIFY(finishedSpy.isValid()); offer->dragAndDropFinished(); QVERIFY(finishedSpy.wait()); delete offer; // verify that we did not get any further input events QVERIFY(pointerMotionSpy.isEmpty()); QCOMPARE(buttonPressSpy.count(), 1); } void TestDragAndDrop::testTouchDragAndDrop() { // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); s->setSize(QSize(100,100)); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &DataSource::selectedDragAndDropActionChanged); QVERIFY(dataSourceSelectedActionChangedSpy.isValid()); // now we need to pass touch focus to the Surface and simulate a touch down QSignalSpy sequenceStartedSpy(m_touch, &Touch::sequenceStarted); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy pointAddedSpy(m_touch, &Touch::pointAdded); QVERIFY(pointAddedSpy.isValid()); m_seatInterface->setFocusedTouchSurface(serverSurface); m_seatInterface->setTimestamp(2); const qint32 touchId = m_seatInterface->touchDown(QPointF(50,50)); QVERIFY(sequenceStartedSpy.wait()); QScopedPointer tp(sequenceStartedSpy.first().at(0).value()); QVERIFY(!tp.isNull()); QCOMPARE(tp->time(), quint32(2)); // add some signal spies for client side QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); QSignalSpy dragMotionSpy(m_dataDevice, &DataDevice::dragMotion); QVERIFY(dragMotionSpy.isValid()); QSignalSpy touchMotionSpy(m_touch, &Touch::pointMoved); QVERIFY(touchMotionSpy.isValid()); QSignalSpy sourceDropSpy(m_dataSource, &DataSource::dragAndDropPerformed); QVERIFY(sourceDropSpy.isValid()); // now we can start the drag and drop QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted); QVERIFY(dragStartedSpy.isValid()); m_dataSource->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); m_dataDevice->startDrag(tp->downSerial(), m_dataSource, s.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QVERIFY(!m_seatInterface->dragSource()->icon()); QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), tp->downSerial()); QVERIFY(dragEnteredSpy.wait()); QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.first().first().value(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0)); QCOMPARE(m_dataDevice->dragSurface().data(), s.data()); auto offer = m_dataDevice->dragOffer(); QVERIFY(offer); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::None); QSignalSpy offerActionChangedSpy(offer, &DataOffer::selectedDragAndDropActionChanged); QVERIFY(offerActionChangedSpy.isValid()); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); QTRY_COMPARE(offer->sourceDragAndDropActions(), DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); offer->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move, DataDeviceManager::DnDAction::Move); QVERIFY(offerActionChangedSpy.wait()); QCOMPARE(offerActionChangedSpy.count(), 1); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1); QCOMPARE(m_dataSource->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); // simulate motion m_seatInterface->setTimestamp(3); m_seatInterface->touchMove(touchId, QPointF(75, 75)); QVERIFY(dragMotionSpy.wait()); QCOMPARE(dragMotionSpy.count(), 1); QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(75, 75)); QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u); // simulate drop QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded); QVERIFY(serverDragEndedSpy.isValid()); QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->touchUp(touchId); QVERIFY(sourceDropSpy.isEmpty()); QVERIFY(droppedSpy.wait()); QCOMPARE(sourceDropSpy.count(), 1); QCOMPARE(serverDragEndedSpy.count(), 1); QSignalSpy finishedSpy(m_dataSource, &DataSource::dragAndDropFinished); QVERIFY(finishedSpy.isValid()); offer->dragAndDropFinished(); QVERIFY(finishedSpy.wait()); delete offer; // verify that we did not get any further input events QVERIFY(touchMotionSpy.isEmpty()); QCOMPARE(pointAddedSpy.count(), 0); } void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource() { // this test simulates the problem from BUG 389221 using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &DataSource::selectedDragAndDropActionChanged); QVERIFY(dataSourceSelectedActionChangedSpy.isValid()); // now we need to pass pointer focus to the Surface and simulate a button press QSignalSpy buttonPressSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(buttonPressSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); m_seatInterface->setTimestamp(2); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonPressSpy.wait()); QCOMPARE(buttonPressSpy.first().at(1).value(), quint32(2)); // add some signal spies for client side QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); QSignalSpy dragMotionSpy(m_dataDevice, &DataDevice::dragMotion); QVERIFY(dragMotionSpy.isValid()); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); QSignalSpy dragLeftSpy(m_dataDevice, &DataDevice::dragLeft); QVERIFY(dragLeftSpy.isValid()); // now we can start the drag and drop QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted); QVERIFY(dragStartedSpy.isValid()); m_dataSource->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); m_dataDevice->startDrag(buttonPressSpy.first().first().value(), m_dataSource, s.data()); QVERIFY(dragStartedSpy.wait()); QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QVERIFY(!m_seatInterface->dragSource()->icon()); QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), buttonPressSpy.first().first().value()); QVERIFY(dragEnteredSpy.wait()); QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.first().first().value(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0)); QCOMPARE(m_dataDevice->dragSurface().data(), s.data()); auto offer = m_dataDevice->dragOffer(); QVERIFY(offer); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::None); QSignalSpy offerActionChangedSpy(offer, &DataOffer::selectedDragAndDropActionChanged); QVERIFY(offerActionChangedSpy.isValid()); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1); QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); QTRY_COMPARE(offer->sourceDragAndDropActions(), DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move); offer->setDragAndDropActions(DataDeviceManager::DnDAction::Copy | DataDeviceManager::DnDAction::Move, DataDeviceManager::DnDAction::Move); QVERIFY(offerActionChangedSpy.wait()); QCOMPARE(offerActionChangedSpy.count(), 1); QCOMPARE(offer->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1); QCOMPARE(m_dataSource->selectedDragAndDropAction(), DataDeviceManager::DnDAction::Move); // simulate motion m_seatInterface->setTimestamp(3); m_seatInterface->setPointerPos(QPointF(3, 3)); QVERIFY(dragMotionSpy.wait()); QCOMPARE(dragMotionSpy.count(), 1); QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3)); QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u); // now delete the DataSource delete m_dataSource; m_dataSource = nullptr; QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded); QVERIFY(serverDragEndedSpy.isValid()); QVERIFY(dragLeftSpy.isEmpty()); QVERIFY(dragLeftSpy.wait()); QTRY_COMPARE(dragLeftSpy.count(), 1); QTRY_COMPARE(serverDragEndedSpy.count(), 1); // simulate drop QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->pointerButtonReleased(1); QVERIFY(!droppedSpy.wait(500)); // verify that we did not get any further input events QVERIFY(pointerMotionSpy.isEmpty()); QCOMPARE(buttonPressSpy.count(), 2); } void TestDragAndDrop::testPointerEventsIgnored() { // this test verifies that all pointer events are ignored on the focused Pointer device during drag using namespace KWayland::Server; using namespace KWayland::Client; // first create a window QScopedPointer s(createSurface()); auto serverSurface = getServerSurface(); QVERIFY(serverSurface); // pass it pointer focus m_seatInterface->setFocusedPointerSurface(serverSurface); // create signal spies for all the pointer events QSignalSpy pointerEnteredSpy(m_pointer, &Pointer::entered); QVERIFY(pointerEnteredSpy.isValid()); QSignalSpy pointerLeftSpy(m_pointer, &Pointer::left); QVERIFY(pointerLeftSpy.isValid()); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); QSignalSpy axisSpy(m_pointer, &Pointer::axisChanged); QVERIFY(axisSpy.isValid()); QSignalSpy buttonSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(buttonSpy.isValid()); QSignalSpy dragEnteredSpy(m_dataDevice, &DataDevice::dragEntered); QVERIFY(dragEnteredSpy.isValid()); // first simulate a few things quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(10, 10)); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Vertical, 5); // verify that we have those QVERIFY(axisSpy.wait()); QCOMPARE(axisSpy.count(), 1); QCOMPARE(pointerMotionSpy.count(), 1); QCOMPARE(pointerEnteredSpy.count(), 1); QVERIFY(buttonSpy.isEmpty()); QVERIFY(pointerLeftSpy.isEmpty()); // let's start the drag m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonSpy.wait()); QCOMPARE(buttonSpy.count(), 1); m_dataDevice->startDrag(buttonSpy.first().first().value(), m_dataSource, s.data()); QVERIFY(dragEnteredSpy.wait()); // now simulate all the possible pointer interactions m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(2); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(2); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Vertical, 5); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxis(Qt::Horizontal, 5); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(serverSurface); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(50, 50)); // last but not least, simulate the drop QSignalSpy droppedSpy(m_dataDevice, &DataDevice::dropped); QVERIFY(droppedSpy.isValid()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(1); QVERIFY(droppedSpy.wait()); // all the changes should have been ignored QCOMPARE(axisSpy.count(), 1); QCOMPARE(pointerMotionSpy.count(), 1); QCOMPARE(pointerEnteredSpy.count(), 1); QCOMPARE(buttonSpy.count(), 1); QVERIFY(pointerLeftSpy.isEmpty()); } QTEST_GUILESS_MAIN(TestDragAndDrop) #include "test_drag_drop.moc" diff --git a/autotests/client/test_error.cpp b/autotests/client/test_error.cpp index a264396..9f3eaae 100644 --- a/autotests/client/test_error.cpp +++ b/autotests/client/test_error.cpp @@ -1,205 +1,191 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/plasmashell.h" #include "../../src/client/registry.h" #include "../../src/client/shell.h" #include "../../src/client/surface.h" // server #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/shell_interface.h" #include "../../src/server/plasmashell_interface.h" #include #include // For EPROTO using namespace KWayland::Client; using namespace KWayland::Server; class ErrorTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testMultipleShellSurfacesForSurface(); void testMultiplePlasmaShellSurfacesForSurface(); void testTransientForSameSurface_data(); void testTransientForSameSurface(); private: Display *m_display = nullptr; CompositorInterface *m_ci = nullptr; ShellInterface *m_si = nullptr; PlasmaShellInterface *m_psi = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Compositor *m_compositor = nullptr; Shell *m_shell = nullptr; PlasmaShell *m_plasmaShell = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-error-0"); void ErrorTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_ci = m_display->createCompositor(m_display); m_ci->create(); m_si = m_display->createShell(m_display); m_si->create(); m_psi = m_display->createPlasmaShell(m_display); m_psi->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor); m_shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version, this); QVERIFY(m_shell); m_plasmaShell = registry.createPlasmaShell(registry.interface(Registry::Interface::PlasmaShell).name, registry.interface(Registry::Interface::PlasmaShell).version, this); QVERIFY(m_plasmaShell); } void ErrorTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_plasmaShell) CLEANUP(m_shell) CLEANUP(m_compositor) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_psi) CLEANUP(m_si) CLEANUP(m_ci) CLEANUP(m_display) #undef CLEANUP } void ErrorTest::testMultipleShellSurfacesForSurface() { // this test verifies that creating two ShellSurfaces for the same Surface triggers a protocol error QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(errorSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer shellSurface1(m_shell->createSurface(surface.data())); QScopedPointer shellSurface2(m_shell->createSurface(surface.data())); QVERIFY(errorSpy.wait()); QVERIFY(m_connection->hasError()); QCOMPARE(m_connection->errorCode(), EPROTO); } void ErrorTest::testMultiplePlasmaShellSurfacesForSurface() { // this test verifies that creating two ShellSurfaces for the same Surface triggers a protocol error QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(errorSpy.isValid()); // PlasmaShell is too smart and doesn't allow us to create a second PlasmaShellSurface // thus we need to cheat by creating a surface manually auto surface = wl_compositor_create_surface(*m_compositor); QScopedPointer shellSurface1(m_plasmaShell->createSurface(surface)); QScopedPointer shellSurface2(m_plasmaShell->createSurface(surface)); QVERIFY(!m_connection->hasError()); QVERIFY(errorSpy.wait()); QVERIFY(m_connection->hasError()); QCOMPARE(m_connection->errorCode(), EPROTO); wl_surface_destroy(surface); } void ErrorTest::testTransientForSameSurface_data() { QTest::addColumn("flag"); QTest::newRow("transient") << ShellSurface::TransientFlag::Default; QTest::newRow("transient no focus") << ShellSurface::TransientFlag::NoFocus; } void ErrorTest::testTransientForSameSurface() { // this test verifies that creating a transient shell surface for itself triggers a protocol error QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(errorSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer shellSurface(m_shell->createSurface(surface.data())); QFETCH(ShellSurface::TransientFlag, flag); shellSurface->setTransient(surface.data(), QPoint(), flag); QVERIFY(errorSpy.wait()); QVERIFY(m_connection->hasError()); QCOMPARE(m_connection->errorCode(), EPROTO); } QTEST_GUILESS_MAIN(ErrorTest) #include "test_error.moc" diff --git a/autotests/client/test_fake_input.cpp b/autotests/client/test_fake_input.cpp index e752957..d4e2f5b 100644 --- a/autotests/client/test_fake_input.cpp +++ b/autotests/client/test_fake_input.cpp @@ -1,481 +1,467 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/fakeinput.h" #include "../../src/client/registry.h" // server #include "../../src/server/display.h" #include "../../src/server/fakeinput_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) class FakeInputTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testAuthenticate(); void testMotion(); void testMotionAbsolute(); void testPointerButtonQt_data(); void testPointerButtonQt(); void testPointerButtonLinux_data(); void testPointerButtonLinux(); void testAxis_data(); void testAxis(); void testTouch(); void testKeyboardKeyLinux_data(); void testKeyboardKeyLinux(); private: Display *m_display = nullptr; FakeInputInterface *m_fakeInputInterface = nullptr; FakeInputDevice *m_device = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; FakeInput *m_fakeInput = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-fake-input-0"); void FakeInputTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_fakeInputInterface = m_display->createFakeInput(); m_fakeInputInterface->create(); QSignalSpy deviceCreatedSpy(m_fakeInputInterface, &FakeInputInterface::deviceCreated); QVERIFY(deviceCreatedSpy.isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_fakeInput = registry.createFakeInput(registry.interface(Registry::Interface::FakeInput).name, registry.interface(Registry::Interface::FakeInput).version, this); QVERIFY(m_fakeInput->isValid()); QVERIFY(deviceCreatedSpy.wait()); m_device = deviceCreatedSpy.first().first().value(); QVERIFY(m_device); } void FakeInputTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_fakeInput) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_device) CLEANUP(m_fakeInputInterface) CLEANUP(m_display) #undef CLEANUP } void FakeInputTest::testAuthenticate() { // this test verifies that an authenticate request is passed to the Server QVERIFY(!m_device->isAuthenticated()); QSignalSpy authenticationRequestedSpy(m_device, &FakeInputDevice::authenticationRequested); QVERIFY(authenticationRequestedSpy.isValid()); m_fakeInput->authenticate(QStringLiteral("test-case"), QStringLiteral("to test")); QVERIFY(authenticationRequestedSpy.wait()); QCOMPARE(authenticationRequestedSpy.count(), 1); QCOMPARE(authenticationRequestedSpy.first().at(0).toString(), QStringLiteral("test-case")); QCOMPARE(authenticationRequestedSpy.first().at(1).toString(), QStringLiteral("to test")); m_device->setAuthentication(true); QVERIFY(m_device->isAuthenticated()); } void FakeInputTest::testMotion() { // this test verifies that motion is properly passed to the server QVERIFY(!m_device->isAuthenticated()); QSignalSpy motionSpy(m_device, &FakeInputDevice::pointerMotionRequested); QVERIFY(motionSpy.isValid()); // without an authentication we shouldn't get the signals m_fakeInput->requestPointerMove(QSizeF(1, 2)); QVERIFY(!motionSpy.wait(100)); // now let's authenticate the interface m_device->setAuthentication(true); m_fakeInput->requestPointerMove(QSizeF(1, 2)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 1); QCOMPARE(motionSpy.last().first().toSizeF(), QSizeF(1, 2)); // just for the fun: once more m_fakeInput->requestPointerMove(QSizeF(0, 0)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 2); QCOMPARE(motionSpy.last().first().toSizeF(), QSizeF(0, 0)); } void FakeInputTest::testMotionAbsolute() { // this test verifies that motion is properly passed to the server QVERIFY(!m_device->isAuthenticated()); QSignalSpy motionSpy(m_device, &FakeInputDevice::pointerMotionAbsoluteRequested); QVERIFY(motionSpy.isValid()); // without an authentication we shouldn't get the signals m_fakeInput->requestPointerMoveAbsolute(QPointF(1, 2)); QVERIFY(!motionSpy.wait(100)); // now let's authenticate the interface m_device->setAuthentication(true); m_fakeInput->requestPointerMoveAbsolute(QPointF(1, 2)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 1); QCOMPARE(motionSpy.last().first().toPointF(), QPointF(1, 2)); // just for the fun: once more m_fakeInput->requestPointerMoveAbsolute(QPointF(0, 0)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 2); QCOMPARE(motionSpy.last().first().toPointF(), QPointF(0, 0)); } void FakeInputTest::testPointerButtonQt_data() { QTest::addColumn("qtButton"); QTest::addColumn("linuxButton"); QTest::newRow("left") << Qt::LeftButton << quint32(BTN_LEFT); QTest::newRow("right") << Qt::RightButton << quint32(BTN_RIGHT); QTest::newRow("middle") << Qt::MiddleButton << quint32(BTN_MIDDLE); } void FakeInputTest::testPointerButtonQt() { // this test verifies that pointer button events are properly passed to the server with Qt button codes QVERIFY(!m_device->isAuthenticated()); QSignalSpy pressedSpy(m_device, &FakeInputDevice::pointerButtonPressRequested); QVERIFY(pressedSpy.isValid()); QSignalSpy releasedSpy(m_device, &FakeInputDevice::pointerButtonReleaseRequested); QVERIFY(releasedSpy.isValid()); // without an authentication we shouldn't get the signals QFETCH(Qt::MouseButton, qtButton); m_fakeInput->requestPointerButtonClick(qtButton); QVERIFY(!pressedSpy.wait(100)); QVERIFY(pressedSpy.isEmpty()); QVERIFY(releasedSpy.isEmpty()); // now authenticate m_device->setAuthentication(true); // now our click should work m_fakeInput->requestPointerButtonClick(qtButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 1); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); QTEST(releasedSpy.last().first().value(), "linuxButton"); // and a press/release "manually" m_fakeInput->requestPointerButtonPress(qtButton); QVERIFY(pressedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); // and release m_fakeInput->requestPointerButtonRelease(qtButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 2); QTEST(releasedSpy.last().first().value(), "linuxButton"); } void FakeInputTest::testPointerButtonLinux_data() { QTest::addColumn("linuxButton"); QTest::newRow("left") << quint32(BTN_LEFT); QTest::newRow("right") << quint32(BTN_RIGHT); QTest::newRow("middle") << quint32(BTN_MIDDLE); QTest::newRow("side") << quint32(BTN_SIDE); QTest::newRow("extra") << quint32(BTN_EXTRA); QTest::newRow("forward") << quint32(BTN_FORWARD); QTest::newRow("back") << quint32(BTN_BACK); QTest::newRow("task") << quint32(BTN_TASK); } void FakeInputTest::testPointerButtonLinux() { // this test verifies that pointer button events are properly passed to the server with Qt button codes QVERIFY(!m_device->isAuthenticated()); QSignalSpy pressedSpy(m_device, &FakeInputDevice::pointerButtonPressRequested); QVERIFY(pressedSpy.isValid()); QSignalSpy releasedSpy(m_device, &FakeInputDevice::pointerButtonReleaseRequested); QVERIFY(releasedSpy.isValid()); // without an authentication we shouldn't get the signals QFETCH(quint32, linuxButton); m_fakeInput->requestPointerButtonClick(linuxButton); QVERIFY(!pressedSpy.wait(100)); QVERIFY(pressedSpy.isEmpty()); QVERIFY(releasedSpy.isEmpty()); // now authenticate m_device->setAuthentication(true); // now our click should work m_fakeInput->requestPointerButtonClick(linuxButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 1); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); QTEST(releasedSpy.last().first().value(), "linuxButton"); // and a press/release "manually" m_fakeInput->requestPointerButtonPress(linuxButton); QVERIFY(pressedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); // and release m_fakeInput->requestPointerButtonRelease(linuxButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 2); QTEST(releasedSpy.last().first().value(), "linuxButton"); } void FakeInputTest::testAxis_data() { QTest::addColumn("orientation"); QTest::addColumn("delta"); QTest::newRow("horizontal/1") << Qt::Horizontal << 1.0; QTest::newRow("horizontal/-2") << Qt::Horizontal << -2.0; QTest::newRow("vertical/10") << Qt::Vertical << 10.0; QTest::newRow("vertical/-20") << Qt::Vertical << -22.0; } void FakeInputTest::testAxis() { // this test verifies that pointer axis events are properly passed to the server QVERIFY(!m_device->isAuthenticated()); QSignalSpy axisSpy(m_device, &FakeInputDevice::pointerAxisRequested); QVERIFY(axisSpy.isValid()); QFETCH(Qt::Orientation, orientation); QFETCH(qreal, delta); // without an authentication we shouldn't get the signals m_fakeInput->requestPointerAxis(orientation, delta); QVERIFY(!axisSpy.wait(100)); // now authenticate m_device->setAuthentication(true); // now we can properly test m_fakeInput->requestPointerAxis(orientation, delta); QVERIFY(axisSpy.wait()); QCOMPARE(axisSpy.count(), 1); QCOMPARE(axisSpy.first().at(0).value(), orientation); QCOMPARE(axisSpy.first().at(1).value(), delta); } void FakeInputTest::testTouch() { QVERIFY(!m_device->isAuthenticated()); QSignalSpy touchDownSpy(m_device, &FakeInputDevice::touchDownRequested); QVERIFY(touchDownSpy.isValid()); QSignalSpy touchMotionSpy(m_device, &FakeInputDevice::touchMotionRequested); QVERIFY(touchMotionSpy.isValid()); QSignalSpy touchUpSpy(m_device, &FakeInputDevice::touchUpRequested); QVERIFY(touchUpSpy.isValid()); QSignalSpy touchFrameSpy(m_device, &FakeInputDevice::touchFrameRequested); QVERIFY(touchFrameSpy.isValid()); QSignalSpy touchCancelSpy(m_device, &FakeInputDevice::touchCancelRequested); QVERIFY(touchCancelSpy.isValid()); // without an authentication we shouldn't get the signals m_fakeInput->requestTouchDown(0, QPointF(1, 2)); QVERIFY(!touchDownSpy.wait(100)); m_fakeInput->requestTouchMotion(0, QPointF(3, 4)); QVERIFY(!touchMotionSpy.wait(100)); m_fakeInput->requestTouchUp(0); QVERIFY(!touchUpSpy.wait(100)); m_fakeInput->requestTouchDown(1, QPointF(5, 6)); QVERIFY(!touchDownSpy.wait(100)); m_fakeInput->requestTouchFrame(); QVERIFY(!touchFrameSpy.wait(100)); m_fakeInput->requestTouchCancel(); QVERIFY(!touchCancelSpy.wait(100)); // now let's authenticate the interface m_device->setAuthentication(true); m_fakeInput->requestTouchDown(0, QPointF(1, 2)); QVERIFY(touchDownSpy.wait()); QCOMPARE(touchDownSpy.count(), 1); QCOMPARE(touchDownSpy.last().at(0).value(), quint32(0)); QCOMPARE(touchDownSpy.last().at(1).toPointF(), QPointF(1, 2)); // Same id should not trigger another touchDown. m_fakeInput->requestTouchDown(0, QPointF(5,6)); QVERIFY(!touchDownSpy.wait(100)); m_fakeInput->requestTouchMotion(0, QPointF(3, 4)); QVERIFY(touchMotionSpy.wait()); QCOMPARE(touchMotionSpy.count(), 1); QCOMPARE(touchMotionSpy.last().at(0).value(), quint32(0)); QCOMPARE(touchMotionSpy.last().at(1).toPointF(), QPointF(3, 4)); // touchMotion with bogus id should not trigger signal. m_fakeInput->requestTouchMotion(1, QPointF(3, 4)); QVERIFY(!touchMotionSpy.wait(100)); m_fakeInput->requestTouchUp(0); QVERIFY(touchUpSpy.wait()); QCOMPARE(touchUpSpy.count(), 1); QCOMPARE(touchUpSpy.last().at(0).value(), quint32(0)); // touchUp with bogus id should not trigger signal. m_fakeInput->requestTouchUp(1); QVERIFY(!touchUpSpy.wait(100)); m_fakeInput->requestTouchDown(1, QPointF(5, 6)); QVERIFY(touchDownSpy.wait()); QCOMPARE(touchDownSpy.count(), 2); QCOMPARE(touchDownSpy.last().at(0).value(), quint32(1)); QCOMPARE(touchDownSpy.last().at(1).toPointF(), QPointF(5, 6)); m_fakeInput->requestTouchFrame(); QVERIFY(touchFrameSpy.wait()); QCOMPARE(touchFrameSpy.count(), 1); m_fakeInput->requestTouchCancel(); QVERIFY(touchCancelSpy.wait()); QCOMPARE(touchCancelSpy.count(), 1); } void FakeInputTest::testKeyboardKeyLinux_data() { QTest::addColumn("linuxKey"); QTest::newRow("A") << quint32(KEY_A); QTest::newRow("S") << quint32(KEY_S); QTest::newRow("D") << quint32(KEY_D); QTest::newRow("F") << quint32(KEY_F); } void FakeInputTest::testKeyboardKeyLinux() { // this test verifies that keyboard key events are properly passed to the server with Qt button codes QVERIFY(!m_device->isAuthenticated()); QSignalSpy pressedSpy(m_device, &FakeInputDevice::keyboardKeyPressRequested); QVERIFY(pressedSpy.isValid()); QSignalSpy releasedSpy(m_device, &FakeInputDevice::keyboardKeyReleaseRequested); QVERIFY(releasedSpy.isValid()); // without an authentication we shouldn't get the signals QFETCH(quint32, linuxKey); m_fakeInput->requestKeyboardKeyPress(linuxKey); m_fakeInput->requestKeyboardKeyRelease(linuxKey); QVERIFY(!pressedSpy.wait(100)); QVERIFY(pressedSpy.isEmpty()); QVERIFY(releasedSpy.isEmpty()); // now authenticate m_device->setAuthentication(true); // now our click should work m_fakeInput->requestKeyboardKeyPress(linuxKey); m_fakeInput->requestKeyboardKeyRelease(linuxKey); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 1); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxKey"); QTEST(releasedSpy.last().first().value(), "linuxKey"); // and a press/release "manually" m_fakeInput->requestKeyboardKeyPress(linuxKey); QVERIFY(pressedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxKey"); // and release m_fakeInput->requestKeyboardKeyRelease(linuxKey); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 2); QTEST(releasedSpy.last().first().value(), "linuxKey"); } QTEST_GUILESS_MAIN(FakeInputTest) #include "test_fake_input.moc" diff --git a/autotests/client/test_idle.cpp b/autotests/client/test_idle.cpp index 73a3969..7882aac 100644 --- a/autotests/client/test_idle.cpp +++ b/autotests/client/test_idle.cpp @@ -1,289 +1,275 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/idle.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" // server #include "../../src/server/display.h" #include "../../src/server/idle_interface.h" #include "../../src/server/seat_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class IdleTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testTimeout(); void testSimulateUserActivity(); void testServerSimulateUserActivity(); void testIdleInhibit(); void testIdleInhibitBlocksTimeout(); private: Display *m_display = nullptr; SeatInterface *m_seatInterface = nullptr; IdleInterface *m_idleInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Seat *m_seat = nullptr; Idle *m_idle = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-idle-0"); void IdleTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_seatInterface = m_display->createSeat(); m_seatInterface->setName(QStringLiteral("seat0")); m_seatInterface->create(); m_idleInterface = m_display->createIdle(); m_idleInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); QVERIFY(m_seat->isValid()); m_idle = registry.createIdle(registry.interface(Registry::Interface::Idle).name, registry.interface(Registry::Interface::Idle).version, this); QVERIFY(m_idle->isValid()); } void IdleTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_idle) CLEANUP(m_seat) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_idleInterface) CLEANUP(m_seatInterface) CLEANUP(m_display) #undef CLEANUP } void IdleTest::testTimeout() { // this test verifies the basic functionality of a timeout, that it gets fired // and that it resumes from idle, etc. QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec QVERIFY(!idleSpy.wait(500)); // the default of 5 sec will now pass QVERIFY(idleSpy.wait()); // simulate some activity QVERIFY(resumedFormIdleSpy.isEmpty()); m_seatInterface->setTimestamp(1); QVERIFY(resumedFormIdleSpy.wait()); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } void IdleTest::testSimulateUserActivity() { // this test verifies that simulate user activity doesn't fire the timer QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); m_connection->flush(); QTest::qWait(4000); timeout->simulateUserActivity(); // waiting default five sec should fail QVERIFY(!idleSpy.wait()); // another 2 sec should fire QVERIFY(idleSpy.wait(2000)); // now simulating user activity should emit a resumedFromIdle QVERIFY(resumedFormIdleSpy.isEmpty()); timeout->simulateUserActivity(); QVERIFY(resumedFormIdleSpy.wait()); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } void IdleTest::testServerSimulateUserActivity() { // this test verifies that simulate user activity doesn't fire the timer QScopedPointer timeout(m_idle->getTimeout(6000, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); m_connection->flush(); QTest::qWait(4000); m_idleInterface->simulateUserActivity(); // waiting default five sec should fail QVERIFY(!idleSpy.wait()); // another 2 sec should fire QVERIFY(idleSpy.wait(2000)); // now simulating user activity should emit a resumedFromIdle QVERIFY(resumedFormIdleSpy.isEmpty()); m_idleInterface->simulateUserActivity(); QVERIFY(resumedFormIdleSpy.wait()); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } void IdleTest::testIdleInhibit() { QCOMPARE(m_idleInterface->isInhibited(), false); QSignalSpy idleInhibitedSpy(m_idleInterface, &IdleInterface::inhibitedChanged); QVERIFY(idleInhibitedSpy.isValid()); m_idleInterface->inhibit(); QCOMPARE(m_idleInterface->isInhibited(), true); QCOMPARE(idleInhibitedSpy.count(), 1); m_idleInterface->inhibit(); QCOMPARE(m_idleInterface->isInhibited(), true); QCOMPARE(idleInhibitedSpy.count(), 1); m_idleInterface->uninhibit(); QCOMPARE(m_idleInterface->isInhibited(), true); QCOMPARE(idleInhibitedSpy.count(), 1); m_idleInterface->uninhibit(); QCOMPARE(m_idleInterface->isInhibited(), false); QCOMPARE(idleInhibitedSpy.count(), 2); } void IdleTest::testIdleInhibitBlocksTimeout() { // this test verifies that a timeout does not fire when the system is inhibited // so first inhibit QCOMPARE(m_idleInterface->isInhibited(), false); m_idleInterface->inhibit(); QScopedPointer timeout(m_idle->getTimeout(1, m_seat)); QVERIFY(timeout->isValid()); QSignalSpy idleSpy(timeout.data(), &IdleTimeout::idle); QVERIFY(idleSpy.isValid()); QSignalSpy resumedFormIdleSpy(timeout.data(), &IdleTimeout::resumeFromIdle); QVERIFY(resumedFormIdleSpy.isValid()); // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec QVERIFY(!idleSpy.wait(500)); // the default of 5 sec won't pass QVERIFY(!idleSpy.wait()); // simulate some activity QVERIFY(resumedFormIdleSpy.isEmpty()); m_seatInterface->setTimestamp(1); // resume from idle should not fire QVERIFY(!resumedFormIdleSpy.wait()); // let's uninhibit m_idleInterface->uninhibit(); QCOMPARE(m_idleInterface->isInhibited(), false); // we requested a timeout of 1 msec, but the minimum the server sets is 5 sec QVERIFY(!idleSpy.wait(500)); // the default of 5 sec will now pass QVERIFY(idleSpy.wait()); // if we inhibit now it will trigger a resume from idle QVERIFY(resumedFormIdleSpy.isEmpty()); m_idleInterface->inhibit(); QVERIFY(resumedFormIdleSpy.wait()); // let's wait again just to verify that also inhibit for already existing IdleTimeout works QVERIFY(!idleSpy.wait(500)); QVERIFY(!idleSpy.wait()); QCOMPARE(idleSpy.count(), 1); timeout.reset(); m_connection->flush(); m_display->dispatchEvents(); } QTEST_GUILESS_MAIN(IdleTest) #include "test_idle.moc" diff --git a/autotests/client/test_plasma_virtual_desktop.cpp b/autotests/client/test_plasma_virtual_desktop.cpp index b797258..d5fa347 100644 --- a/autotests/client/test_plasma_virtual_desktop.cpp +++ b/autotests/client/test_plasma_virtual_desktop.cpp @@ -1,547 +1,533 @@ -/******************************************************************** -Copyright 2018 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmavirtualdesktop.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/plasmavirtualdesktop_interface.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/client/plasmawindowmanagement.h" using namespace KWayland::Client; class TestVirtualDesktop : public QObject { Q_OBJECT public: explicit TestVirtualDesktop(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testSetRows(); void testConnectNewClient(); void testDestroy(); void testActivate(); void testEnterLeaveDesktop(); void testAllDesktops(); void testCreateRequested(); void testRemoveRequested(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_plasmaVirtualDesktopManagementInterface; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; KWayland::Server::PlasmaWindowInterface *m_windowInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::PlasmaVirtualDesktopManagement *m_plasmaVirtualDesktopManagement; KWayland::Client::EventQueue *m_queue; KWayland::Client::PlasmaWindowManagement *m_windowManagement; KWayland::Client::PlasmaWindow *m_window; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-virtual-desktop-0"); TestVirtualDesktop::TestVirtualDesktop(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestVirtualDesktop::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy plasmaVirtualDesktopManagementSpy(®istry, &Registry::plasmaVirtualDesktopManagementAnnounced); QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); QSignalSpy windowManagementSpy(®istry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); QVERIFY(windowManagementSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_plasmaVirtualDesktopManagementInterface = m_display->createPlasmaVirtualDesktopManagement(m_display); m_plasmaVirtualDesktopManagementInterface->create(); QVERIFY(m_plasmaVirtualDesktopManagementInterface->isValid()); QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); m_plasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagement(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); m_windowManagementInterface->create(); QVERIFY(m_windowManagementInterface->isValid()); m_windowManagementInterface->setPlasmaVirtualDesktopManagementInterface(m_plasmaVirtualDesktopManagementInterface); QVERIFY(windowManagementSpy.wait()); m_windowManagement = registry.createPlasmaWindowManagement(windowManagementSpy.first().first().value(), windowManagementSpy.first().last().value(), this); QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow*))); QVERIFY(windowSpy.isValid()); m_windowInterface = m_windowManagementInterface->createWindow(this); m_windowInterface->setPid(1337); QVERIFY(windowSpy.wait()); m_window = windowSpy.first().first().value(); } void TestVirtualDesktop::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_plasmaVirtualDesktopManagement) CLEANUP(m_windowInterface) CLEANUP(m_windowManagement) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_plasmaVirtualDesktopManagementInterface) CLEANUP(m_windowManagementInterface) CLEANUP(m_display) #undef CLEANUP } void TestVirtualDesktop::testCreate() { QSignalSpy desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopCreated); QSignalSpy managementDoneSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::done); //on this createDesktop bind() isn't called already, the desktopadded signals will be sent after bind happened KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-1")); desktop1Int->setName("Desktop 1"); desktopCreatedSpy.wait(); QList arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-1")); QCOMPARE(arguments.at(1).toUInt(), (quint32)0); m_plasmaVirtualDesktopManagementInterface->sendDone(); managementDoneSpy.wait(); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); QSignalSpy desktop1DoneSpy(desktop1, &PlasmaVirtualDesktop::done); desktop1Int->sendDone(); desktop1DoneSpy.wait(); QCOMPARE(desktop1->id(), QStringLiteral("0-1")); QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); //on those createDesktop the bind will already be done KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-2")); desktop2Int->setName("Desktop 2"); desktopCreatedSpy.wait(); arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-2")); QCOMPARE(arguments.at(1).toUInt(), (quint32)1); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); KWayland::Server::PlasmaVirtualDesktopInterface *desktop3Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-3")); desktop3Int->setName("Desktop 3"); desktopCreatedSpy.wait(); arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-3")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); m_plasmaVirtualDesktopManagementInterface->sendDone(); managementDoneSpy.wait(); //get the clients KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; QSignalSpy desktop2DoneSpy(desktop2, &PlasmaVirtualDesktop::done); desktop2Int->sendDone(); desktop2DoneSpy.wait(); KWayland::Client::PlasmaVirtualDesktop *desktop3 = m_plasmaVirtualDesktopManagement->desktops()[2]; QSignalSpy desktop3DoneSpy(desktop3, &PlasmaVirtualDesktop::done); desktop3Int->sendDone(); desktop3DoneSpy.wait(); QCOMPARE(desktop1->id(), QStringLiteral("0-1")); QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); QCOMPARE(desktop2->id(), QStringLiteral("0-2")); QCOMPARE(desktop2->name(), QStringLiteral("Desktop 2")); QCOMPARE(desktop3->id(), QStringLiteral("0-3")); QCOMPARE(desktop3->name(), QStringLiteral("Desktop 3")); //coherence of order between client and server QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 3); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); } } void TestVirtualDesktop::testSetRows() { //rebuild some desktops testCreate(); QSignalSpy rowsChangedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::rowsChanged); m_plasmaVirtualDesktopManagementInterface->setRows(3); QVERIFY(rowsChangedSpy.wait()); QCOMPARE(m_plasmaVirtualDesktopManagement->rows(), 3); } void TestVirtualDesktop::testConnectNewClient() { //rebuild some desktops testCreate(); Registry registry; QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QSignalSpy plasmaVirtualDesktopManagementSpy(®istry, &Registry::plasmaVirtualDesktopManagementAnnounced); QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); KWayland::Client::PlasmaVirtualDesktopManagement *otherPlasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagement(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); QSignalSpy managementDoneSpy(otherPlasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::done); managementDoneSpy.wait(); QCOMPARE(otherPlasmaVirtualDesktopManagement->desktops().length(), 3); delete otherPlasmaVirtualDesktopManagement; } void TestVirtualDesktop::testDestroy() { //rebuild some desktops testCreate(); KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); - + QSignalSpy desktop1IntDestroyedSpy(desktop1Int, &QObject::destroyed); QSignalSpy desktop1DestroyedSpy(desktop1, &QObject::destroyed); QSignalSpy desktop1RemovedSpy(desktop1, &KWayland::Client::PlasmaVirtualDesktop::removed); m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-1")); //test that both server and client desktoip interfaces go away desktop1IntDestroyedSpy.wait(); desktop1RemovedSpy.wait(); desktop1DestroyedSpy.wait(); //coherence of order between client and server QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 2); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); } //Test the desktopRemoved signal of the manager, remove another desktop as the signals can't be tested at the same time QSignalSpy desktopManagerRemovedSpy(m_plasmaVirtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved); m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-2")); desktopManagerRemovedSpy.wait(); QCOMPARE(desktopManagerRemovedSpy.takeFirst().at(0).toString(), QStringLiteral("0-2")); QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 1); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); } void TestVirtualDesktop::testActivate() { //rebuild some desktops testCreate(); KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); QVERIFY(desktop1->isActive()); QVERIFY(desktop1Int->isActive()); KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->desktops()[1]; KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; QVERIFY(!desktop2Int->isActive()); QSignalSpy requestActivateSpy(desktop2Int, &KWayland::Server::PlasmaVirtualDesktopInterface::activateRequested); QSignalSpy activatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::activated); desktop2->requestActivate(); requestActivateSpy.wait(); //This simulates a compositor which supports only one active desktop at a time for (auto *deskInt : m_plasmaVirtualDesktopManagementInterface->desktops()) { if (deskInt->id() == desktop2->id()) { deskInt->setActive(true); } else { deskInt->setActive(false); } } activatedSpy.wait(); //correct state in the server QVERIFY(desktop2Int->isActive()); QVERIFY(!desktop1Int->isActive()); //correct state in the client QVERIFY(desktop2Int->isActive()); QVERIFY(!desktop1Int->isActive()); //Test the deactivated signal QSignalSpy deactivatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::deactivated); for (auto *deskInt : m_plasmaVirtualDesktopManagementInterface->desktops()) { if (deskInt->id() == desktop1->id()) { deskInt->setActive(true); } else { deskInt->setActive(false); } } deactivatedSpy.wait(); } void TestVirtualDesktop::testEnterLeaveDesktop() { testCreate(); QSignalSpy enterRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::enterPlasmaVirtualDesktopRequested); m_window->requestEnterVirtualDesktop(QStringLiteral("0-1")); enterRequestedSpy.wait(); QCOMPARE(enterRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QSignalSpy virtualDesktopEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered); //agree to the request m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-1")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); //check if the client received the enter virtualDesktopEnteredSpy.wait(); QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); //add another desktop, server side m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); virtualDesktopEnteredSpy.wait(); QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-3")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 2); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); QCOMPARE(m_window->plasmaVirtualDesktops()[1], QStringLiteral("0-3")); //try to add an invalid desktop m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("invalid")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); //remove a desktop QSignalSpy leaveRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::leavePlasmaVirtualDesktopRequested); m_window->requestLeaveVirtualDesktop(QStringLiteral("0-1")); leaveRequestedSpy.wait(); QCOMPARE(leaveRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QSignalSpy virtualDesktopLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft); //agree to the request m_windowInterface->removePlasmaVirtualDesktop(QStringLiteral("0-1")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); //check if the client received the leave virtualDesktopLeftSpy.wait(); QCOMPARE(virtualDesktopLeftSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); //Destroy desktop 2 m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-3")); //the window should receive a left signal from the destroyed desktop virtualDesktopLeftSpy.wait(); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); } void TestVirtualDesktop::testAllDesktops() { testCreate(); QSignalSpy virtualDesktopEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered); QSignalSpy virtualDesktopLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft); //in the beginning the window is on desktop 1 and desktop 3 m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-1")); m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); virtualDesktopEnteredSpy.wait(); //setting on all desktops QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); m_windowInterface->setOnAllDesktops(true); //setting on all desktops, the window will leave every desktop virtualDesktopLeftSpy.wait(); QCOMPARE(virtualDesktopLeftSpy.count(), 2); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); QVERIFY(m_window->isOnAllDesktops()); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); QVERIFY(m_window->isOnAllDesktops()); //return to the active desktop (0-1) m_windowInterface->setOnAllDesktops(false); virtualDesktopEnteredSpy.wait(); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); QVERIFY(!m_window->isOnAllDesktops()); } void TestVirtualDesktop::testCreateRequested() { //rebuild some desktops testCreate(); QSignalSpy desktopCreateRequestedSpy(m_plasmaVirtualDesktopManagementInterface, &KWayland::Server::PlasmaVirtualDesktopManagementInterface::desktopCreateRequested); QSignalSpy desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopCreated); //listen for createdRequested m_plasmaVirtualDesktopManagement->requestCreateVirtualDesktop(QStringLiteral("Desktop"), 1); desktopCreateRequestedSpy.wait(); QCOMPARE(desktopCreateRequestedSpy.first().first().toString(), QStringLiteral("Desktop")); QCOMPARE(desktopCreateRequestedSpy.first().at(1).toUInt(), (quint32)1); //actually create m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-4"), 1); KWayland::Server::PlasmaVirtualDesktopInterface *desktopInt = m_plasmaVirtualDesktopManagementInterface->desktops().at(1); QCOMPARE(desktopInt->id(), QStringLiteral("0-4")); desktopInt->setName(QStringLiteral("Desktop")); desktopCreatedSpy.wait(); - + QCOMPARE(desktopCreatedSpy.first().first().toString(), QStringLiteral("0-4")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().count(), 4); PlasmaVirtualDesktop *desktop = m_plasmaVirtualDesktopManagement->desktops().at(1); QSignalSpy desktopDoneSpy(desktop, &PlasmaVirtualDesktop::done); desktopInt->sendDone(); // desktopDoneSpy.wait(); //check the order is correct QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(0)->id(), QStringLiteral("0-1")); QCOMPARE(desktop->id(), QStringLiteral("0-4")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(2)->id(), QStringLiteral("0-2")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(3)->id(), QStringLiteral("0-3")); } void TestVirtualDesktop::testRemoveRequested() { //rebuild some desktops testCreate(); QSignalSpy desktopRemoveRequestedSpy(m_plasmaVirtualDesktopManagementInterface, &KWayland::Server::PlasmaVirtualDesktopManagementInterface::desktopRemoveRequested); //request a remove, just check the request arrived, ignore the request. m_plasmaVirtualDesktopManagement->requestRemoveVirtualDesktop(QStringLiteral("0-1")); desktopRemoveRequestedSpy.wait(); QCOMPARE(desktopRemoveRequestedSpy.first().first().toString(), QStringLiteral("0-1")); } QTEST_GUILESS_MAIN(TestVirtualDesktop) #include "test_plasma_virtual_desktop.moc" diff --git a/autotests/client/test_plasma_window_model.cpp b/autotests/client/test_plasma_window_model.cpp index 358ddf3..d622c74 100644 --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -1,940 +1,926 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/plasmawindowmanagement.h" #include "../../src/client/plasmawindowmodel.h" // server #include "../../src/server/display.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/server/plasmavirtualdesktop_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) typedef void (KWayland::Client::PlasmaWindow::*ClientWindowSignal)(); Q_DECLARE_METATYPE(ClientWindowSignal) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBoolSetter)(bool); Q_DECLARE_METATYPE(ServerWindowBoolSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowStringSetter)(const QString&); Q_DECLARE_METATYPE(ServerWindowStringSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowQuint32Setter)(quint32); Q_DECLARE_METATYPE(ServerWindowQuint32Setter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowVoidSetter)(); Q_DECLARE_METATYPE(ServerWindowVoidSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowIconSetter)(const QIcon&); Q_DECLARE_METATYPE(ServerWindowIconSetter) class PlasmaWindowModelTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testRoleNames_data(); void testRoleNames(); void testAddRemoveRows(); void testDefaultData_data(); void testDefaultData(); void testIsActive(); void testIsFullscreenable(); void testIsFullscreen(); void testIsMaximizable(); void testIsMaximized(); void testIsMinimizable(); void testIsMinimized(); void testIsKeepAbove(); void testIsKeepBelow(); void testIsDemandingAttention(); void testSkipTaskbar(); void testSkipSwitcher(); void testIsShadeable(); void testIsShaded(); void testIsMovable(); void testIsResizable(); void testIsVirtualDesktopChangeable(); void testIsCloseable(); void testGeometry(); void testTitle(); void testAppId(); void testPid(); void testVirtualDesktops(); // TODO icon: can we ensure a theme is installed on CI? void testRequests(); // TODO: minimized geometry // TODO: model reset void testCreateWithUnmappedWindow(); void testChangeWindowAfterModelDestroy_data(); void testChangeWindowAfterModelDestroy(); void testCreateWindowAfterModelDestroy(); private: bool testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)); Display *m_display = nullptr; PlasmaWindowManagementInterface *m_pwInterface = nullptr; PlasmaWindowManagement *m_pw = nullptr; KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_plasmaVirtualDesktopManagementInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-fake-input-0"); void PlasmaWindowModelTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_pwInterface = m_display->createPlasmaWindowManagement(); m_pwInterface->create(); m_plasmaVirtualDesktopManagementInterface = m_display->createPlasmaVirtualDesktopManagement(m_display); m_plasmaVirtualDesktopManagementInterface->create(); QVERIFY(m_plasmaVirtualDesktopManagementInterface->isValid()); m_plasmaVirtualDesktopManagementInterface->createDesktop("desktop1"); m_plasmaVirtualDesktopManagementInterface->createDesktop("desktop2"); m_pwInterface->setPlasmaVirtualDesktopManagementInterface(m_plasmaVirtualDesktopManagementInterface); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_pw = registry.createPlasmaWindowManagement(registry.interface(Registry::Interface::PlasmaWindowManagement).name, registry.interface(Registry::Interface::PlasmaWindowManagement).version, this); QVERIFY(m_pw->isValid()); } void PlasmaWindowModelTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_pw) CLEANUP(m_plasmaVirtualDesktopManagementInterface) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_pwInterface) CLEANUP(m_display) #undef CLEANUP } bool PlasmaWindowModelTest::testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)) { #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return false; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return false; auto model = m_pw->createWindowModel(); VERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); VERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); VERIFY(w); VERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); VERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); COMPARE(model->data(index, role).toBool(), false); (w->*(function))(true); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 1); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), true); (w->*(function))(false); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 2); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), false); #undef COMPARE #undef VERIFY return true; } void PlasmaWindowModelTest::testRoleNames_data() { QTest::addColumn("role"); QTest::addColumn("name"); QTest::newRow("display") << int(Qt::DisplayRole) << QByteArrayLiteral("DisplayRole"); QTest::newRow("decoration") << int(Qt::DecorationRole) << QByteArrayLiteral("DecorationRole"); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QByteArrayLiteral("AppId"); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QByteArrayLiteral("Pid"); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QByteArrayLiteral("IsActive"); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QByteArrayLiteral("IsFullscreenable"); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QByteArrayLiteral("IsFullscreen"); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QByteArrayLiteral("IsMaximizable"); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QByteArrayLiteral("IsMaximized"); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QByteArrayLiteral("IsMinimizable"); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QByteArrayLiteral("IsMinimized"); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QByteArrayLiteral("IsKeepAbove"); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QByteArrayLiteral("IsKeepBelow"); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QByteArrayLiteral("VirtualDesktop"); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QByteArrayLiteral("IsOnAllDesktops"); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QByteArrayLiteral("IsDemandingAttention"); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QByteArrayLiteral("SkipTaskbar"); QTest::newRow("SkipSwitcher") << int(PlasmaWindowModel::SkipSwitcher) << QByteArrayLiteral("SkipSwitcher"); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QByteArrayLiteral("IsShadeable"); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QByteArrayLiteral("IsShaded"); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QByteArrayLiteral("IsMovable"); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QByteArrayLiteral("IsResizable"); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QByteArrayLiteral("IsVirtualDesktopChangeable"); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QByteArrayLiteral("IsCloseable"); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QByteArrayLiteral("Geometry"); } void PlasmaWindowModelTest::testRoleNames() { // just verifies that all role names are available auto model = m_pw->createWindowModel(); QVERIFY(model); const auto roles = model->roleNames(); QFETCH(int, role); auto it = roles.find(role); QVERIFY(it != roles.end()); QTEST(it.value(), "name"); } void PlasmaWindowModelTest::testAddRemoveRows() { // this test verifies that adding/removing rows to the Model works auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); // now let's add a row QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); // this happens by creating a PlasmaWindow on server side auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QCOMPARE(rowInsertedSpy.count(), 1); QVERIFY(!rowInsertedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(rowInsertedSpy.first().at(2).toInt(), 0); // the model should have a row now QCOMPARE(model->rowCount(), 1); QVERIFY(model->index(0).isValid()); // that index doesn't have children QCOMPARE(model->rowCount(model->index(0)), 0); // process events in order to ensure that the resource is created on server side before we unmap the window QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); // now let's remove that again QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); w->unmap(); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QVERIFY(!rowRemovedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowRemovedSpy.first().at(1).toInt(), 0); QCOMPARE(rowRemovedSpy.first().at(2).toInt(), 0); // now the model is empty again QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); QSignalSpy wDestroyedSpy(w, &QObject::destroyed); QVERIFY(wDestroyedSpy.isValid()); QVERIFY(wDestroyedSpy.wait()); } void PlasmaWindowModelTest::testDefaultData_data() { QTest::addColumn("role"); QTest::addColumn("value"); QTest::newRow("display") << int(Qt::DisplayRole) << QVariant(QString()); QTest::newRow("decoration") << int(Qt::DecorationRole) << QVariant(QIcon()); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QVariant(QString()); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QVariant(false); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QVariant(false); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QVariant(false); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QVariant(false); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QVariant(false); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QVariant(false); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QVariant(false); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QVariant(false); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QVariant(false); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QVariant(0); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QVariant(true); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QVariant(false); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QVariant(false); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QVariant(false); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QVariant(false); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QVariant(false); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QVariant(false); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QVariant(false); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QVariant(false); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QVariant(QRect()); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QVariant(0); } void PlasmaWindowModelTest::testDefaultData() { // this test validates the default data of a PlasmaWindow without having set any values // first create a model with a window auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QModelIndex index = model->index(0); QFETCH(int, role); QTEST(model->data(index, role), "value"); } void PlasmaWindowModelTest::testIsActive() { QVERIFY(testBooleanData(PlasmaWindowModel::IsActive, &PlasmaWindowInterface::setActive)); } void PlasmaWindowModelTest::testIsFullscreenable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreenable, &PlasmaWindowInterface::setFullscreenable)); } void PlasmaWindowModelTest::testIsFullscreen() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreen, &PlasmaWindowInterface::setFullscreen)); } void PlasmaWindowModelTest::testIsMaximizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximizable, &PlasmaWindowInterface::setMaximizeable)); } void PlasmaWindowModelTest::testIsMaximized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximized, &PlasmaWindowInterface::setMaximized)); } void PlasmaWindowModelTest::testIsMinimizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimizable, &PlasmaWindowInterface::setMinimizeable)); } void PlasmaWindowModelTest::testIsMinimized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimized, &PlasmaWindowInterface::setMinimized)); } void PlasmaWindowModelTest::testIsKeepAbove() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepAbove, &PlasmaWindowInterface::setKeepAbove)); } void PlasmaWindowModelTest::testIsKeepBelow() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepBelow, &PlasmaWindowInterface::setKeepBelow)); } void PlasmaWindowModelTest::testIsDemandingAttention() { QVERIFY(testBooleanData(PlasmaWindowModel::IsDemandingAttention, &PlasmaWindowInterface::setDemandsAttention)); } void PlasmaWindowModelTest::testSkipTaskbar() { QVERIFY(testBooleanData(PlasmaWindowModel::SkipTaskbar, &PlasmaWindowInterface::setSkipTaskbar)); } void PlasmaWindowModelTest::testSkipSwitcher() { QVERIFY(testBooleanData(PlasmaWindowModel::SkipSwitcher, &PlasmaWindowInterface::setSkipSwitcher)); } void PlasmaWindowModelTest::testIsShadeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShadeable, &PlasmaWindowInterface::setShadeable)); } void PlasmaWindowModelTest::testIsShaded() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShaded, &PlasmaWindowInterface::setShaded)); } void PlasmaWindowModelTest::testIsMovable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMovable, &PlasmaWindowInterface::setMovable)); } void PlasmaWindowModelTest::testIsResizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsResizable, &PlasmaWindowInterface::setResizable)); } void PlasmaWindowModelTest::testIsVirtualDesktopChangeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsVirtualDesktopChangeable, &PlasmaWindowInterface::setVirtualDesktopChangeable)); } void PlasmaWindowModelTest::testIsCloseable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsCloseable, &PlasmaWindowInterface::setCloseable)); } void PlasmaWindowModelTest::testGeometry() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), QRect()); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QRect geom(0, 15, 50, 75); w->setGeometry(geom); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::Geometry)}); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), geom); } void PlasmaWindowModelTest::testTitle() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QString()); w->setTitle(QStringLiteral("foo")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(Qt::DisplayRole)}); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QStringLiteral("foo")); } void PlasmaWindowModelTest::testAppId() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QString()); w->setAppId(QStringLiteral("org.kde.testapp")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::AppId)}); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QStringLiteral("org.kde.testapp")); } void PlasmaWindowModelTest::testPid() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); w->setPid(1337); QVERIFY(w); m_connection->flush(); m_display->dispatchEvents(); QVERIFY(rowInsertedSpy.wait()); //pid should be set as soon as the new row appears const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::Pid).toInt(), 1337); } void PlasmaWindowModelTest::testVirtualDesktops() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList()); w->addPlasmaVirtualDesktop("desktop1"); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 2); QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.first().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktops)}); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::IsOnAllDesktops)}); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({"desktop1"})); QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), false); dataChangedSpy.clear(); w->addPlasmaVirtualDesktop("desktop2"); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktops)}); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({"desktop1", "desktop2"})); QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), false); w->removePlasmaVirtualDesktop("desktop2"); w->removePlasmaVirtualDesktop("desktop1"); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::IsOnAllDesktops)}); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({})); QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), true); QVERIFY(!dataChangedSpy.wait(100)); } void PlasmaWindowModelTest::testRequests() { // this test verifies that the various requests are properly passed to the server auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QSignalSpy activateRequestedSpy(w, &PlasmaWindowInterface::activeRequested); QVERIFY(activateRequestedSpy.isValid()); QSignalSpy closeRequestedSpy(w, &PlasmaWindowInterface::closeRequested); QVERIFY(closeRequestedSpy.isValid()); QSignalSpy moveRequestedSpy(w, &PlasmaWindowInterface::moveRequested); QVERIFY(moveRequestedSpy.isValid()); QSignalSpy resizeRequestedSpy(w, &PlasmaWindowInterface::resizeRequested); QVERIFY(resizeRequestedSpy.isValid()); QSignalSpy virtualDesktopRequestedSpy(w, &PlasmaWindowInterface::virtualDesktopRequested); QVERIFY(virtualDesktopRequestedSpy.isValid()); QSignalSpy keepAboveRequestedSpy(w, &PlasmaWindowInterface::keepAboveRequested); QVERIFY(keepAboveRequestedSpy.isValid()); QSignalSpy keepBelowRequestedSpy(w, &PlasmaWindowInterface::keepBelowRequested); QVERIFY(keepBelowRequestedSpy.isValid()); QSignalSpy minimizedRequestedSpy(w, &PlasmaWindowInterface::minimizedRequested); QVERIFY(minimizedRequestedSpy.isValid()); QSignalSpy maximizeRequestedSpy(w, &PlasmaWindowInterface::maximizedRequested); QVERIFY(maximizeRequestedSpy.isValid()); QSignalSpy shadeRequestedSpy(w, &PlasmaWindowInterface::shadedRequested); QVERIFY(shadeRequestedSpy.isValid()); // first let's use some invalid row numbers model->requestActivate(-1); model->requestClose(-1); model->requestVirtualDesktop(-1, 1); model->requestToggleKeepAbove(-1); model->requestToggleKeepBelow(-1); model->requestToggleMinimized(-1); model->requestToggleMaximized(-1); model->requestActivate(1); model->requestClose(1); model->requestMove(1); model->requestResize(1); model->requestVirtualDesktop(1, 1); model->requestToggleKeepAbove(1); model->requestToggleKeepBelow(1); model->requestToggleMinimized(1); model->requestToggleMaximized(1); model->requestToggleShaded(1); // that should not have triggered any signals QVERIFY(!activateRequestedSpy.wait(100)); QVERIFY(activateRequestedSpy.isEmpty()); QVERIFY(closeRequestedSpy.isEmpty()); QVERIFY(moveRequestedSpy.isEmpty()); QVERIFY(resizeRequestedSpy.isEmpty()); QVERIFY(virtualDesktopRequestedSpy.isEmpty()); QVERIFY(minimizedRequestedSpy.isEmpty()); QVERIFY(maximizeRequestedSpy.isEmpty()); QVERIFY(shadeRequestedSpy.isEmpty()); // now with the proper row // activate model->requestActivate(0); QVERIFY(activateRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(activateRequestedSpy.first().first().toBool(), true); QCOMPARE(closeRequestedSpy.count(), 0); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // close model->requestClose(0); QVERIFY(closeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // move model->requestMove(0); QVERIFY(moveRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // resize model->requestResize(0); QVERIFY(resizeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // virtual desktop model->requestVirtualDesktop(0, 1); QVERIFY(virtualDesktopRequestedSpy.wait()); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.first().first().toUInt(), 1u); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // keep above model->requestToggleKeepAbove(0); QVERIFY(keepAboveRequestedSpy.wait()); QCOMPARE(keepAboveRequestedSpy.count(), 1); QCOMPARE(keepAboveRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // keep Below model->requestToggleKeepBelow(0); QVERIFY(keepBelowRequestedSpy.wait()); QCOMPARE(keepBelowRequestedSpy.count(), 1); QCOMPARE(keepBelowRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // minimize model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // maximize model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.count(), 0); // shade model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 1); // the toggles can also support a different state QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); // keepAbove w->setKeepAbove(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleKeepAbove(0); QVERIFY(keepAboveRequestedSpy.wait()); QCOMPARE(keepAboveRequestedSpy.count(), 2); QCOMPARE(keepAboveRequestedSpy.last().first().toBool(), false); // keepBelow w->setKeepBelow(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleKeepBelow(0); QVERIFY(keepBelowRequestedSpy.wait()); QCOMPARE(keepBelowRequestedSpy.count(), 2); QCOMPARE(keepBelowRequestedSpy.last().first().toBool(), false); // minimize w->setMinimized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 2); QCOMPARE(minimizedRequestedSpy.last().first().toBool(), false); // maximized w->setMaximized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 2); QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false); // shaded w->setShaded(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 2); QCOMPARE(shadeRequestedSpy.last().first().toBool(), false); } void PlasmaWindowModelTest::testCreateWithUnmappedWindow() { // this test verifies that creating the model just when an unmapped window exists doesn't cause problems // that is the unmapped window should be added (as expected), but also be removed again // create a window in "normal way" QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); QVERIFY(window); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QSignalSpy unmappedSpy(window, &PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(window, &PlasmaWindow::destroyed); QVERIFY(destroyedSpy.isValid()); // unmap should be triggered, but not yet the destroyed w->unmap(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.isEmpty()); auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 1); QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QCOMPARE(model->rowCount(), 0); QCOMPARE(destroyedSpy.count(), 1); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy_data() { QTest::addColumn("changedSignal"); QTest::addColumn("setter"); QTest::addColumn("value"); QTest::newRow("active") << &PlasmaWindow::activeChanged << QVariant::fromValue(&PlasmaWindowInterface::setActive) << QVariant(true); QTest::newRow("minimized") << &PlasmaWindow::minimizedChanged << QVariant::fromValue(&PlasmaWindowInterface::setMinimized) << QVariant(true); QTest::newRow("fullscreen") << &PlasmaWindow::fullscreenChanged << QVariant::fromValue(&PlasmaWindowInterface::setFullscreen) << QVariant(true); QTest::newRow("keepAbove") << &PlasmaWindow::keepAboveChanged << QVariant::fromValue(&PlasmaWindowInterface::setKeepAbove) << QVariant(true); QTest::newRow("keepBelow") << &PlasmaWindow::keepBelowChanged << QVariant::fromValue(&PlasmaWindowInterface::setKeepBelow) << QVariant(true); QTest::newRow("maximized") << &PlasmaWindow::maximizedChanged << QVariant::fromValue(&PlasmaWindowInterface::setMaximized) << QVariant(true); QTest::newRow("demandsAttention") << &PlasmaWindow::demandsAttentionChanged << QVariant::fromValue(&PlasmaWindowInterface::setDemandsAttention) << QVariant(true); QTest::newRow("closeable") << &PlasmaWindow::closeableChanged << QVariant::fromValue(&PlasmaWindowInterface::setCloseable) << QVariant(true); QTest::newRow("minimizeable") << &PlasmaWindow::minimizeableChanged << QVariant::fromValue(&PlasmaWindowInterface::setMinimizeable) << QVariant(true); QTest::newRow("maximizeable") << &PlasmaWindow::maximizeableChanged << QVariant::fromValue(&PlasmaWindowInterface::setMaximizeable) << QVariant(true); QTest::newRow("fullscreenable") << &PlasmaWindow::fullscreenableChanged << QVariant::fromValue(&PlasmaWindowInterface::setFullscreenable) << QVariant(true); QTest::newRow("skipTaskbar") << &PlasmaWindow::skipTaskbarChanged << QVariant::fromValue(&PlasmaWindowInterface::setSkipTaskbar) << QVariant(true); QTest::newRow("shadeable") << &PlasmaWindow::shadeableChanged << QVariant::fromValue(&PlasmaWindowInterface::setShadeable) << QVariant(true); QTest::newRow("shaded") << &PlasmaWindow::shadedChanged << QVariant::fromValue(&PlasmaWindowInterface::setShaded) << QVariant(true); QTest::newRow("movable") << &PlasmaWindow::movableChanged << QVariant::fromValue(&PlasmaWindowInterface::setMovable) << QVariant(true); QTest::newRow("resizable") << &PlasmaWindow::resizableChanged << QVariant::fromValue(&PlasmaWindowInterface::setResizable) << QVariant(true); QTest::newRow("vdChangeable") << &PlasmaWindow::virtualDesktopChangeableChanged << QVariant::fromValue(&PlasmaWindowInterface::setVirtualDesktopChangeable) << QVariant(true); QTest::newRow("onallDesktop") << &PlasmaWindow::onAllDesktopsChanged << QVariant::fromValue(&PlasmaWindowInterface::setOnAllDesktops) << QVariant(true); QTest::newRow("title") << &PlasmaWindow::titleChanged << QVariant::fromValue(&PlasmaWindowInterface::setTitle) << QVariant(QStringLiteral("foo")); QTest::newRow("appId") << &PlasmaWindow::appIdChanged << QVariant::fromValue(&PlasmaWindowInterface::setAppId) << QVariant(QStringLiteral("foo")); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 28) QTest::newRow("iconname" ) << &PlasmaWindow::iconChanged << QVariant::fromValue(&PlasmaWindowInterface::setThemedIconName) << QVariant(QStringLiteral("foo")); #endif QTest::newRow("icon" ) << &PlasmaWindow::iconChanged << QVariant::fromValue(&PlasmaWindowInterface::setIcon) << QVariant::fromValue(QIcon::fromTheme(QStringLiteral("foo"))); QTest::newRow("vd") << &PlasmaWindow::virtualDesktopChanged << QVariant::fromValue(&PlasmaWindowInterface::setVirtualDesktop) << QVariant(2u); QTest::newRow("unmapped") << &PlasmaWindow::unmapped << QVariant::fromValue(&PlasmaWindowInterface::unmap) << QVariant(); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy() { // this test verifies that changes in a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QCOMPARE(model->rowCount(), 1); delete model; QFETCH(ClientWindowSignal, changedSignal); QSignalSpy changedSpy(window, changedSignal); QVERIFY(changedSpy.isValid()); QVERIFY(!window->isActive()); QFETCH(QVariant, setter); QFETCH(QVariant, value); if (QMetaType::Type(value.type()) == QMetaType::Bool) { (w->*(setter.value()))(value.toBool()); } else if (QMetaType::Type(value.type()) == QMetaType::QString) { (w->*(setter.value()))(value.toString()); } else if (QMetaType::Type(value.type()) == QMetaType::UInt) { (w->*(setter.value()))(value.toUInt()); } else if (!value.isValid()) { (w->*(setter.value()))(); } QVERIFY(changedSpy.wait()); } void PlasmaWindowModelTest::testCreateWindowAfterModelDestroy() { // this test verifies that creating a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); delete model; QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); } QTEST_GUILESS_MAIN(PlasmaWindowModelTest) #include "test_plasma_window_model.moc" diff --git a/autotests/client/test_plasmashell.cpp b/autotests/client/test_plasmashell.cpp index e14d953..e284fd2 100644 --- a/autotests/client/test_plasmashell.cpp +++ b/autotests/client/test_plasmashell.cpp @@ -1,538 +1,524 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWayland #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmashell.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/plasmashell_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class TestPlasmaShell : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testRole_data(); void testRole(); void testPosition(); void testSkipTaskbar(); void testSkipSwitcher(); void testPanelBehavior_data(); void testPanelBehavior(); void testAutoHidePanel(); void testPanelTakesFocus(); void testDisconnect(); void testWhileDestroying(); private: Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; PlasmaShellInterface *m_plasmaShellInterface = nullptr; ConnectionThread *m_connection = nullptr; Compositor *m_compositor = nullptr; EventQueue *m_queue = nullptr; QThread *m_thread = nullptr; Registry *m_registry = nullptr; PlasmaShell *m_plasmaShell = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-plasma-shell-0"); void TestPlasmaShell::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_display->createShm(); m_plasmaShellInterface = m_display->createPlasmaShell(m_display); m_plasmaShellInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy interfacesAnnouncedSpy(m_registry, &Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); #define CREATE(variable, factory, iface) \ variable = m_registry->create##factory(m_registry->interface(Registry::Interface::iface).name, m_registry->interface(Registry::Interface::iface).version, this); \ QVERIFY(variable); CREATE(m_compositor, Compositor, Compositor) CREATE(m_plasmaShell, PlasmaShell, PlasmaShell) #undef CREATE } void TestPlasmaShell::cleanup() { #define DELETE(name) \ if (name) { \ delete name; \ name = nullptr; \ } DELETE(m_plasmaShell) DELETE(m_compositor) DELETE(m_queue) DELETE(m_registry) #undef DELETE if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestPlasmaShell::testRole_data() { QTest::addColumn("clientRole"); QTest::addColumn("serverRole"); QTest::newRow("desktop") << PlasmaShellSurface::Role::Desktop << PlasmaShellSurfaceInterface::Role::Desktop; QTest::newRow("osd") << PlasmaShellSurface::Role::OnScreenDisplay << PlasmaShellSurfaceInterface::Role::OnScreenDisplay; QTest::newRow("panel") << PlasmaShellSurface::Role::Panel << PlasmaShellSurfaceInterface::Role::Panel; QTest::newRow("notification") << PlasmaShellSurface::Role::Notification << PlasmaShellSurfaceInterface::Role::Notification; QTest::newRow("tooltip") << PlasmaShellSurface::Role::ToolTip << PlasmaShellSurfaceInterface::Role::ToolTip; QTest::newRow("criticalnotification") << PlasmaShellSurface::Role::CriticalNotification << PlasmaShellSurfaceInterface::Role::CriticalNotification; } void TestPlasmaShell::testRole() { // this test verifies that setting the role on a plasma shell surface works // first create signal spies QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); // create the surface QScopedPointer s(m_compositor->createSurface()); // no PlasmaShellSurface for the Surface yet yet QVERIFY(!PlasmaShellSurface::get(s.data())); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QCOMPARE(ps->role(), PlasmaShellSurface::Role::Normal); // now we should have a PlasmaShellSurface for QCOMPARE(PlasmaShellSurface::get(s.data()), ps.data()); // try to create another PlasmaShellSurface for the same Surface, should return from cache QCOMPARE(m_plasmaShell->createSurface(s.data()), ps.data()); // and get them on the server QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); QCOMPARE(surfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QCOMPARE(sps->surface(), surfaceCreatedSpy.first().first().value()); QCOMPARE(sps->shell(), m_plasmaShellInterface); QCOMPARE(PlasmaShellSurfaceInterface::get(sps->resource()), sps); QVERIFY(!PlasmaShellSurfaceInterface::get(nullptr)); // default role should be normal QCOMPARE(sps->role(), PlasmaShellSurfaceInterface::Role::Normal); // now change it QSignalSpy roleChangedSpy(sps, &PlasmaShellSurfaceInterface::roleChanged); QVERIFY(roleChangedSpy.isValid()); QFETCH(PlasmaShellSurface::Role, clientRole); ps->setRole(clientRole); QCOMPARE(ps->role(), clientRole); QVERIFY(roleChangedSpy.wait()); QCOMPARE(roleChangedSpy.count(), 1); QTEST(sps->role(), "serverRole"); // try changing again should not emit the signal ps->setRole(clientRole); QVERIFY(!roleChangedSpy.wait(100)); // set role back to normal ps->setRole(PlasmaShellSurface::Role::Normal); QCOMPARE(ps->role(), PlasmaShellSurface::Role::Normal); QVERIFY(roleChangedSpy.wait()); QCOMPARE(roleChangedSpy.count(), 2); QCOMPARE(sps->role(), PlasmaShellSurfaceInterface::Role::Normal); } void TestPlasmaShell::testPosition() { // this test verifies that updating the position of a PlasmaShellSurface is properly passed to the server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); // default position should not be set QVERIFY(!sps->isPositionSet()); QCOMPARE(sps->position(), QPoint()); // now let's try to change the position QSignalSpy positionChangedSpy(sps, &PlasmaShellSurfaceInterface::positionChanged); QVERIFY(positionChangedSpy.isValid()); ps->setPosition(QPoint(1, 2)); QVERIFY(positionChangedSpy.wait()); QCOMPARE(positionChangedSpy.count(), 1); QVERIFY(sps->isPositionSet()); QCOMPARE(sps->position(), QPoint(1, 2)); // let's try to set same position, should not trigger an update ps->setPosition(QPoint(1, 2)); QVERIFY(!positionChangedSpy.wait(100)); // different point should work, though ps->setPosition(QPoint(3, 4)); QVERIFY(positionChangedSpy.wait()); QCOMPARE(positionChangedSpy.count(), 2); QCOMPARE(sps->position(), QPoint(3, 4)); } void TestPlasmaShell::testSkipTaskbar() { // this test verifies that sip taskbar is properly passed to server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QVERIFY(!sps->skipTaskbar()); // now change QSignalSpy skipTaskbarChangedSpy(sps, &PlasmaShellSurfaceInterface::skipTaskbarChanged); QVERIFY(skipTaskbarChangedSpy.isValid()); ps->setSkipTaskbar(true); QVERIFY(skipTaskbarChangedSpy.wait()); QVERIFY(sps->skipTaskbar()); // setting to same again should not emit the signal ps->setSkipTaskbar(true); QEXPECT_FAIL("", "Should not be emitted if not changed", Continue); QVERIFY(!skipTaskbarChangedSpy.wait(100)); QVERIFY(sps->skipTaskbar()); // setting to false should change again ps->setSkipTaskbar(false); QVERIFY(skipTaskbarChangedSpy.wait()); QVERIFY(!sps->skipTaskbar()); } void TestPlasmaShell::testSkipSwitcher() { // this test verifies that Skip Switcher is properly passed to server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QVERIFY(!sps->skipSwitcher()); // now change QSignalSpy skipSwitcherChangedSpy(sps, &PlasmaShellSurfaceInterface::skipSwitcherChanged); QVERIFY(skipSwitcherChangedSpy.isValid()); ps->setSkipSwitcher(true); QVERIFY(skipSwitcherChangedSpy.wait()); QVERIFY(sps->skipSwitcher()); // setting to same again should not emit the signal ps->setSkipSwitcher(true); QEXPECT_FAIL("", "Should not be emitted if not changed", Continue); QVERIFY(!skipSwitcherChangedSpy.wait(100)); QVERIFY(sps->skipSwitcher()); // setting to false should change again ps->setSkipSwitcher(false); QVERIFY(skipSwitcherChangedSpy.wait()); QVERIFY(!sps->skipSwitcher()); } void TestPlasmaShell::testPanelBehavior_data() { QTest::addColumn("client"); QTest::addColumn("server"); QTest::newRow("autohide") << PlasmaShellSurface::PanelBehavior::AutoHide << PlasmaShellSurfaceInterface::PanelBehavior::AutoHide; QTest::newRow("can cover") << PlasmaShellSurface::PanelBehavior::WindowsCanCover << PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover; QTest::newRow("go below") << PlasmaShellSurface::PanelBehavior::WindowsGoBelow << PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow; } void TestPlasmaShell::testPanelBehavior() { // this test verifies that the panel behavior is properly passed to the server QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); ps->setRole(PlasmaShellSurface::Role::Panel); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); // verify that we got a plasma shell surface auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QVERIFY(sps->surface()); QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible); // now change the behavior QSignalSpy behaviorChangedSpy(sps, &PlasmaShellSurfaceInterface::panelBehaviorChanged); QVERIFY(behaviorChangedSpy.isValid()); QFETCH(PlasmaShellSurface::PanelBehavior, client); ps->setPanelBehavior(client); QVERIFY(behaviorChangedSpy.wait()); QTEST(sps->panelBehavior(), "server"); // changing to same should not trigger the signal ps->setPanelBehavior(client); QVERIFY(!behaviorChangedSpy.wait(100)); // but changing back to Always Visible should work ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); QVERIFY(behaviorChangedSpy.wait()); QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible); } void TestPlasmaShell::testAutoHidePanel() { // this test verifies that auto-hiding panels work correctly QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); ps->setRole(PlasmaShellSurface::Role::Panel); ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AutoHide); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); QCOMPARE(sps->panelBehavior(), PlasmaShellSurfaceInterface::PanelBehavior::AutoHide); QSignalSpy autoHideRequestedSpy(sps, &PlasmaShellSurfaceInterface::panelAutoHideHideRequested); QVERIFY(autoHideRequestedSpy.isValid()); QSignalSpy autoHideShowRequestedSpy(sps, &PlasmaShellSurfaceInterface::panelAutoHideShowRequested); QVERIFY(autoHideShowRequestedSpy.isValid()); ps->requestHideAutoHidingPanel(); QVERIFY(autoHideRequestedSpy.wait()); QCOMPARE(autoHideRequestedSpy.count(), 1); QCOMPARE(autoHideShowRequestedSpy.count(), 0); QSignalSpy panelShownSpy(ps.data(), &PlasmaShellSurface::autoHidePanelShown); QVERIFY(panelShownSpy.isValid()); QSignalSpy panelHiddenSpy(ps.data(), &PlasmaShellSurface::autoHidePanelHidden); QVERIFY(panelHiddenSpy.isValid()); sps->hideAutoHidingPanel(); QVERIFY(panelHiddenSpy.wait()); QCOMPARE(panelHiddenSpy.count(), 1); QCOMPARE(panelShownSpy.count(), 0); ps->requestShowAutoHidingPanel(); QVERIFY(autoHideShowRequestedSpy.wait()); QCOMPARE(autoHideRequestedSpy.count(), 1); QCOMPARE(autoHideShowRequestedSpy.count(), 1); sps->showAutoHidingPanel(); QVERIFY(panelShownSpy.wait()); QCOMPARE(panelHiddenSpy.count(), 1); QCOMPARE(panelShownSpy.count(), 1); // change panel type ps->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible); // requesting auto hide should raise error QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(errorSpy.isValid()); ps->requestHideAutoHidingPanel(); QVERIFY(errorSpy.wait()); } void TestPlasmaShell::testPanelTakesFocus() { // this test verifies that whether a panel wants to take focus is passed through correctly QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); ps->setRole(PlasmaShellSurface::Role::Panel); QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QSignalSpy plasmaSurfaceTakesFocusSpy(sps, &PlasmaShellSurfaceInterface::panelTakesFocusChanged); QVERIFY(sps); QCOMPARE(sps->role(), PlasmaShellSurfaceInterface::Role::Panel); QCOMPARE(sps->panelTakesFocus(), false); ps->setPanelTakesFocus(true); m_connection->flush(); QVERIFY(plasmaSurfaceTakesFocusSpy.wait()); QCOMPARE(plasmaSurfaceTakesFocusSpy.count(), 1); QCOMPARE(sps->panelTakesFocus(), true); ps->setPanelTakesFocus(false); m_connection->flush(); QVERIFY(plasmaSurfaceTakesFocusSpy.wait()); QCOMPARE(plasmaSurfaceTakesFocusSpy.count(), 2); QCOMPARE(sps->panelTakesFocus(), false); } void TestPlasmaShell::testDisconnect() { // this test verifies that a disconnect cleans up QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(plasmaSurfaceCreatedSpy.isValid()); // create the surface QScopedPointer s(m_compositor->createSurface()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); // and get them on the server QVERIFY(plasmaSurfaceCreatedSpy.wait()); QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); auto sps = plasmaSurfaceCreatedSpy.first().first().value(); QVERIFY(sps); // disconnect QSignalSpy clientDisconnectedSpy(sps->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy surfaceDestroyedSpy(sps, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(surfaceDestroyedSpy.count(), 0); QVERIFY(surfaceDestroyedSpy.wait()); QCOMPARE(surfaceDestroyedSpy.count(), 1); s->destroy(); ps->destroy(); m_plasmaShell->destroy(); m_compositor->destroy(); m_registry->destroy(); m_queue->destroy(); } void TestPlasmaShell::testWhileDestroying() { // this test tries to hit a condition that a Surface gets created with an ID which was already // used for a previous Surface. For each Surface we try to create a PlasmaShellSurface. // Even if there was a Surface in the past with the same ID, it should create the PlasmaShellSurface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // create ShellSurface QSignalSpy shellSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_plasmaShell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); // now try to create more surfaces QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); for (int i = 0; i < 100; i++) { s.reset(); s.reset(m_compositor->createSurface()); m_plasmaShell->createSurface(s.data(), this); QVERIFY(surfaceCreatedSpy.wait()); } QVERIFY(clientErrorSpy.isEmpty()); QVERIFY(!clientErrorSpy.wait(100)); QVERIFY(clientErrorSpy.isEmpty()); } QTEST_GUILESS_MAIN(TestPlasmaShell) #include "test_plasmashell.moc" diff --git a/autotests/client/test_pointer_constraints.cpp b/autotests/client/test_pointer_constraints.cpp index b9dad00..17c501d 100644 --- a/autotests/client/test_pointer_constraints.cpp +++ b/autotests/client/test_pointer_constraints.cpp @@ -1,452 +1,438 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/event_queue.h" #include "../../src/client/pointer.h" #include "../../src/client/pointerconstraints.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" // server #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/pointerconstraints_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/surface_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(KWayland::Client::PointerConstraints::LifeTime) Q_DECLARE_METATYPE(KWayland::Server::ConfinedPointerInterface::LifeTime) Q_DECLARE_METATYPE(KWayland::Server::LockedPointerInterface::LifeTime) class TestPointerConstraints : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testLockPointer_data(); void testLockPointer(); void testConfinePointer_data(); void testConfinePointer(); void testAlreadyConstrained_data(); void testAlreadyConstrained(); private: Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; SeatInterface *m_seatInterface = nullptr; PointerConstraintsInterface *m_pointerConstraintsInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Compositor *m_compositor = nullptr; Seat *m_seat = nullptr; Pointer *m_pointer = nullptr; PointerConstraints *m_pointerConstraints = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-pointer_constraint-0"); void TestPointerConstraints::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasPointer(true); m_seatInterface->create(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_pointerConstraintsInterface = m_display->createPointerConstraints(PointerConstraintsInterfaceVersion::UnstableV1, m_display); m_pointerConstraintsInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced); QVERIFY(interfaceAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor); QVERIFY(m_compositor->isValid()); m_pointerConstraints = registry.createPointerConstraints(registry.interface(Registry::Interface::PointerConstraintsUnstableV1).name, registry.interface(Registry::Interface::PointerConstraintsUnstableV1).version, this); QVERIFY(m_pointerConstraints); QVERIFY(m_pointerConstraints->isValid()); m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); QVERIFY(m_seat); QVERIFY(m_seat->isValid()); QSignalSpy pointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerChangedSpy.isValid()); QVERIFY(pointerChangedSpy.wait()); m_pointer = m_seat->createPointer(this); QVERIFY(m_pointer); } void TestPointerConstraints::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_pointerConstraints) CLEANUP(m_pointer) CLEANUP(m_seat) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_seatInterface); CLEANUP(m_pointerConstraintsInterface) CLEANUP(m_display) #undef CLEANUP } void TestPointerConstraints::testLockPointer_data() { QTest::addColumn("clientLifeTime"); QTest::addColumn("serverLifeTime"); QTest::addColumn("hasConstraintAfterUnlock"); QTest::addColumn("pointerChangedCount"); QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << LockedPointerInterface::LifeTime::Persistent << true << 1; QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << LockedPointerInterface::LifeTime::OneShot << false << 2; } void TestPointerConstraints::testLockPointer() { // this test verifies the basic interaction for lock pointer // first create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->lockedPointer().isNull()); QVERIFY(serverSurface->confinedPointer().isNull()); // now create the locked pointer QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged); QVERIFY(pointerConstraintsChangedSpy.isValid()); QFETCH(PointerConstraints::LifeTime, clientLifeTime); QScopedPointer lockedPointer(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, clientLifeTime)); QSignalSpy lockedSpy(lockedPointer.data(), &LockedPointer::locked); QVERIFY(lockedSpy.isValid()); QSignalSpy unlockedSpy(lockedPointer.data(), &LockedPointer::unlocked); QVERIFY(unlockedSpy.isValid()); QVERIFY(lockedPointer->isValid()); QVERIFY(pointerConstraintsChangedSpy.wait()); auto serverLockedPointer = serverSurface->lockedPointer(); QVERIFY(serverLockedPointer); QVERIFY(serverSurface->confinedPointer().isNull()); QCOMPARE(serverLockedPointer->isLocked(), false); QCOMPARE(serverLockedPointer->region(), QRegion()); QFETCH(LockedPointerInterface::LifeTime, serverLifeTime); QCOMPARE(serverLockedPointer->lifeTime(), serverLifeTime); // setting to unlocked now should not trigger an unlocked spy serverLockedPointer->setLocked(false); QVERIFY(!unlockedSpy.wait(500)); // try setting a region QSignalSpy destroyedSpy(serverLockedPointer.data(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); QSignalSpy regionChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::regionChanged); QVERIFY(regionChangedSpy.isValid()); lockedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor)); // it's double buffered QVERIFY(!regionChangedSpy.wait(500)); surface->commit(Surface::CommitFlag::None); QVERIFY(regionChangedSpy.wait()); QCOMPARE(serverLockedPointer->region(), QRegion(0, 5, 10, 20)); // and unset region again lockedPointer->setRegion(nullptr); surface->commit(Surface::CommitFlag::None); QVERIFY(regionChangedSpy.wait()); QCOMPARE(serverLockedPointer->region(), QRegion()); // let's lock the surface QSignalSpy lockedChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::lockedChanged); QVERIFY(lockedChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); QSignalSpy pointerMotionSpy(m_pointer, &Pointer::motion); QVERIFY(pointerMotionSpy.isValid()); m_seatInterface->setPointerPos(QPoint(0, 1)); QVERIFY(pointerMotionSpy.wait()); serverLockedPointer->setLocked(true); QCOMPARE(serverLockedPointer->isLocked(), true); m_seatInterface->setPointerPos(QPoint(1, 1)); QCOMPARE(lockedChangedSpy.count(), 1); QCOMPARE(pointerMotionSpy.count(), 1); QVERIFY(lockedSpy.isEmpty()); QVERIFY(lockedSpy.wait()); QVERIFY(unlockedSpy.isEmpty()); const QPointF hint = QPointF(1.5, 0.5); QSignalSpy hintChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::cursorPositionHintChanged); lockedPointer->setCursorPositionHint(hint); QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.)); surface->commit(Surface::CommitFlag::None); QVERIFY(hintChangedSpy.wait()); QCOMPARE(serverLockedPointer->cursorPositionHint(), hint); // and unlock again serverLockedPointer->setLocked(false); QCOMPARE(serverLockedPointer->isLocked(), false); QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.)); QCOMPARE(lockedChangedSpy.count(), 2); QTEST(!serverSurface->lockedPointer().isNull(), "hasConstraintAfterUnlock"); QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount"); QVERIFY(unlockedSpy.wait()); QCOMPARE(unlockedSpy.count(), 1); QCOMPARE(lockedSpy.count(), 1); // now motion should work again m_seatInterface->setPointerPos(QPoint(0, 1)); QVERIFY(pointerMotionSpy.wait()); QCOMPARE(pointerMotionSpy.count(), 2); lockedPointer.reset(); QVERIFY(destroyedSpy.wait()); QCOMPARE(pointerConstraintsChangedSpy.count(), 2); } void TestPointerConstraints::testConfinePointer_data() { QTest::addColumn("clientLifeTime"); QTest::addColumn("serverLifeTime"); QTest::addColumn("hasConstraintAfterUnlock"); QTest::addColumn("pointerChangedCount"); QTest::newRow("persistent") << PointerConstraints::LifeTime::Persistent << ConfinedPointerInterface::LifeTime::Persistent << true << 1; QTest::newRow("oneshot") << PointerConstraints::LifeTime::OneShot << ConfinedPointerInterface::LifeTime::OneShot << false << 2; } void TestPointerConstraints::testConfinePointer() { // this test verifies the basic interaction for confined pointer // first create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->lockedPointer().isNull()); QVERIFY(serverSurface->confinedPointer().isNull()); // now create the confined pointer QSignalSpy pointerConstraintsChangedSpy(serverSurface, &SurfaceInterface::pointerConstraintsChanged); QVERIFY(pointerConstraintsChangedSpy.isValid()); QFETCH(PointerConstraints::LifeTime, clientLifeTime); QScopedPointer confinedPointer(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, clientLifeTime)); QSignalSpy confinedSpy(confinedPointer.data(), &ConfinedPointer::confined); QVERIFY(confinedSpy.isValid()); QSignalSpy unconfinedSpy(confinedPointer.data(), &ConfinedPointer::unconfined); QVERIFY(unconfinedSpy.isValid()); QVERIFY(confinedPointer->isValid()); QVERIFY(pointerConstraintsChangedSpy.wait()); auto serverConfinedPointer = serverSurface->confinedPointer(); QVERIFY(serverConfinedPointer); QVERIFY(serverSurface->lockedPointer().isNull()); QCOMPARE(serverConfinedPointer->isConfined(), false); QCOMPARE(serverConfinedPointer->region(), QRegion()); QFETCH(ConfinedPointerInterface::LifeTime, serverLifeTime); QCOMPARE(serverConfinedPointer->lifeTime(), serverLifeTime); // setting to unconfined now should not trigger an unconfined spy serverConfinedPointer->setConfined(false); QVERIFY(!unconfinedSpy.wait(500)); // try setting a region QSignalSpy destroyedSpy(serverConfinedPointer.data(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); QSignalSpy regionChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::regionChanged); QVERIFY(regionChangedSpy.isValid()); confinedPointer->setRegion(m_compositor->createRegion(QRegion(0, 5, 10, 20), m_compositor)); // it's double buffered QVERIFY(!regionChangedSpy.wait(500)); surface->commit(Surface::CommitFlag::None); QVERIFY(regionChangedSpy.wait()); QCOMPARE(serverConfinedPointer->region(), QRegion(0, 5, 10, 20)); // and unset region again confinedPointer->setRegion(nullptr); surface->commit(Surface::CommitFlag::None); QVERIFY(regionChangedSpy.wait()); QCOMPARE(serverConfinedPointer->region(), QRegion()); // let's confine the surface QSignalSpy confinedChangedSpy(serverConfinedPointer.data(), &ConfinedPointerInterface::confinedChanged); QVERIFY(confinedChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); serverConfinedPointer->setConfined(true); QCOMPARE(serverConfinedPointer->isConfined(), true); QCOMPARE(confinedChangedSpy.count(), 1); QVERIFY(confinedSpy.isEmpty()); QVERIFY(confinedSpy.wait()); QVERIFY(unconfinedSpy.isEmpty()); // and unconfine again serverConfinedPointer->setConfined(false); QCOMPARE(serverConfinedPointer->isConfined(), false); QCOMPARE(confinedChangedSpy.count(), 2); QTEST(!serverSurface->confinedPointer().isNull(), "hasConstraintAfterUnlock"); QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount"); QVERIFY(unconfinedSpy.wait()); QCOMPARE(unconfinedSpy.count(), 1); QCOMPARE(confinedSpy.count(), 1); confinedPointer.reset(); QVERIFY(destroyedSpy.wait()); QCOMPARE(pointerConstraintsChangedSpy.count(), 2); } enum class Constraint { Lock, Confine }; Q_DECLARE_METATYPE(Constraint) void TestPointerConstraints::testAlreadyConstrained_data() { QTest::addColumn("firstConstraint"); QTest::addColumn("secondConstraint"); QTest::newRow("confine-confine") << Constraint::Confine << Constraint::Confine; QTest::newRow("lock-confine") << Constraint::Lock << Constraint::Confine; QTest::newRow("confine-lock") << Constraint::Confine << Constraint::Lock; QTest::newRow("lock-lock") << Constraint::Lock << Constraint::Lock; } void TestPointerConstraints::testAlreadyConstrained() { // this test verifies that creating a pointer constraint for an already constrained surface triggers an error // first create a surface QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QFETCH(Constraint, firstConstraint); QScopedPointer confinedPointer; QScopedPointer lockedPointer; switch (firstConstraint) { case Constraint::Lock: lockedPointer.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot)); break; case Constraint::Confine: confinedPointer.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot)); break; default: Q_UNREACHABLE(); } QVERIFY(confinedPointer || lockedPointer); QSignalSpy errorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(errorSpy.isValid()); QFETCH(Constraint, secondConstraint); QScopedPointer confinedPointer2; QScopedPointer lockedPointer2; switch (secondConstraint) { case Constraint::Lock: lockedPointer2.reset(m_pointerConstraints->lockPointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot)); break; case Constraint::Confine: confinedPointer2.reset(m_pointerConstraints->confinePointer(surface.data(), m_pointer, nullptr, PointerConstraints::LifeTime::OneShot)); break; default: Q_UNREACHABLE(); } QVERIFY(errorSpy.wait()); QVERIFY(m_connection->hasError()); if (confinedPointer2) { confinedPointer2->destroy(); } if (lockedPointer2) { lockedPointer2->destroy(); } if (confinedPointer) { confinedPointer->destroy(); } if (lockedPointer) { lockedPointer->destroy(); } surface->destroy(); m_compositor->destroy(); m_pointerConstraints->destroy(); m_pointer->destroy(); m_seat->destroy(); m_queue->destroy(); } QTEST_GUILESS_MAIN(TestPointerConstraints) #include "test_pointer_constraints.moc" diff --git a/autotests/client/test_remote_access.cpp b/autotests/client/test_remote_access.cpp index f2f6be0..155fa83 100644 --- a/autotests/client/test_remote_access.cpp +++ b/autotests/client/test_remote_access.cpp @@ -1,488 +1,474 @@ -/******************************************************************** -Copyright 2016 Oleg Chernovskiy -Copyright 2018 Roman Gilg - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + SPDX-FileCopyrightText: 2018 Roman Gilg + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/remote_access.h" #include "../../src/client/registry.h" #include "../../src/client/output.h" // server #include "../../src/server/display.h" #include "../../src/server/output_interface.h" #include "../../src/server/remote_access_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(const BufferHandle *) Q_DECLARE_METATYPE(const RemoteBuffer *) Q_DECLARE_METATYPE(const void *) class RemoteAccessTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testSendReleaseSingle(); void testSendReleaseMultiple(); void testSendReleaseCrossScreen(); void testSendClientGone(); void testSendReceiveClientGone(); private: Display *m_display = nullptr; OutputInterface *m_outputInterface[2] = {nullptr}; RemoteAccessManagerInterface *m_remoteAccessInterface = nullptr; }; class MockupClient : public QObject { Q_OBJECT public: MockupClient(QObject *parent = nullptr); ~MockupClient(); void bindOutput(int index); ConnectionThread *connection = nullptr; QThread *thread = nullptr; EventQueue *queue = nullptr; Registry *registry = nullptr; RemoteAccessManager *remoteAccess = nullptr; Output *outputs[2] = {nullptr}; }; static const QString s_socketName = QStringLiteral("kwayland-test-remote-access-0"); MockupClient::MockupClient(QObject *parent) : QObject(parent) { // setup connection connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection->setSocketName(s_socketName); thread = new QThread(this); connection->moveToThread(thread); thread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); queue = new EventQueue(this); queue->setup(connection); registry = new Registry(this); QSignalSpy interfacesAnnouncedSpy(registry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry->setEventQueue(queue); registry->create(connection); QVERIFY(registry->isValid()); registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); remoteAccess = registry->createRemoteAccessManager( registry->interface(Registry::Interface::RemoteAccessManager).name, registry->interface(Registry::Interface::RemoteAccessManager).version, this); QVERIFY(remoteAccess->isValid()); connection->flush(); } MockupClient::~MockupClient() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(outputs[0]) CLEANUP(outputs[1]) CLEANUP(remoteAccess) CLEANUP(queue) CLEANUP(registry) if (thread) { if (connection) { connection->flush(); connection->deleteLater(); connection = nullptr; } thread->quit(); thread->wait(); delete thread; thread = nullptr; } #undef CLEANUP } void MockupClient::bindOutput(int index) { // client-bound output outputs[index] = registry->createOutput(registry->interfaces(Registry::Interface::Output)[index].name, registry->interfaces(Registry::Interface::Output)[index].version, this); QVERIFY(outputs[index]->isValid()); connection->flush(); } void RemoteAccessTest::init() { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); auto initOutputIface = [this](int i) { m_outputInterface[i] = m_display->createOutput(); m_outputInterface[i]->create(); }; initOutputIface(0); initOutputIface(1); m_remoteAccessInterface = m_display->createRemoteAccessManager(); m_remoteAccessInterface->create(); QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); } void RemoteAccessTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_outputInterface[0]) CLEANUP(m_outputInterface[1]) CLEANUP(m_remoteAccessInterface) CLEANUP(m_display) #undef CLEANUP } void RemoteAccessTest::testSendReleaseSingle() { // this test verifies that a buffer is sent to client and returned back // setup QVERIFY(!m_remoteAccessInterface->isBound()); auto *client = new MockupClient(this); client->bindOutput(0); m_display->dispatchEvents(); QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy.isValid()); BufferHandle *buf = new BufferHandle(); QTemporaryFile *tmpFile = new QTemporaryFile(this); tmpFile->open(); buf->setFd(tmpFile->handle()); buf->setSize(50, 50); buf->setFormat(100500); buf->setStride(7800); m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf); // receive buffer QVERIFY(bufferReadySpy.wait()); auto rbuf = bufferReadySpy.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy(rbuf, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy.isValid()); // wait for params QVERIFY(paramsObtainedSpy.wait()); // client fd is different, not subject to check QCOMPARE(rbuf->width(), 50u); QCOMPARE(rbuf->height(), 50u); QCOMPARE(rbuf->format(), 100500u); QCOMPARE(rbuf->stride(), 7800u); // release QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); delete rbuf; QVERIFY(bufferReleasedSpy.wait()); // cleanup delete buf; delete client; m_display->dispatchEvents(); QVERIFY(!m_remoteAccessInterface->isBound()); } void RemoteAccessTest::testSendReleaseMultiple() { // this test verifies that a buffer is sent to 2 clients and returned back // setup QVERIFY(!m_remoteAccessInterface->isBound()); auto *client1 = new MockupClient(this); auto *client2 = new MockupClient(this); client1->bindOutput(0); client2->bindOutput(0); m_display->dispatchEvents(); QVERIFY(m_remoteAccessInterface->isBound()); // now we have 2 clients QSignalSpy bufferReadySpy1(client1->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy1.isValid()); QSignalSpy bufferReadySpy2(client2->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy2.isValid()); BufferHandle *buf = new BufferHandle(); QTemporaryFile *tmpFile = new QTemporaryFile(this); tmpFile->open(); buf->setFd(tmpFile->handle()); buf->setSize(50, 50); buf->setFormat(100500); buf->setStride(7800); m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf); // wait for event loop QVERIFY(bufferReadySpy1.wait()); // receive buffer at client 1 QCOMPARE(bufferReadySpy1.size(), 1); auto rbuf1 = bufferReadySpy1.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy1(rbuf1, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy1.isValid()); if (bufferReadySpy2.size() == 0) { QVERIFY(bufferReadySpy2.wait()); } // receive buffer at client 2 QCOMPARE(bufferReadySpy2.size(), 1); auto rbuf2 = bufferReadySpy2.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy2(rbuf2, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy2.isValid()); // wait for event loop QVERIFY(paramsObtainedSpy1.size() == 1 || paramsObtainedSpy1.wait()); QCOMPARE(paramsObtainedSpy1.size(), 1); if (paramsObtainedSpy2.size() == 0) { QVERIFY(paramsObtainedSpy2.wait()); } QCOMPARE(paramsObtainedSpy2.size(), 1); // release QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); delete rbuf1; QVERIFY(!bufferReleasedSpy.wait(1000)); // one client released, second still holds buffer! delete rbuf2; QVERIFY(bufferReleasedSpy.wait()); // all clients released, buffer should be freed // cleanup delete buf; delete client1; delete client2; m_display->dispatchEvents(); QVERIFY(!m_remoteAccessInterface->isBound()); } void RemoteAccessTest::testSendReleaseCrossScreen() { // this test verifies that multiple buffers for multiple screens are sent to // multiple clients and returned back // setup QVERIFY(!m_remoteAccessInterface->isBound()); auto *client1 = new MockupClient(this); auto *client2 = new MockupClient(this); client1->bindOutput(1); client2->bindOutput(0); m_display->dispatchEvents(); QVERIFY(m_remoteAccessInterface->isBound()); // now we have 2 clients QSignalSpy bufferReadySpy1(client1->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy1.isValid()); QSignalSpy bufferReadySpy2(client2->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy2.isValid()); BufferHandle *buf1 = new BufferHandle(); QTemporaryFile *tmpFile1 = new QTemporaryFile(this); tmpFile1->open(); BufferHandle *buf2 = new BufferHandle(); QTemporaryFile *tmpFile2 = new QTemporaryFile(this); tmpFile2->open(); buf1->setFd(tmpFile1->handle()); buf1->setSize(50, 50); buf1->setFormat(100500); buf1->setStride(7800); buf2->setFd(tmpFile2->handle()); buf2->setSize(100, 100); buf2->setFormat(100500); buf2->setStride(7800); m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf1); m_remoteAccessInterface->sendBufferReady(m_outputInterface[1], buf2); // wait for event loop QVERIFY(bufferReadySpy1.wait()); if (bufferReadySpy2.size() == 0) { QVERIFY(bufferReadySpy2.wait()); } // receive buffer at client 1 QCOMPARE(bufferReadySpy1.size(), 1); auto rbuf1 = bufferReadySpy1.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy1(rbuf1, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy1.isValid()); // receive buffer at client 2 QCOMPARE(bufferReadySpy2.size(), 1); auto rbuf2 = bufferReadySpy2.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy2(rbuf2, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy2.isValid()); // wait for event loop if (paramsObtainedSpy1.isEmpty()) { QVERIFY(paramsObtainedSpy1.wait()); } if (paramsObtainedSpy2.isEmpty()) { QVERIFY(paramsObtainedSpy2.wait()); } QCOMPARE(paramsObtainedSpy1.size(), 1); QCOMPARE(paramsObtainedSpy2.size(), 1); // release QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); delete rbuf1; QVERIFY(bufferReleasedSpy.wait()); delete rbuf2; QVERIFY(bufferReleasedSpy.wait()); QCOMPARE(bufferReleasedSpy.size(), 2); // cleanup delete buf1; delete buf2; delete client1; delete client2; m_display->dispatchEvents(); QVERIFY(!m_remoteAccessInterface->isBound()); } void RemoteAccessTest::testSendClientGone() { // this test verifies that when buffer is sent and client is gone, server will release buffer correctly QVERIFY(!m_remoteAccessInterface->isBound()); auto *client = new MockupClient(this); client->bindOutput(0); m_display->dispatchEvents(); QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy.isValid()); BufferHandle *buf = new BufferHandle(); QTemporaryFile *tmpFile = new QTemporaryFile(this); tmpFile->open(); buf->setFd(tmpFile->handle()); buf->setSize(50, 50); buf->setFormat(100500); buf->setStride(7800); m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf); // release forcefully QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); delete client; QVERIFY(bufferReleasedSpy.wait()); // cleanup delete buf; m_display->dispatchEvents(); QVERIFY(!m_remoteAccessInterface->isBound()); } void RemoteAccessTest::testSendReceiveClientGone() { - // this test verifies that when buffer is sent, received and client is gone, + // this test verifies that when buffer is sent, received and client is gone, // both client and server will release buffer correctly QVERIFY(!m_remoteAccessInterface->isBound()); auto *client = new MockupClient(this); client->bindOutput(0); m_display->dispatchEvents(); QVERIFY(m_remoteAccessInterface->isBound()); // we have one client now QSignalSpy bufferReadySpy(client->remoteAccess, &RemoteAccessManager::bufferReady); QVERIFY(bufferReadySpy.isValid()); BufferHandle *buf = new BufferHandle(); QTemporaryFile *tmpFile = new QTemporaryFile(this); tmpFile->open(); buf->setFd(tmpFile->handle()); buf->setSize(50, 50); buf->setFormat(100500); buf->setStride(7800); m_remoteAccessInterface->sendBufferReady(m_outputInterface[0], buf); // receive buffer QVERIFY(bufferReadySpy.wait()); auto rbuf = bufferReadySpy.takeFirst()[1].value(); QSignalSpy paramsObtainedSpy(rbuf, &RemoteBuffer::parametersObtained); QVERIFY(paramsObtainedSpy.isValid()); // wait for params QVERIFY(paramsObtainedSpy.wait()); // client fd is different, not subject to check QCOMPARE(rbuf->width(), 50u); QCOMPARE(rbuf->height(), 50u); QCOMPARE(rbuf->format(), 100500u); QCOMPARE(rbuf->stride(), 7800u); // release forcefully QSignalSpy bufferReleasedSpy(m_remoteAccessInterface, &RemoteAccessManagerInterface::bufferReleased); QVERIFY(bufferReleasedSpy.isValid()); delete client; QVERIFY(bufferReleasedSpy.wait()); // cleanup delete buf; m_display->dispatchEvents(); QVERIFY(!m_remoteAccessInterface->isBound()); } QTEST_GUILESS_MAIN(RemoteAccessTest) #include "test_remote_access.moc" diff --git a/autotests/client/test_selection.cpp b/autotests/client/test_selection.cpp index ad92fc0..3610558 100644 --- a/autotests/client/test_selection.cpp +++ b/autotests/client/test_selection.cpp @@ -1,272 +1,258 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" // server #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class SelectionTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testClearOnEnter(); private: Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; SeatInterface *m_seatInterface = nullptr; DataDeviceManagerInterface *m_ddmInterface = nullptr; struct Connection { ConnectionThread *connection = nullptr; QThread *thread = nullptr; EventQueue *queue = nullptr; Compositor *compositor = nullptr; Seat *seat = nullptr; DataDeviceManager *ddm = nullptr; Keyboard *keyboard = nullptr; DataDevice *dataDevice = nullptr; }; bool setupConnection(Connection *c); void cleanupConnection(Connection *c); Connection m_client1; Connection m_client2; }; static const QString s_socketName = QStringLiteral("kwayland-test-selection-0"); void SelectionTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasKeyboard(true); m_seatInterface->create(); m_ddmInterface = m_display->createDataDeviceManager(m_display); m_ddmInterface->create(); // setup connection setupConnection(&m_client1); setupConnection(&m_client2); } bool SelectionTest::setupConnection(Connection* c) { c->connection = new ConnectionThread; QSignalSpy connectedSpy(c->connection, &ConnectionThread::connected); if (!connectedSpy.isValid()) { return false; } c->connection->setSocketName(s_socketName); c->thread = new QThread(this); c->connection->moveToThread(c->thread); c->thread->start(); c->connection->initConnection(); if (!connectedSpy.wait(500)) { return false; } c->queue = new EventQueue(this); c->queue->setup(c->connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); if (!interfacesAnnouncedSpy.isValid()) { return false; } registry.setEventQueue(c->queue); registry.create(c->connection); if (!registry.isValid()) { return false; } registry.setup(); if (!interfacesAnnouncedSpy.wait(500)) { return false; } c->compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); if (!c->compositor->isValid()) { return false; } c->ddm = registry.createDataDeviceManager(registry.interface(Registry::Interface::DataDeviceManager).name, registry.interface(Registry::Interface::DataDeviceManager).version, this); if (!c->ddm->isValid()) { return false; } c->seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); if (!c->seat->isValid()) { return false; } QSignalSpy keyboardSpy(c->seat, &Seat::hasKeyboardChanged); if (!keyboardSpy.isValid()) { return false; } if (!keyboardSpy.wait(500)) { return false; } if (!c->seat->hasKeyboard()) { return false; } c->keyboard = c->seat->createKeyboard(c->seat); if (!c->keyboard->isValid()) { return false; } c->dataDevice = c->ddm->getDataDevice(c->seat, this); if (!c->dataDevice->isValid()) { return false; } return true; } void SelectionTest::cleanup() { cleanupConnection(&m_client1); cleanupConnection(&m_client2); #define CLEANUP(variable) \ delete variable; \ variable = nullptr; CLEANUP(m_ddmInterface) CLEANUP(m_seatInterface) CLEANUP(m_compositorInterface) CLEANUP(m_display) #undef CLEANUP } void SelectionTest::cleanupConnection(Connection *c) { delete c->dataDevice; c->dataDevice = nullptr; delete c->keyboard; c->keyboard = nullptr; delete c->ddm; c->ddm = nullptr; delete c->seat; c->seat = nullptr; delete c->compositor; c->compositor = nullptr; delete c->queue; c->queue = nullptr; if (c->connection) { c->connection->deleteLater(); c->connection = nullptr; } if (c->thread) { c->thread->quit(); c->thread->wait(); delete c->thread; c->thread = nullptr; } } void SelectionTest::testClearOnEnter() { // this test verifies that the selection is cleared prior to keyboard enter if there is no current selection QSignalSpy selectionClearedClient1Spy(m_client1.dataDevice, &DataDevice::selectionCleared); QVERIFY(selectionClearedClient1Spy.isValid()); QSignalSpy keyboardEnteredClient1Spy(m_client1.keyboard, &Keyboard::entered); QVERIFY(keyboardEnteredClient1Spy.isValid()); // now create a Surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s1(m_client1.compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface1 = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface1); // pass this surface keyboard focus m_seatInterface->setFocusedKeyboardSurface(serverSurface1); // should get a clear QVERIFY(selectionClearedClient1Spy.wait()); // let's set a selection QScopedPointer dataSource(m_client1.ddm->createDataSource()); dataSource->offer(QStringLiteral("text/plain")); m_client1.dataDevice->setSelection(keyboardEnteredClient1Spy.first().first().value(), dataSource.data()); // now let's bring in client 2 QSignalSpy selectionOfferedClient2Spy(m_client2.dataDevice, &DataDevice::selectionOffered); QVERIFY(selectionOfferedClient2Spy.isValid()); QSignalSpy selectionClearedClient2Spy(m_client2.dataDevice, &DataDevice::selectionCleared); QVERIFY(selectionClearedClient2Spy.isValid()); QSignalSpy keyboardEnteredClient2Spy(m_client2.keyboard, &Keyboard::entered); QVERIFY(keyboardEnteredClient2Spy.isValid()); QScopedPointer s2(m_client2.compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface2 = surfaceCreatedSpy.last().first().value(); QVERIFY(serverSurface2); // entering that surface should give a selection offer m_seatInterface->setFocusedKeyboardSurface(serverSurface2); QVERIFY(selectionOfferedClient2Spy.wait()); QVERIFY(selectionClearedClient2Spy.isEmpty()); // set a data source but without offers QScopedPointer dataSource2(m_client2.ddm->createDataSource()); m_client2.dataDevice->setSelection(keyboardEnteredClient2Spy.first().first().value(), dataSource2.data()); QVERIFY(selectionOfferedClient2Spy.wait()); // and clear m_client2.dataDevice->clearSelection(keyboardEnteredClient2Spy.first().first().value()); QVERIFY(selectionClearedClient2Spy.wait()); // now pass focus to first surface m_seatInterface->setFocusedKeyboardSurface(serverSurface1); // we should get a clear QVERIFY(selectionClearedClient1Spy.wait()); } QTEST_GUILESS_MAIN(SelectionTest) #include "test_selection.moc" diff --git a/autotests/client/test_server_side_decoration.cpp b/autotests/client/test_server_side_decoration.cpp index 6a6f2a4..20c8d45 100644 --- a/autotests/client/test_server_side_decoration.cpp +++ b/autotests/client/test_server_side_decoration.cpp @@ -1,328 +1,314 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/server_decoration.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/server_decoration_interface.h" class TestServerSideDecoration : public QObject { Q_OBJECT public: explicit TestServerSideDecoration(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate_data(); void testCreate(); void testRequest_data(); void testRequest(); void testSurfaceDestroy(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::ServerSideDecorationManagerInterface *m_serverSideDecorationManagerInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::ServerSideDecorationManager *m_serverSideDecorationManager = nullptr; QThread *m_thread = nullptr; KWayland::Client::Registry *m_registry = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-server-side-decoration-0"); TestServerSideDecoration::TestServerSideDecoration(QObject *parent) : QObject(parent) { } void TestServerSideDecoration::init() { using namespace KWayland::Server; using namespace KWayland::Client; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy compositorSpy(m_registry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy serverSideDecoManagerSpy(m_registry, &Registry::serverSideDecorationManagerAnnounced); QVERIFY(serverSideDecoManagerSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_serverSideDecorationManagerInterface = m_display->createServerSideDecorationManager(m_display); m_serverSideDecorationManagerInterface->create(); QVERIFY(m_serverSideDecorationManagerInterface->isValid()); QVERIFY(serverSideDecoManagerSpy.wait()); m_serverSideDecorationManager = m_registry->createServerSideDecorationManager(serverSideDecoManagerSpy.first().first().value(), serverSideDecoManagerSpy.first().last().value(), this); } void TestServerSideDecoration::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_serverSideDecorationManager) { delete m_serverSideDecorationManager; m_serverSideDecorationManager = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestServerSideDecoration::testCreate_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("serverMode"); QTest::addColumn("clientMode"); QTest::newRow("none") << ServerSideDecorationManagerInterface::Mode::None << ServerSideDecoration::Mode::None; QTest::newRow("client") << ServerSideDecorationManagerInterface::Mode::Client << ServerSideDecoration::Mode::Client; QTest::newRow("server") << ServerSideDecorationManagerInterface::Mode::Server << ServerSideDecoration::Mode::Server; } void TestServerSideDecoration::testCreate() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(KWayland::Server::ServerSideDecorationManagerInterface::Mode, serverMode); m_serverSideDecorationManagerInterface->setDefaultMode(serverMode); QCOMPARE(m_serverSideDecorationManagerInterface->defaultMode(), serverMode); QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QSignalSpy decorationCreated(m_serverSideDecorationManagerInterface, &ServerSideDecorationManagerInterface::decorationCreated); QVERIFY(decorationCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(!ServerSideDecorationInterface::get(serverSurface)); // create server side deco QScopedPointer serverSideDecoration(m_serverSideDecorationManager->create(surface.data())); QCOMPARE(serverSideDecoration->mode(), ServerSideDecoration::Mode::None); QSignalSpy modeChangedSpy(serverSideDecoration.data(), &ServerSideDecoration::modeChanged); QVERIFY(modeChangedSpy.isValid()); QVERIFY(decorationCreated.wait()); auto serverDeco = decorationCreated.first().first().value(); QVERIFY(serverDeco); QCOMPARE(serverDeco, ServerSideDecorationInterface::get(serverSurface)); QCOMPARE(serverDeco->surface(), serverSurface); // after binding the client should get the default mode QVERIFY(modeChangedSpy.wait()); QCOMPARE(modeChangedSpy.count(), 1); QTEST(serverSideDecoration->mode(), "clientMode"); // and destroy QSignalSpy destroyedSpy(serverDeco, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); serverSideDecoration.reset(); QVERIFY(destroyedSpy.wait()); } void TestServerSideDecoration::testRequest_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("defaultMode"); QTest::addColumn("clientMode"); QTest::addColumn("clientRequestMode"); QTest::addColumn("serverRequestedMode"); const auto serverNone = ServerSideDecorationManagerInterface::Mode::None; const auto serverClient = ServerSideDecorationManagerInterface::Mode::Client; const auto serverServer = ServerSideDecorationManagerInterface::Mode::Server; const auto clientNone = ServerSideDecoration::Mode::None; const auto clientClient = ServerSideDecoration::Mode::Client; const auto clientServer = ServerSideDecoration::Mode::Server; QTest::newRow("none->none") << serverNone << clientNone << clientNone << serverNone; QTest::newRow("none->client") << serverNone << clientNone << clientClient << serverClient; QTest::newRow("none->server") << serverNone << clientNone << clientServer << serverServer; QTest::newRow("client->none") << serverClient << clientClient << clientNone << serverNone; QTest::newRow("client->client") << serverClient << clientClient << clientClient << serverClient; QTest::newRow("client->server") << serverClient << clientClient << clientServer << serverServer; QTest::newRow("server->none") << serverServer << clientServer << clientNone << serverNone; QTest::newRow("server->client") << serverServer << clientServer << clientClient << serverClient; QTest::newRow("server->server") << serverServer << clientServer << clientServer << serverServer; } void TestServerSideDecoration::testRequest() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(KWayland::Server::ServerSideDecorationManagerInterface::Mode, defaultMode); m_serverSideDecorationManagerInterface->setDefaultMode(defaultMode); QCOMPARE(m_serverSideDecorationManagerInterface->defaultMode(), defaultMode); QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QSignalSpy decorationCreated(m_serverSideDecorationManagerInterface, &ServerSideDecorationManagerInterface::decorationCreated); QVERIFY(decorationCreated.isValid()); // create server side deco QScopedPointer surface(m_compositor->createSurface()); QScopedPointer serverSideDecoration(m_serverSideDecorationManager->create(surface.data())); QCOMPARE(serverSideDecoration->mode(), ServerSideDecoration::Mode::None); QSignalSpy modeChangedSpy(serverSideDecoration.data(), &ServerSideDecoration::modeChanged); QVERIFY(modeChangedSpy.isValid()); QVERIFY(decorationCreated.wait()); auto serverDeco = decorationCreated.first().first().value(); QVERIFY(serverDeco); QSignalSpy modeSpy(serverDeco, &ServerSideDecorationInterface::modeRequested); QVERIFY(modeSpy.isValid()); // after binding the client should get the default mode QVERIFY(modeChangedSpy.wait()); QCOMPARE(modeChangedSpy.count(), 1); QTEST(serverSideDecoration->mode(), "clientMode"); // request a change QFETCH(ServerSideDecoration::Mode, clientRequestMode); serverSideDecoration->requestMode(clientRequestMode); // mode not yet changed QTEST(serverSideDecoration->mode(), "clientMode"); QVERIFY(modeSpy.wait()); QCOMPARE(modeSpy.count(), 1); QFETCH(ServerSideDecorationManagerInterface::Mode, serverRequestedMode); QCOMPARE(modeSpy.first().first().value(), serverRequestedMode); // mode not yet changed QCOMPARE(serverDeco->mode(), defaultMode); serverDeco->setMode(serverRequestedMode); QCOMPARE(serverDeco->mode(), serverRequestedMode); // should be sent to client QVERIFY(modeChangedSpy.wait()); QCOMPARE(modeChangedSpy.count(), 2); QCOMPARE(serverSideDecoration->mode(), clientRequestMode); } void TestServerSideDecoration::testSurfaceDestroy() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QSignalSpy decorationCreated(m_serverSideDecorationManagerInterface, &ServerSideDecorationManagerInterface::decorationCreated); QVERIFY(decorationCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QScopedPointer serverSideDecoration(m_serverSideDecorationManager->create(surface.data())); QCOMPARE(serverSideDecoration->mode(), ServerSideDecoration::Mode::None); QVERIFY(decorationCreated.wait()); auto serverDeco = decorationCreated.first().first().value(); QVERIFY(serverDeco); // destroy the parent surface QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); QSignalSpy decorationDestroyedSpy(serverDeco, &QObject::destroyed); QVERIFY(decorationDestroyedSpy.isValid()); surface.reset(); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(decorationDestroyedSpy.isEmpty()); // destroy the blur serverSideDecoration.reset(); QVERIFY(decorationDestroyedSpy.wait()); } QTEST_GUILESS_MAIN(TestServerSideDecoration) #include "test_server_side_decoration.moc" diff --git a/autotests/client/test_server_side_decoration_palette.cpp b/autotests/client/test_server_side_decoration_palette.cpp index fe46f25..5757648 100644 --- a/autotests/client/test_server_side_decoration_palette.cpp +++ b/autotests/client/test_server_side_decoration_palette.cpp @@ -1,191 +1,177 @@ -/******************************************************************** -Copyright 2017 David Edmundson -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/server_decoration_palette.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/server_decoration_palette_interface.h" using namespace KWayland::Client; class TestServerSideDecorationPalette : public QObject { Q_OBJECT public: explicit TestServerSideDecorationPalette(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreateAndSet(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::ServerSideDecorationPaletteManagerInterface *m_paletteManagerInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ServerSideDecorationPaletteManager *m_paletteManager; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-decopalette-0"); TestServerSideDecorationPalette::TestServerSideDecorationPalette(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestServerSideDecorationPalette::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy registrySpy(®istry, &Registry::serverSideDecorationPaletteManagerAnnounced); QVERIFY(registrySpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_paletteManagerInterface = m_display->createServerSideDecorationPaletteManager(m_display); m_paletteManagerInterface->create(); QVERIFY(m_paletteManagerInterface->isValid()); QVERIFY(registrySpy.wait()); m_paletteManager = registry.createServerSideDecorationPaletteManager(registrySpy.first().first().value(), registrySpy.first().last().value(), this); } void TestServerSideDecorationPalette::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_paletteManager) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_paletteManagerInterface) CLEANUP(m_display) #undef CLEANUP } void TestServerSideDecorationPalette::testCreateAndSet() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy paletteCreatedSpy(m_paletteManagerInterface, &KWayland::Server::ServerSideDecorationPaletteManagerInterface::paletteCreated); QVERIFY(!m_paletteManagerInterface->paletteForSurface(serverSurface)); auto palette = m_paletteManager->create(surface.data(), surface.data()); QVERIFY(paletteCreatedSpy.wait()); auto paletteInterface = paletteCreatedSpy.first().first().value(); QCOMPARE(m_paletteManagerInterface->paletteForSurface(serverSurface), paletteInterface); QCOMPARE(paletteInterface->palette(), QString()); QSignalSpy changedSpy(paletteInterface, &KWayland::Server::ServerSideDecorationPaletteInterface::paletteChanged); palette->setPalette("foobar"); QVERIFY(changedSpy.wait()); QCOMPARE(paletteInterface->palette(), QString("foobar")); // and destroy QSignalSpy destroyedSpy(paletteInterface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete palette; QVERIFY(destroyedSpy.wait()); QVERIFY(!m_paletteManagerInterface->paletteForSurface(serverSurface)); } QTEST_GUILESS_MAIN(TestServerSideDecorationPalette) #include "test_server_side_decoration_palette.moc" diff --git a/autotests/client/test_shadow.cpp b/autotests/client/test_shadow.cpp index c340cc2..21c169a 100644 --- a/autotests/client/test_shadow.cpp +++ b/autotests/client/test_shadow.cpp @@ -1,305 +1,291 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/shadow.h" #include "../../src/client/shm_pool.h" #include "../../src/client/surface.h" // server #include "../../src/server/buffer_interface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/shadow_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class ShadowTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testCreateShadow(); void testShadowElements(); void testSurfaceDestroy(); private: Display *m_display = nullptr; ConnectionThread *m_connection = nullptr; CompositorInterface *m_compositorInterface = nullptr; ShadowManagerInterface *m_shadowInterface = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; ShmPool *m_shm = nullptr; Compositor *m_compositor = nullptr; ShadowManager *m_shadow = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-shadow-0"); void ShadowTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_shadowInterface = m_display->createShadowManager(m_display); m_shadowInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version, this); QVERIFY(m_shm->isValid()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor->isValid()); m_shadow = registry.createShadowManager(registry.interface(Registry::Interface::Shadow).name, registry.interface(Registry::Interface::Shadow).version, this); QVERIFY(m_shadow->isValid()); } void ShadowTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_shm) CLEANUP(m_compositor) CLEANUP(m_shadow) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_shadowInterface) CLEANUP(m_display) #undef CLEANUP } void ShadowTest::testCreateShadow() { // this test verifies the basic shadow behavior, create for surface, commit it, etc. QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // a surface without anything should not have a Shadow QVERIFY(!serverSurface->shadow()); QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged); QVERIFY(shadowChangedSpy.isValid()); // let's create a shadow for the Surface QScopedPointer shadow(m_shadow->createShadow(surface.data())); // that should not have triggered the shadowChangedSpy) QVERIFY(!shadowChangedSpy.wait(100)); // now let's commit the surface, that should trigger the shadow changed surface->commit(Surface::CommitFlag::None); QVERIFY(shadowChangedSpy.wait()); QCOMPARE(shadowChangedSpy.count(), 1); // we didn't set anything on the shadow, so it should be all default values auto serverShadow = serverSurface->shadow(); QVERIFY(serverShadow); QCOMPARE(serverShadow->offset(), QMarginsF()); QVERIFY(!serverShadow->topLeft()); QVERIFY(!serverShadow->top()); QVERIFY(!serverShadow->topRight()); QVERIFY(!serverShadow->right()); QVERIFY(!serverShadow->bottomRight()); QVERIFY(!serverShadow->bottom()); QVERIFY(!serverShadow->bottomLeft()); QVERIFY(!serverShadow->left()); // now let's remove the shadow m_shadow->removeShadow(surface.data()); // just removing should not remove it yet, surface needs to be committed QVERIFY(!shadowChangedSpy.wait(100)); surface->commit(Surface::CommitFlag::None); QVERIFY(shadowChangedSpy.wait()); QCOMPARE(shadowChangedSpy.count(), 2); QVERIFY(!serverSurface->shadow()); } void ShadowTest::testShadowElements() { // this test verifies that all shadow elements are correctly passed to the server // first create surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged); QVERIFY(shadowChangedSpy.isValid()); // now create the shadow QScopedPointer shadow(m_shadow->createShadow(surface.data())); QImage topLeftImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); topLeftImage.fill(Qt::white); shadow->attachTopLeft(m_shm->createBuffer(topLeftImage)); QImage topImage(QSize(11, 11), QImage::Format_ARGB32_Premultiplied); topImage.fill(Qt::black); shadow->attachTop(m_shm->createBuffer(topImage)); QImage topRightImage(QSize(12, 12), QImage::Format_ARGB32_Premultiplied); topRightImage.fill(Qt::red); shadow->attachTopRight(m_shm->createBuffer(topRightImage)); QImage rightImage(QSize(13, 13), QImage::Format_ARGB32_Premultiplied); rightImage.fill(Qt::darkRed); shadow->attachRight(m_shm->createBuffer(rightImage)); QImage bottomRightImage(QSize(14, 14), QImage::Format_ARGB32_Premultiplied); bottomRightImage.fill(Qt::green); shadow->attachBottomRight(m_shm->createBuffer(bottomRightImage)); QImage bottomImage(QSize(15, 15), QImage::Format_ARGB32_Premultiplied); bottomImage.fill(Qt::darkGreen); shadow->attachBottom(m_shm->createBuffer(bottomImage)); QImage bottomLeftImage(QSize(16, 16), QImage::Format_ARGB32_Premultiplied); bottomLeftImage.fill(Qt::blue); shadow->attachBottomLeft(m_shm->createBuffer(bottomLeftImage)); QImage leftImage(QSize(17, 17), QImage::Format_ARGB32_Premultiplied); leftImage.fill(Qt::darkBlue); shadow->attachLeft(m_shm->createBuffer(leftImage)); shadow->setOffsets(QMarginsF(1, 2, 3, 4)); shadow->commit(); surface->commit(Surface::CommitFlag::None); QVERIFY(shadowChangedSpy.wait()); auto serverShadow = serverSurface->shadow(); QVERIFY(serverShadow); QCOMPARE(serverShadow->offset(), QMarginsF(1, 2, 3, 4)); QCOMPARE(serverShadow->topLeft()->data(), topLeftImage); QCOMPARE(serverShadow->top()->data(), topImage); QCOMPARE(serverShadow->topRight()->data(), topRightImage); QCOMPARE(serverShadow->right()->data(), rightImage); QCOMPARE(serverShadow->bottomRight()->data(), bottomRightImage); QCOMPARE(serverShadow->bottom()->data(), bottomImage); QCOMPARE(serverShadow->bottomLeft()->data(), bottomLeftImage); QCOMPARE(serverShadow->left()->data(), leftImage); // try to destroy the buffer // first attach one buffer shadow->attachTopLeft(m_shm->createBuffer(topLeftImage)); // create a destroyed signal QSignalSpy destroyedSpy(serverShadow->topLeft(), &BufferInterface::aboutToBeDestroyed); QVERIFY(destroyedSpy.isValid()); delete m_shm; m_shm = nullptr; QVERIFY(destroyedSpy.wait()); // now all buffers should be gone // TODO: does that need a signal? QVERIFY(!serverShadow->topLeft()); QVERIFY(!serverShadow->top()); QVERIFY(!serverShadow->topRight()); QVERIFY(!serverShadow->right()); QVERIFY(!serverShadow->bottomRight()); QVERIFY(!serverShadow->bottom()); QVERIFY(!serverShadow->bottomLeft()); QVERIFY(!serverShadow->left()); } void ShadowTest::testSurfaceDestroy() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged); QVERIFY(shadowChangedSpy.isValid()); QScopedPointer shadow(m_shadow->createShadow(surface.data())); shadow->commit(); surface->commit(Surface::CommitFlag::None); QVERIFY(shadowChangedSpy.wait()); auto serverShadow = serverSurface->shadow(); QVERIFY(serverShadow); // destroy the parent surface QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); QSignalSpy shadowDestroyedSpy(serverShadow.data(), &QObject::destroyed); QVERIFY(shadowDestroyedSpy.isValid()); surface.reset(); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(shadowDestroyedSpy.isEmpty()); // destroy the shadow shadow.reset(); QVERIFY(shadowDestroyedSpy.wait()); QCOMPARE(shadowDestroyedSpy.count(), 1); } QTEST_GUILESS_MAIN(ShadowTest) #include "test_shadow.moc" diff --git a/autotests/client/test_shm_pool.cpp b/autotests/client/test_shm_pool.cpp index c95562d..2aa1be9 100644 --- a/autotests/client/test_shm_pool.cpp +++ b/autotests/client/test_shm_pool.cpp @@ -1,248 +1,234 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/surface_interface.h" class TestShmPool : public QObject { Q_OBJECT public: explicit TestShmPool(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreateBufferNullImage(); void testCreateBufferNullSize(); void testCreateBufferInvalidSize(); void testCreateBufferFromImage(); void testCreateBufferFromImageWithAlpha(); void testCreateBufferFromData(); void testReuseBuffer(); void testDestroy(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shmPool; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-surface-0"); TestShmPool::TestShmPool(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_shmPool(nullptr) , m_thread(nullptr) { } void TestShmPool::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); KWayland::Client::Registry registry; QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); // here we need a shm pool m_display->createShm(); QVERIFY(shmSpy.wait()); m_shmPool = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); } void TestShmPool::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_shmPool) { delete m_shmPool; m_shmPool = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestShmPool::testCreateBufferNullImage() { QVERIFY(m_shmPool->isValid()); QImage img; QVERIFY(img.isNull()); QVERIFY(!m_shmPool->createBuffer(img)); } void TestShmPool::testCreateBufferNullSize() { QVERIFY(m_shmPool->isValid()); QSize size(0, 0); QVERIFY(size.isNull()); QVERIFY(!m_shmPool->createBuffer(size, 0, nullptr)); } void TestShmPool::testCreateBufferInvalidSize() { QVERIFY(m_shmPool->isValid()); QSize size; QVERIFY(!size.isValid()); QVERIFY(!m_shmPool->createBuffer(size, 0, nullptr)); } void TestShmPool::testCreateBufferFromImage() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_RGB32); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_RGB32); QCOMPARE(img2, img); } void TestShmPool::testCreateBufferFromImageWithAlpha() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(QColor(255,0,0,100)); //red with alpha QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testCreateBufferFromData() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img.size(), img.bytesPerLine(), img.constBits()).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testReuseBuffer() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); buffer->setReleased(true); buffer->setUsed(false); // same image should get the same buffer auto buffer2 = m_shmPool->createBuffer(img).toStrongRef(); QCOMPARE(buffer, buffer2); buffer2->setReleased(true); buffer2->setUsed(false); // image with different size should get us a new buffer auto buffer3 = m_shmPool->getBuffer(QSize(10, 10), 8); QVERIFY(buffer3 != buffer2); // image with a different format should get us a new buffer QImage img2(24, 24, QImage::Format_RGB32); img2.fill(Qt::black); QVERIFY(!img2.isNull()); auto buffer4 = m_shmPool->createBuffer(img2).toStrongRef(); QVERIFY(buffer4); QVERIFY(buffer4 != buffer2); QVERIFY(buffer4 != buffer3); } void TestShmPool::testDestroy() { using namespace KWayland::Client; connect(m_connection, &ConnectionThread::connectionDied, m_shmPool, &ShmPool::destroy); QVERIFY(m_shmPool->isValid()); // let's create one Buffer m_shmPool->getBuffer(QSize(10, 10), 8); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!m_shmPool->isValid()); // calling destroy again should not fail m_shmPool->destroy(); } QTEST_GUILESS_MAIN(TestShmPool) #include "test_shm_pool.moc" diff --git a/autotests/client/test_text_input.cpp b/autotests/client/test_text_input.cpp index 269610c..c81299f 100644 --- a/autotests/client/test_text_input.cpp +++ b/autotests/client/test_text_input.cpp @@ -1,936 +1,922 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // client #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/registry.h" #include "../../src/client/seat.h" #include "../../src/client/surface.h" #include "../../src/client/textinput.h" // server #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" #include "../../src/server/textinput_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; class TextInputTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testEnterLeave_data(); void testEnterLeave(); void testShowHidePanel_data(); void testShowHidePanel(); void testCursorRectangle_data(); void testCursorRectangle(); void testPreferredLanguage_data(); void testPreferredLanguage(); void testReset_data(); void testReset(); void testSurroundingText_data(); void testSurroundingText(); void testContentHints_data(); void testContentHints(); void testContentPurpose_data(); void testContentPurpose(); void testTextDirection_data(); void testTextDirection(); void testLanguage_data(); void testLanguage(); void testKeyEvent_data(); void testKeyEvent(); void testPreEdit_data(); void testPreEdit(); void testCommit_data(); void testCommit(); private: SurfaceInterface *waitForSurface(); TextInput *createTextInput(TextInputInterfaceVersion version); Display *m_display = nullptr; SeatInterface *m_seatInterface = nullptr; CompositorInterface *m_compositorInterface = nullptr; TextInputManagerInterface *m_textInputManagerV0Interface = nullptr; TextInputManagerInterface *m_textInputManagerV2Interface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; Seat *m_seat = nullptr; Keyboard *m_keyboard = nullptr; Compositor *m_compositor = nullptr; TextInputManager *m_textInputManagerV0 = nullptr; TextInputManager *m_textInputManagerV2 = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-text-input-0"); void TextInputTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_seatInterface = m_display->createSeat(); m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasTouch(true); m_seatInterface->create(); m_compositorInterface = m_display->createCompositor(); m_compositorInterface->create(); m_textInputManagerV0Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV0); m_textInputManagerV0Interface->create(); m_textInputManagerV2Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV2); m_textInputManagerV2Interface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); QVERIFY(m_seat->isValid()); QSignalSpy hasKeyboardSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(hasKeyboardSpy.isValid()); QVERIFY(hasKeyboardSpy.wait()); m_keyboard = m_seat->createKeyboard(this); QVERIFY(m_keyboard->isValid()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor->isValid()); m_textInputManagerV0 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV0).name, registry.interface(Registry::Interface::TextInputManagerUnstableV0).version, this); QVERIFY(m_textInputManagerV0->isValid()); m_textInputManagerV2 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV2).name, registry.interface(Registry::Interface::TextInputManagerUnstableV2).version, this); QVERIFY(m_textInputManagerV2->isValid()); } void TextInputTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_textInputManagerV0) CLEANUP(m_textInputManagerV2) CLEANUP(m_keyboard) CLEANUP(m_seat) CLEANUP(m_compositor) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_textInputManagerV0Interface) CLEANUP(m_textInputManagerV2Interface) CLEANUP(m_compositorInterface) CLEANUP(m_seatInterface) CLEANUP(m_display) #undef CLEANUP } SurfaceInterface *TextInputTest::waitForSurface() { QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); if (!surfaceCreatedSpy.isValid()) { return nullptr; } if (!surfaceCreatedSpy.wait(500)) { return nullptr; } if (surfaceCreatedSpy.count() != 1) { return nullptr; } return surfaceCreatedSpy.first().first().value(); } TextInput *TextInputTest::createTextInput(TextInputInterfaceVersion version) { switch (version) { case TextInputInterfaceVersion::UnstableV0: return m_textInputManagerV0->createTextInput(m_seat); case TextInputInterfaceVersion::UnstableV2: return m_textInputManagerV2->createTextInput(m_seat); default: Q_UNREACHABLE(); return nullptr; } } void TextInputTest::testEnterLeave_data() { QTest::addColumn("version"); QTest::addColumn("updatesDirectly"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0 << false; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2 << true; } void TextInputTest::testEnterLeave() { // this test verifies that enter leave are sent correctly QScopedPointer surface(m_compositor->createSurface()); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QVERIFY(!textInput.isNull()); QSignalSpy enteredSpy(textInput.data(), &TextInput::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(textInput.data(), &TextInput::left); QVERIFY(leftSpy.isValid()); QSignalSpy textInputChangedSpy(m_seatInterface, &SeatInterface::focusedTextInputChanged); QVERIFY(textInputChangedSpy.isValid()); // now let's try to enter it QVERIFY(!m_seatInterface->focusedTextInput()); QVERIFY(!m_seatInterface->focusedTextInputSurface()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedTextInputSurface(), serverSurface); // text input not yet set for the surface QFETCH(bool, updatesDirectly); QCOMPARE(bool(m_seatInterface->focusedTextInput()), updatesDirectly); QCOMPARE(textInputChangedSpy.isEmpty(), !updatesDirectly); textInput->enable(surface.data()); // this should trigger on server side if (!updatesDirectly) { QVERIFY(textInputChangedSpy.wait()); } QCOMPARE(textInputChangedSpy.count(), 1); auto serverTextInput = m_seatInterface->focusedTextInput(); QVERIFY(serverTextInput); QCOMPARE(serverTextInput->interfaceVersion(), version); QSignalSpy enabledChangedSpy(serverTextInput, &TextInputInterface::enabledChanged); QVERIFY(enabledChangedSpy.isValid()); if (updatesDirectly) { QVERIFY(enabledChangedSpy.wait()); enabledChangedSpy.clear(); } QCOMPARE(serverTextInput->surface().data(), serverSurface); QVERIFY(serverTextInput->isEnabled()); // and trigger an enter if (enteredSpy.isEmpty()) { QVERIFY(enteredSpy.wait()); } QCOMPARE(enteredSpy.count(), 1); QCOMPARE(textInput->enteredSurface(), surface.data()); // now trigger a leave m_seatInterface->setFocusedKeyboardSurface(nullptr); QCOMPARE(textInputChangedSpy.count(), 2); QVERIFY(!m_seatInterface->focusedTextInput()); QVERIFY(leftSpy.wait()); QVERIFY(!textInput->enteredSurface()); QVERIFY(serverTextInput->isEnabled()); // if we enter again we should directly get the text input as it's still activated m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(textInputChangedSpy.count(), 3); QVERIFY(m_seatInterface->focusedTextInput()); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(textInput->enteredSurface(), surface.data()); QVERIFY(serverTextInput->isEnabled()); // let's deactivate on client side textInput->disable(surface.data()); QVERIFY(enabledChangedSpy.wait()); QCOMPARE(enabledChangedSpy.count(), 1); QVERIFY(!serverTextInput->isEnabled()); // does not trigger a leave QCOMPARE(textInputChangedSpy.count(), 3); // should still be the same text input QCOMPARE(m_seatInterface->focusedTextInput(), serverTextInput); //reset textInput->enable(surface.data()); QVERIFY(enabledChangedSpy.wait()); //trigger an enter again and leave, but this //time we try sending an event after the surface is unbound //but not yet destroyed. It should work without errors QCOMPARE(textInput->enteredSurface(), surface.data()); connect(serverSurface, &Resource::unbound, [=]() { m_seatInterface->setFocusedKeyboardSurface(nullptr); }); //delete the client and wait for the server to catch up QSignalSpy unboundSpy(serverSurface, &QObject::destroyed); surface.reset(); QVERIFY(unboundSpy.wait()); QVERIFY(leftSpy.wait()); QVERIFY(!textInput->enteredSurface()); } void TextInputTest::testShowHidePanel_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testShowHidePanel() { // this test verifies that the requests for show/hide panel work // and that status is properly sent to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QSignalSpy showPanelRequestedSpy(ti, &TextInputInterface::requestShowInputPanel); QVERIFY(showPanelRequestedSpy.isValid()); QSignalSpy hidePanelRequestedSpy(ti, &TextInputInterface::requestHideInputPanel); QVERIFY(hidePanelRequestedSpy.isValid()); QSignalSpy inputPanelStateChangedSpy(textInput.data(), &TextInput::inputPanelStateChanged); QVERIFY(inputPanelStateChangedSpy.isValid()); QCOMPARE(textInput->isInputPanelVisible(), false); textInput->showInputPanel(); QVERIFY(showPanelRequestedSpy.wait()); ti->setInputPanelState(true, QRect(0, 0, 0, 0)); QVERIFY(inputPanelStateChangedSpy.wait()); QCOMPARE(textInput->isInputPanelVisible(), true); textInput->hideInputPanel(); QVERIFY(hidePanelRequestedSpy.wait()); ti->setInputPanelState(false, QRect(0, 0, 0, 0)); QVERIFY(inputPanelStateChangedSpy.wait()); QCOMPARE(textInput->isInputPanelVisible(), false); } void TextInputTest::testCursorRectangle_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testCursorRectangle() { // this test verifies that passing the cursor rectangle from client to server works // and that setting visibility state from server to client works QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->cursorRectangle(), QRect()); QSignalSpy cursorRectangleChangedSpy(ti, &TextInputInterface::cursorRectangleChanged); QVERIFY(cursorRectangleChangedSpy.isValid()); textInput->setCursorRectangle(QRect(10, 20, 30, 40)); QVERIFY(cursorRectangleChangedSpy.wait()); QCOMPARE(ti->cursorRectangle(), QRect(10, 20, 30, 40)); } void TextInputTest::testPreferredLanguage_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testPreferredLanguage() { // this test verifies that passing the preferred language from client to server works QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QVERIFY(ti->preferredLanguage().isEmpty()); QSignalSpy preferredLanguageChangedSpy(ti, &TextInputInterface::preferredLanguageChanged); QVERIFY(preferredLanguageChangedSpy.isValid()); textInput->setPreferredLanguage(QStringLiteral("foo")); QVERIFY(preferredLanguageChangedSpy.wait()); QCOMPARE(ti->preferredLanguage(), QStringLiteral("foo").toUtf8()); } void TextInputTest::testReset_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testReset() { // this test verifies that the reset request is properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QSignalSpy resetRequestedSpy(ti, &TextInputInterface::requestReset); QVERIFY(resetRequestedSpy.isValid()); textInput->reset(); QVERIFY(resetRequestedSpy.wait()); } void TextInputTest::testSurroundingText_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testSurroundingText() { // this test verifies that surrounding text is properly passed around QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QVERIFY(ti->surroundingText().isEmpty()); QCOMPARE(ti->surroundingTextCursorPosition(), 0); QCOMPARE(ti->surroundingTextSelectionAnchor(), 0); QSignalSpy surroundingTextChangedSpy(ti, &TextInputInterface::surroundingTextChanged); QVERIFY(surroundingTextChangedSpy.isValid()); textInput->setSurroundingText(QStringLiteral("100 €, 100 $"), 5, 6); QVERIFY(surroundingTextChangedSpy.wait()); QCOMPARE(ti->surroundingText(), QStringLiteral("100 €, 100 $").toUtf8()); QCOMPARE(ti->surroundingTextCursorPosition(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(',')); QCOMPARE(ti->surroundingTextSelectionAnchor(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(' ', ti->surroundingTextCursorPosition())); } void TextInputTest::testContentHints_data() { QTest::addColumn("version"); QTest::addColumn("clientHints"); QTest::addColumn("serverHints"); QTest::newRow("completion/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion); QTest::newRow("Correction/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection); QTest::newRow("Capitalization/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization); QTest::newRow("Lowercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::LowerCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase); QTest::newRow("Uppercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::UpperCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase); QTest::newRow("Titlecase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::TitleCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase); QTest::newRow("HiddenText/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::HiddenText) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText); QTest::newRow("SensitiveData/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData); QTest::newRow("Latin/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::Latin) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin); QTest::newRow("Multiline/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::MultiLine) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine); QTest::newRow("autos/v0") << TextInputInterfaceVersion::UnstableV0 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization); // all has combinations which don't make sense - what's both lowercase and uppercase? QTest::newRow("all/v0") << TextInputInterfaceVersion::UnstableV0 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin | TextInput::ContentHint::MultiLine) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine); // same for version 2 QTest::newRow("completion/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion); QTest::newRow("Correction/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection); QTest::newRow("Capitalization/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization); QTest::newRow("Lowercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::LowerCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase); QTest::newRow("Uppercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::UpperCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase); QTest::newRow("Titlecase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::TitleCase) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase); QTest::newRow("HiddenText/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::HiddenText) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText); QTest::newRow("SensitiveData/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData); QTest::newRow("Latin/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::Latin) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin); QTest::newRow("Multiline/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::MultiLine) << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine); QTest::newRow("autos/v2") << TextInputInterfaceVersion::UnstableV2 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization); // all has combinations which don't make sense - what's both lowercase and uppercase? QTest::newRow("all/v2") << TextInputInterfaceVersion::UnstableV2 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin | TextInput::ContentHint::MultiLine) << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine); } void TextInputTest::testContentHints() { // this test verifies that content hints are properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints()); QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged); QVERIFY(contentTypeChangedSpy.isValid()); QFETCH(TextInput::ContentHints, clientHints); textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QTEST(ti->contentHints(), "serverHints"); // setting to same should not trigger an update textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal); QVERIFY(!contentTypeChangedSpy.wait(100)); // unsetting should work textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints()); } void TextInputTest::testContentPurpose_data() { QTest::addColumn("version"); QTest::addColumn("clientPurpose"); QTest::addColumn("serverPurpose"); QTest::newRow("Alpha/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha; QTest::newRow("Digits/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits; QTest::newRow("Number/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number; QTest::newRow("Phone/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone; QTest::newRow("Url/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url; QTest::newRow("Email/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email; QTest::newRow("Name/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name; QTest::newRow("Password/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Password << TextInputInterface::ContentPurpose::Password; QTest::newRow("Date/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date; QTest::newRow("Time/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time; QTest::newRow("Datetime/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::DateTime << TextInputInterface::ContentPurpose::DateTime; QTest::newRow("Terminal/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Terminal << TextInputInterface::ContentPurpose::Terminal; QTest::newRow("Alpha/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha; QTest::newRow("Digits/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits; QTest::newRow("Number/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number; QTest::newRow("Phone/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone; QTest::newRow("Url/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url; QTest::newRow("Email/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email; QTest::newRow("Name/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name; QTest::newRow("Password/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Password << TextInputInterface::ContentPurpose::Password; QTest::newRow("Date/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date; QTest::newRow("Time/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time; QTest::newRow("Datetime/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::DateTime << TextInputInterface::ContentPurpose::DateTime; QTest::newRow("Terminal/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Terminal << TextInputInterface::ContentPurpose::Terminal; } void TextInputTest::testContentPurpose() { // this test verifies that content purpose are properly passed from client to server QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal); QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged); QVERIFY(contentTypeChangedSpy.isValid()); QFETCH(TextInput::ContentPurpose, clientPurpose); textInput->setContentType(TextInput::ContentHints(), clientPurpose); QVERIFY(contentTypeChangedSpy.wait()); QTEST(ti->contentPurpose(), "serverPurpose"); // setting to same should not trigger an update textInput->setContentType(TextInput::ContentHints(), clientPurpose); QVERIFY(!contentTypeChangedSpy.wait(100)); // unsetting should work textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal); QVERIFY(contentTypeChangedSpy.wait()); QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal); } void TextInputTest::testTextDirection_data() { QTest::addColumn("version"); QTest::addColumn("textDirection"); QTest::newRow("ltr/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::LeftToRight; QTest::newRow("rtl/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::RightToLeft; QTest::newRow("ltr/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::LeftToRight; QTest::newRow("rtl/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::RightToLeft; } void TextInputTest::testTextDirection() { // this test verifies that the text direction is sent from server to client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // default should be auto QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // let's send the new text direction QSignalSpy textDirectionChangedSpy(textInput.data(), &TextInput::textDirectionChanged); QVERIFY(textDirectionChangedSpy.isValid()); QFETCH(Qt::LayoutDirection, textDirection); ti->setTextDirection(textDirection); QVERIFY(textDirectionChangedSpy.wait()); QCOMPARE(textInput->textDirection(), textDirection); // setting again should not change ti->setTextDirection(textDirection); QVERIFY(!textDirectionChangedSpy.wait(100)); // setting back to auto ti->setTextDirection(Qt::LayoutDirectionAuto); QVERIFY(textDirectionChangedSpy.wait()); QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto); } void TextInputTest::testLanguage_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testLanguage() { // this test verifies that language is sent from server to client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // default should be empty QVERIFY(textInput->language().isEmpty()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // let's send the new language QSignalSpy langugageChangedSpy(textInput.data(), &TextInput::languageChanged); QVERIFY(langugageChangedSpy.isValid()); ti->setLanguage(QByteArrayLiteral("foo")); QVERIFY(langugageChangedSpy.wait()); QCOMPARE(textInput->language(), QByteArrayLiteral("foo")); // setting to same should not trigger ti->setLanguage(QByteArrayLiteral("foo")); QVERIFY(!langugageChangedSpy.wait(100)); // but to something else should trigger again ti->setLanguage(QByteArrayLiteral("bar")); QVERIFY(langugageChangedSpy.wait()); QCOMPARE(textInput->language(), QByteArrayLiteral("bar")); } void TextInputTest::testKeyEvent_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testKeyEvent() { qRegisterMetaType(); qRegisterMetaType(); // this test verifies that key events are properly sent to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // TODO: test modifiers QSignalSpy keyEventSpy(textInput.data(), &TextInput::keyEvent); QVERIFY(keyEventSpy.isValid()); m_seatInterface->setTimestamp(100); ti->keysymPressed(2); QVERIFY(keyEventSpy.wait()); QCOMPARE(keyEventSpy.count(), 1); QCOMPARE(keyEventSpy.last().at(0).value(), 2u); QCOMPARE(keyEventSpy.last().at(1).value(), TextInput::KeyState::Pressed); QCOMPARE(keyEventSpy.last().at(2).value(), Qt::KeyboardModifiers()); QCOMPARE(keyEventSpy.last().at(3).value(), 100u); m_seatInterface->setTimestamp(101); ti->keysymReleased(2); QVERIFY(keyEventSpy.wait()); QCOMPARE(keyEventSpy.count(), 2); QCOMPARE(keyEventSpy.last().at(0).value(), 2u); QCOMPARE(keyEventSpy.last().at(1).value(), TextInput::KeyState::Released); QCOMPARE(keyEventSpy.last().at(2).value(), Qt::KeyboardModifiers()); QCOMPARE(keyEventSpy.last().at(3).value(), 101u); } void TextInputTest::testPreEdit_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testPreEdit() { // this test verifies that pre-edit is correctly passed to the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // verify default values QVERIFY(textInput->composingText().isEmpty()); QVERIFY(textInput->composingFallbackText().isEmpty()); QCOMPARE(textInput->composingTextCursorPosition(), 0); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // now let's pass through some pre-edit events QSignalSpy composingTextChangedSpy(textInput.data(), &TextInput::composingTextChanged); QVERIFY(composingTextChangedSpy.isValid()); ti->setPreEditCursor(1); ti->preEdit(QByteArrayLiteral("foo"), QByteArrayLiteral("bar")); QVERIFY(composingTextChangedSpy.wait()); QCOMPARE(composingTextChangedSpy.count(), 1); QCOMPARE(textInput->composingText(), QByteArrayLiteral("foo")); QCOMPARE(textInput->composingFallbackText(), QByteArrayLiteral("bar")); QCOMPARE(textInput->composingTextCursorPosition(), 1); // when no pre edit cursor is sent, it's at end of text ti->preEdit(QByteArrayLiteral("foobar"), QByteArray()); QVERIFY(composingTextChangedSpy.wait()); QCOMPARE(composingTextChangedSpy.count(), 2); QCOMPARE(textInput->composingText(), QByteArrayLiteral("foobar")); QCOMPARE(textInput->composingFallbackText(), QByteArray()); QCOMPARE(textInput->composingTextCursorPosition(), 6); } void TextInputTest::testCommit_data() { QTest::addColumn("version"); QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0; QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2; } void TextInputTest::testCommit() { // this test verifies that the commit is handled correctly by the client QScopedPointer surface(m_compositor->createSurface()); auto serverSurface = waitForSurface(); QVERIFY(serverSurface); QFETCH(TextInputInterfaceVersion, version); QScopedPointer textInput(createTextInput(version)); QVERIFY(!textInput.isNull()); // verify default values QCOMPARE(textInput->commitText(), QByteArray()); QCOMPARE(textInput->cursorPosition(), 0); QCOMPARE(textInput->anchorPosition(), 0); QCOMPARE(textInput->deleteSurroundingText().beforeLength, 0u); QCOMPARE(textInput->deleteSurroundingText().afterLength, 0u); textInput->enable(surface.data()); m_connection->flush(); m_display->dispatchEvents(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); auto ti = m_seatInterface->focusedTextInput(); QVERIFY(ti); // now let's commit QSignalSpy committedSpy(textInput.data(), &TextInput::committed); QVERIFY(committedSpy.isValid()); ti->setCursorPosition(3, 4); ti->deleteSurroundingText(2, 1); ti->commit(QByteArrayLiteral("foo")); QVERIFY(committedSpy.wait()); QCOMPARE(textInput->commitText(), QByteArrayLiteral("foo")); QCOMPARE(textInput->cursorPosition(), 3); QCOMPARE(textInput->anchorPosition(), 4); QCOMPARE(textInput->deleteSurroundingText().beforeLength, 2u); QCOMPARE(textInput->deleteSurroundingText().afterLength, 1u); } QTEST_GUILESS_MAIN(TextInputTest) #include "test_text_input.moc" diff --git a/autotests/client/test_wayland_appmenu.cpp b/autotests/client/test_wayland_appmenu.cpp index 820501d..bac5c45 100644 --- a/autotests/client/test_wayland_appmenu.cpp +++ b/autotests/client/test_wayland_appmenu.cpp @@ -1,196 +1,182 @@ -/******************************************************************** -Copyright 2017 David Edmundson -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/appmenu.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/appmenu_interface.h" using namespace KWayland::Client; Q_DECLARE_METATYPE(KWayland::Server::AppMenuInterface::InterfaceAddress) class TestAppmenu : public QObject { Q_OBJECT public: explicit TestAppmenu(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreateAndSet(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::AppMenuManagerInterface *m_appmenuManagerInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::AppMenuManager *m_appmenuManager; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-appmenu-0"); TestAppmenu::TestAppmenu(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestAppmenu::init() { using namespace KWayland::Server; qRegisterMetaType(); delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy appmenuSpy(®istry, &Registry::appMenuAnnounced); QVERIFY(appmenuSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_appmenuManagerInterface = m_display->createAppMenuManagerInterface(m_display); m_appmenuManagerInterface->create(); QVERIFY(m_appmenuManagerInterface->isValid()); QVERIFY(appmenuSpy.wait()); m_appmenuManager = registry.createAppMenuManager(appmenuSpy.first().first().value(), appmenuSpy.first().last().value(), this); } void TestAppmenu::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_appmenuManager) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_appmenuManagerInterface) CLEANUP(m_display) #undef CLEANUP } void TestAppmenu::testCreateAndSet() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy appMenuCreated(m_appmenuManagerInterface, &KWayland::Server::AppMenuManagerInterface::appMenuCreated); QVERIFY(!m_appmenuManagerInterface->appMenuForSurface(serverSurface)); auto appmenu = m_appmenuManager->create(surface.data(), surface.data()); QVERIFY(appMenuCreated.wait()); auto appMenuInterface = appMenuCreated.first().first().value(); QCOMPARE(m_appmenuManagerInterface->appMenuForSurface(serverSurface), appMenuInterface); QCOMPARE(appMenuInterface->address().serviceName, QString()); QCOMPARE(appMenuInterface->address().objectPath, QString()); QSignalSpy appMenuChangedSpy(appMenuInterface, &KWayland::Server::AppMenuInterface::addressChanged); appmenu->setAddress("net.somename", "/test/path"); QVERIFY(appMenuChangedSpy.wait()); QCOMPARE(appMenuInterface->address().serviceName, QString("net.somename")); QCOMPARE(appMenuInterface->address().objectPath, QString("/test/path")); // and destroy QSignalSpy destroyedSpy(appMenuInterface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete appmenu; QVERIFY(destroyedSpy.wait()); QVERIFY(!m_appmenuManagerInterface->appMenuForSurface(serverSurface)); } QTEST_GUILESS_MAIN(TestAppmenu) #include "test_wayland_appmenu.moc" diff --git a/autotests/client/test_wayland_blur.cpp b/autotests/client/test_wayland_blur.cpp index eb6b6a9..f6cd202 100644 --- a/autotests/client/test_wayland_blur.cpp +++ b/autotests/client/test_wayland_blur.cpp @@ -1,215 +1,201 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/blur.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/blur_interface.h" using namespace KWayland::Client; class TestBlur : public QObject { Q_OBJECT public: explicit TestBlur(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testSurfaceDestroy(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::BlurManagerInterface *m_blurManagerInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::BlurManager *m_blurManager; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-blur-0"); TestBlur::TestBlur(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestBlur::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy blurSpy(®istry, &Registry::blurAnnounced); QVERIFY(blurSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_blurManagerInterface = m_display->createBlurManager(m_display); m_blurManagerInterface->create(); QVERIFY(m_blurManagerInterface->isValid()); QVERIFY(blurSpy.wait()); m_blurManager = registry.createBlurManager(blurSpy.first().first().value(), blurSpy.first().last().value(), this); } void TestBlur::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_blurManager) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_blurManagerInterface) CLEANUP(m_display) #undef CLEANUP } void TestBlur::testCreate() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy blurChanged(serverSurface, SIGNAL(blurChanged())); auto blur = m_blurManager->createBlur(surface.data(), surface.data()); blur->setRegion(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); blur->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(blurChanged.wait()); QCOMPARE(serverSurface->blur()->region(), QRegion(0, 0, 10, 20)); // and destroy QSignalSpy destroyedSpy(serverSurface->blur().data(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete blur; QVERIFY(destroyedSpy.wait()); } void TestBlur::testSurfaceDestroy() { QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWayland::Server::CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy blurChanged(serverSurface, &KWayland::Server::SurfaceInterface::blurChanged); QVERIFY(blurChanged.isValid()); QScopedPointer blur(m_blurManager->createBlur(surface.data())); blur->setRegion(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); blur->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(blurChanged.wait()); QCOMPARE(serverSurface->blur()->region(), QRegion(0, 0, 10, 20)); // destroy the parent surface QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); QSignalSpy blurDestroyedSpy(serverSurface->blur().data(), &QObject::destroyed); QVERIFY(blurDestroyedSpy.isValid()); surface.reset(); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(blurDestroyedSpy.isEmpty()); // destroy the blur blur.reset(); QVERIFY(blurDestroyedSpy.wait()); } QTEST_GUILESS_MAIN(TestBlur) #include "test_wayland_blur.moc" diff --git a/autotests/client/test_wayland_connection_thread.cpp b/autotests/client/test_wayland_connection_thread.cpp index fa03ac6..c4b9588 100644 --- a/autotests/client/test_wayland_connection_thread.cpp +++ b/autotests/client/test_wayland_connection_thread.cpp @@ -1,301 +1,287 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" // Wayland #include // system #include #include #include class TestWaylandConnectionThread : public QObject { Q_OBJECT public: explicit TestWaylandConnectionThread(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testInitConnectionNoThread(); void testConnectionFailure(); void testConnectionDieing(); void testConnectionThread(); void testConnectFd(); void testConnectFdNoSocketName(); private: KWayland::Server::Display *m_display; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-connection-0"); TestWaylandConnectionThread::TestWaylandConnectionThread(QObject *parent) : QObject(parent) , m_display(nullptr) { } void TestWaylandConnectionThread::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); } void TestWaylandConnectionThread::cleanup() { delete m_display; m_display = nullptr; } void TestWaylandConnectionThread::testInitConnectionNoThread() { QVERIFY(KWayland::Client::ConnectionThread::connections().isEmpty()); QScopedPointer connection(new KWayland::Client::ConnectionThread); QVERIFY(KWayland::Client::ConnectionThread::connections().contains(connection.data())); QCOMPARE(connection->socketName(), QStringLiteral("wayland-0")); connection->setSocketName(s_socketName); QCOMPARE(connection->socketName(), s_socketName); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); connection->initConnection(); QVERIFY(connectedSpy.wait()); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(failedSpy.count(), 0); QVERIFY(connection->display()); connection.reset(); QVERIFY(KWayland::Client::ConnectionThread::connections().isEmpty()); } void TestWaylandConnectionThread::testConnectionFailure() { QScopedPointer connection(new KWayland::Client::ConnectionThread); connection->setSocketName(QStringLiteral("kwin-test-socket-does-not-exist")); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); QSignalSpy failedSpy(connection.data(), SIGNAL(failed())); connection->initConnection(); QVERIFY(failedSpy.wait()); QCOMPARE(connectedSpy.count(), 0); QCOMPARE(failedSpy.count(), 1); QVERIFY(!connection->display()); } static void registryHandleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { Q_UNUSED(data) Q_UNUSED(registry) Q_UNUSED(name) Q_UNUSED(interface) Q_UNUSED(version) } static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) { Q_UNUSED(data) Q_UNUSED(registry) Q_UNUSED(name) } static const struct wl_registry_listener s_registryListener = { registryHandleGlobal, registryHandleGlobalRemove }; void TestWaylandConnectionThread::testConnectionThread() { KWayland::Client::ConnectionThread *connection = new KWayland::Client::ConnectionThread; connection->setSocketName(s_socketName); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); QSignalSpy failedSpy(connection, SIGNAL(failed())); QVERIFY(failedSpy.isValid()); connection->initConnection(); QVERIFY(connectedSpy.wait()); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(failedSpy.count(), 0); QVERIFY(connection->display()); // now we have the connection ready, let's get some events QSignalSpy eventsSpy(connection, SIGNAL(eventsRead())); QVERIFY(eventsSpy.isValid()); wl_display *display = connection->display(); QScopedPointer queue(new KWayland::Client::EventQueue); queue->setup(display); QVERIFY(queue->isValid()); connect(connection, &KWayland::Client::ConnectionThread::eventsRead, queue.data(), &KWayland::Client::EventQueue::dispatch, Qt::QueuedConnection); wl_registry *registry = wl_display_get_registry(display); wl_proxy_set_queue((wl_proxy*)registry, *(queue.data())); wl_registry_add_listener(registry, &s_registryListener, this); wl_display_flush(display); if (eventsSpy.isEmpty()) { QVERIFY(eventsSpy.wait()); } QVERIFY(!eventsSpy.isEmpty()); wl_registry_destroy(registry); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; } void TestWaylandConnectionThread::testConnectionDieing() { QScopedPointer connection(new KWayland::Client::ConnectionThread); QSignalSpy connectedSpy(connection.data(), SIGNAL(connected())); connection->setSocketName(s_socketName); connection->initConnection(); QVERIFY(connectedSpy.wait()); QVERIFY(connection->display()); QSignalSpy diedSpy(connection.data(), SIGNAL(connectionDied())); m_display->terminate(); QVERIFY(!m_display->isRunning()); QVERIFY(diedSpy.wait()); QCOMPARE(diedSpy.count(), 1); QVERIFY(!connection->display()); connectedSpy.clear(); QVERIFY(connectedSpy.isEmpty()); // restarts the server m_display->start(); QVERIFY(m_display->isRunning()); if (connectedSpy.count() == 0) { QVERIFY(connectedSpy.wait()); } QCOMPARE(connectedSpy.count(), 1); } void TestWaylandConnectionThread::testConnectFd() { using namespace KWayland::Client; using namespace KWayland::Server; int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto c = m_display->createClient(sv[0]); QVERIFY(c); QSignalSpy disconnectedSpy(c, &ClientConnection::disconnected); QVERIFY(disconnectedSpy.isValid()); ConnectionThread *connection = new ConnectionThread; QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); connection->setSocketFd(sv[1]); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); // create the Registry QScopedPointer registry(new Registry); QSignalSpy announcedSpy(registry.data(), SIGNAL(interfacesAnnounced())); QVERIFY(announcedSpy.isValid()); registry->create(connection); QScopedPointer queue(new EventQueue); queue->setup(connection); registry->setEventQueue(queue.data()); registry->setup(); QVERIFY(announcedSpy.wait()); registry.reset(); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; c->destroy(); QCOMPARE(disconnectedSpy.count(), 1); } void TestWaylandConnectionThread::testConnectFdNoSocketName() { delete m_display; m_display = nullptr; using namespace KWayland::Client; using namespace KWayland::Server; Display display; display.start(Display::StartMode::ConnectClientsOnly); QVERIFY(display.isRunning()); int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); QVERIFY(display.createClient(sv[0])); ConnectionThread *connection = new ConnectionThread; QSignalSpy connectedSpy(connection, SIGNAL(connected())); QVERIFY(connectedSpy.isValid()); connection->setSocketFd(sv[1]); QThread *connectionThread = new QThread(this); connection->moveToThread(connectionThread); connectionThread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); // create the Registry QScopedPointer registry(new Registry); QSignalSpy announcedSpy(registry.data(), SIGNAL(interfacesAnnounced())); QVERIFY(announcedSpy.isValid()); registry->create(connection); QScopedPointer queue(new EventQueue); queue->setup(connection); registry->setEventQueue(queue.data()); registry->setup(); QVERIFY(announcedSpy.wait()); registry.reset(); queue.reset(); connection->deleteLater(); connectionThread->quit(); connectionThread->wait(); delete connectionThread; } QTEST_GUILESS_MAIN(TestWaylandConnectionThread) #include "test_wayland_connection_thread.moc" diff --git a/autotests/client/test_wayland_contrast.cpp b/autotests/client/test_wayland_contrast.cpp index ecf7f67..90ccd9a 100644 --- a/autotests/client/test_wayland_contrast.cpp +++ b/autotests/client/test_wayland_contrast.cpp @@ -1,225 +1,211 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/contrast.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/contrast_interface.h" #include using namespace KWayland::Client; class TestContrast : public QObject { Q_OBJECT public: explicit TestContrast(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testSurfaceDestroy(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::ContrastManagerInterface *m_contrastManagerInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ContrastManager *m_contrastManager; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-contrast-0"); TestContrast::TestContrast(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestContrast::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy contrastSpy(®istry, &Registry::contrastAnnounced); QVERIFY(contrastSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_contrastManagerInterface = m_display->createContrastManager(m_display); m_contrastManagerInterface->create(); QVERIFY(m_contrastManagerInterface->isValid()); QVERIFY(contrastSpy.wait()); m_contrastManager = registry.createContrastManager(contrastSpy.first().first().value(), contrastSpy.first().last().value(), this); } void TestContrast::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_contrastManager) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_contrastManagerInterface) CLEANUP(m_display) #undef CLEANUP } void TestContrast::testCreate() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy contrastChanged(serverSurface, SIGNAL(contrastChanged())); auto contrast = m_contrastManager->createContrast(surface.data(), surface.data()); contrast->setRegion(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); contrast->setContrast(0.2); contrast->setIntensity(2.0); contrast->setSaturation(1.7); contrast->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(contrastChanged.wait()); QCOMPARE(serverSurface->contrast()->region(), QRegion(0, 0, 10, 20)); QCOMPARE(wl_fixed_from_double(serverSurface->contrast()->contrast()), wl_fixed_from_double(0.2)); QCOMPARE(wl_fixed_from_double(serverSurface->contrast()->intensity()), wl_fixed_from_double(2.0)); QCOMPARE(wl_fixed_from_double(serverSurface->contrast()->saturation()), wl_fixed_from_double(1.7)); // and destroy QSignalSpy destroyedSpy(serverSurface->contrast().data(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete contrast; QVERIFY(destroyedSpy.wait()); } void TestContrast::testSurfaceDestroy() { QSignalSpy serverSurfaceCreated(m_compositorInterface, &KWayland::Server::CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy contrastChanged(serverSurface, &KWayland::Server::SurfaceInterface::contrastChanged); QVERIFY(contrastChanged.isValid()); QScopedPointer contrast(m_contrastManager->createContrast(surface.data())); contrast->setRegion(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); contrast->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(contrastChanged.wait()); QCOMPARE(serverSurface->contrast()->region(), QRegion(0, 0, 10, 20)); // destroy the parent surface QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); QSignalSpy contrastDestroyedSpy(serverSurface->contrast().data(), &QObject::destroyed); QVERIFY(contrastDestroyedSpy.isValid()); surface.reset(); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(contrastDestroyedSpy.isEmpty()); // destroy the blur contrast.reset(); QVERIFY(contrastDestroyedSpy.wait()); } QTEST_GUILESS_MAIN(TestContrast) #include "test_wayland_contrast.moc" diff --git a/autotests/client/test_wayland_filter.cpp b/autotests/client/test_wayland_filter.cpp index 2b7d450..c1fa456 100644 --- a/autotests/client/test_wayland_filter.cpp +++ b/autotests/client/test_wayland_filter.cpp @@ -1,168 +1,154 @@ -/******************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/blur.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/blur_interface.h" #include "../../src/server/filtered_display.h" #include using namespace KWayland::Client; class TestDisplay; class TestFilter : public QObject { Q_OBJECT public: explicit TestFilter(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testFilter_data(); void testFilter(); private: TestDisplay *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::BlurManagerInterface *m_blurManagerInterface; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-blur-0"); //The following non-realistic class allows only clients in the m_allowedClients list to access the blur interface //all other interfaces are allowed class TestDisplay : public KWayland::Server::FilteredDisplay { public: TestDisplay(QObject *parent); bool allowInterface(KWayland::Server::ClientConnection * client, const QByteArray & interfaceName) override; QList m_allowedClients; }; TestDisplay::TestDisplay(QObject *parent): KWayland::Server::FilteredDisplay(parent) {} bool TestDisplay::allowInterface(KWayland::Server::ClientConnection* client, const QByteArray& interfaceName) { if (interfaceName == "org_kde_kwin_blur_manager") { return m_allowedClients.contains(*client); } return true; } TestFilter::TestFilter(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) {} void TestFilter::init() { using namespace KWayland::Server; delete m_display; m_display = new TestDisplay(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_blurManagerInterface = m_display->createBlurManager(m_display); m_blurManagerInterface->create(); QVERIFY(m_blurManagerInterface->isValid()); } void TestFilter::cleanup() { } void TestFilter::testFilter_data() { QTest::addColumn("accessAllowed"); QTest::newRow("granted") << true; QTest::newRow("denied") << false; } void TestFilter::testFilter() { QFETCH(bool, accessAllowed); // setup connection QScopedPointer connection(new KWayland::Client::ConnectionThread()); QSignalSpy connectedSpy(connection.data(), &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection->setSocketName(s_socketName); QScopedPointer thread(new QThread(this)); connection->moveToThread(thread.data()); thread->start(); connection->initConnection(); QVERIFY(connectedSpy.wait()); //use low level API as Server::Display::connections only lists connections which have //been previous fetched via getConnection() if (accessAllowed) { wl_client *clientConnection; wl_client_for_each(clientConnection, wl_display_get_client_list(*m_display)) { m_display->m_allowedClients << clientConnection; } } KWayland::Client::EventQueue queue; queue.setup(connection.data()); Registry registry; QSignalSpy registryDoneSpy(®istry, &Registry::interfacesAnnounced); QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QSignalSpy blurSpy(®istry, &Registry::blurAnnounced); registry.setEventQueue(&queue); registry.create(connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(registryDoneSpy.wait()); QVERIFY(compositorSpy.count() == 1); QVERIFY(blurSpy.count() == accessAllowed ? 1 : 0); thread->quit(); thread->wait(); } QTEST_GUILESS_MAIN(TestFilter) #include "test_wayland_filter.moc" diff --git a/autotests/client/test_wayland_fullscreen_shell.cpp b/autotests/client/test_wayland_fullscreen_shell.cpp index fb5114e..99e0973 100644 --- a/autotests/client/test_wayland_fullscreen_shell.cpp +++ b/autotests/client/test_wayland_fullscreen_shell.cpp @@ -1,161 +1,147 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/registry.h" #include "../../src/client/fullscreen_shell.h" // Wayland #include class TestWaylandFullscreenShell : public QObject { Q_OBJECT public: explicit TestWaylandFullscreenShell(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testRegistry(); void testRegistryCreate(); // TODO: add tests for removal - requires more control over the compositor private: QProcess *m_westonProcess; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-fullscreen-shell-0"); TestWaylandFullscreenShell::TestWaylandFullscreenShell(QObject *parent) : QObject(parent) , m_westonProcess(nullptr) { } void TestWaylandFullscreenShell::init() { QVERIFY(!m_westonProcess); // starts weston m_westonProcess = new QProcess(this); m_westonProcess->setProgram(QStringLiteral("weston")); m_westonProcess->setArguments(QStringList({QStringLiteral("--socket=%1").arg(s_socketName), QStringLiteral("--backend=headless-backend.so"), QStringLiteral("--shell=fullscreen-shell.so")})); m_westonProcess->start(); QVERIFY(m_westonProcess->waitForStarted()); QTest::qWait(500); // wait for the socket to appear QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR")); if (runtimeDir.exists(s_socketName)) { return; } QFileSystemWatcher *socketWatcher = new QFileSystemWatcher(QStringList({runtimeDir.absolutePath()}), this); QSignalSpy socketSpy(socketWatcher, SIGNAL(directoryChanged(QString))); // limit to maximum of 10 waits for (int i = 0; i < 10; ++i) { QVERIFY(socketSpy.wait()); if (runtimeDir.exists(s_socketName)) { delete socketWatcher; return; } } } void TestWaylandFullscreenShell::cleanup() { // terminates weston m_westonProcess->terminate(); QVERIFY(m_westonProcess->waitForFinished()); delete m_westonProcess; m_westonProcess = nullptr; } void TestWaylandFullscreenShell::testRegistry() { if (m_westonProcess->state() != QProcess::Running) { QSKIP("This test requires a running wayland server"); } KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, SIGNAL(fullscreenShellAnnounced(quint32,quint32))); registry.create(connection.display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(connection.display()); QVERIFY(interfacesAnnouncedSpy.wait()); if (!registry.hasInterface(KWayland::Client::Registry::Interface::FullscreenShell)) { QSKIP("Weston does not have fullscreen shell support"); } QCOMPARE(announced.count(), 1); KWayland::Client::FullscreenShell fullscreenShell; QVERIFY(!fullscreenShell.isValid()); QVERIFY(!fullscreenShell.hasCapabilityArbitraryModes()); QVERIFY(!fullscreenShell.hasCapabilityCursorPlane()); fullscreenShell.setup(registry.bindFullscreenShell(announced.first().first().value(), 1)); QVERIFY(fullscreenShell.isValid()); } void TestWaylandFullscreenShell::testRegistryCreate() { if (m_westonProcess->state() != QProcess::Running) { QSKIP("This test requires a running wayland server"); } KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfaceAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, SIGNAL(fullscreenShellAnnounced(quint32,quint32))); registry.create(connection.display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(connection.display()); QVERIFY(interfacesAnnouncedSpy.wait()); if (!registry.hasInterface(KWayland::Client::Registry::Interface::FullscreenShell)) { QSKIP("Weston does not have fullscreen shell support"); } QCOMPARE(announced.count(), 1); KWayland::Client::FullscreenShell *fullscreenShell = registry.createFullscreenShell(announced.first().first().value(), 1, ®istry); QVERIFY(fullscreenShell->isValid()); } QTEST_GUILESS_MAIN(TestWaylandFullscreenShell) #include "test_wayland_fullscreen_shell.moc" diff --git a/autotests/client/test_wayland_output.cpp b/autotests/client/test_wayland_output.cpp index 2c32561..3190474 100644 --- a/autotests/client/test_wayland_output.cpp +++ b/autotests/client/test_wayland_output.cpp @@ -1,629 +1,615 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/dpms.h" #include "../../src/client/output.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/dpms_interface.h" #include "../../src/server/output_interface.h" // Wayland #include class TestWaylandOutput : public QObject { Q_OBJECT public: explicit TestWaylandOutput(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testRegistry(); void testModeChanges(); void testScaleChange(); void testSubPixel_data(); void testSubPixel(); void testTransform_data(); void testTransform(); void testDpms_data(); void testDpms(); void testDpmsRequestMode_data(); void testDpmsRequestMode(); private: KWayland::Server::Display *m_display; KWayland::Server::OutputInterface *m_serverOutput; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); TestWaylandOutput::TestWaylandOutput(QObject *parent) : QObject(parent) , m_display(nullptr) , m_serverOutput(nullptr) , m_connection(nullptr) , m_thread(nullptr) { } void TestWaylandOutput::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_serverOutput = m_display->createOutput(this); QCOMPARE(m_serverOutput->pixelSize(), QSize()); QCOMPARE(m_serverOutput->refreshRate(), 60000); m_serverOutput->addMode(QSize(800, 600), OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred)); QCOMPARE(m_serverOutput->pixelSize(), QSize(800, 600)); m_serverOutput->addMode(QSize(1024, 768)); m_serverOutput->addMode(QSize(1280, 1024), OutputInterface::ModeFlags(), 90000); QCOMPARE(m_serverOutput->pixelSize(), QSize(800, 600)); m_serverOutput->setCurrentMode(QSize(1024, 768)); QCOMPARE(m_serverOutput->pixelSize(), QSize(1024, 768)); QCOMPARE(m_serverOutput->refreshRate(), 60000); m_serverOutput->create(); QCOMPARE(m_serverOutput->isDpmsSupported(), false); QCOMPARE(m_serverOutput->dpmsMode(), OutputInterface::DpmsMode::On); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); } void TestWaylandOutput::cleanup() { if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_serverOutput; m_serverOutput = nullptr; delete m_display; m_display = nullptr; } void TestWaylandOutput::testRegistry() { QSignalSpy globalPositionChangedSpy(m_serverOutput, &KWayland::Server::OutputInterface::globalPositionChanged); QVERIFY(globalPositionChangedSpy.isValid()); QCOMPARE(m_serverOutput->globalPosition(), QPoint(0, 0)); m_serverOutput->setGlobalPosition(QPoint(100, 50)); QCOMPARE(m_serverOutput->globalPosition(), QPoint(100, 50)); QCOMPARE(globalPositionChangedSpy.count(), 1); // changing again should not trigger signal m_serverOutput->setGlobalPosition(QPoint(100, 50)); QCOMPARE(globalPositionChangedSpy.count(), 1); QSignalSpy physicalSizeChangedSpy(m_serverOutput, &KWayland::Server::OutputInterface::physicalSizeChanged); QVERIFY(physicalSizeChangedSpy.isValid()); QCOMPARE(m_serverOutput->physicalSize(), QSize()); m_serverOutput->setPhysicalSize(QSize(200, 100)); QCOMPARE(m_serverOutput->physicalSize(), QSize(200, 100)); QCOMPARE(physicalSizeChangedSpy.count(), 1); // changing again should not trigger signal m_serverOutput->setPhysicalSize(QSize(200, 100)); QCOMPARE(physicalSizeChangedSpy.count(), 1); KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(announced.wait()); KWayland::Client::Output output; QVERIFY(!output.isValid()); QCOMPARE(output.geometry(), QRect()); QCOMPARE(output.globalPosition(), QPoint()); QCOMPARE(output.manufacturer(), QString()); QCOMPARE(output.model(), QString()); QCOMPARE(output.physicalSize(), QSize()); QCOMPARE(output.pixelSize(), QSize()); QCOMPARE(output.refreshRate(), 0); QCOMPARE(output.scale(), 1); QCOMPARE(output.subPixel(), KWayland::Client::Output::SubPixel::Unknown); QCOMPARE(output.transform(), KWayland::Client::Output::Transform::Normal); QSignalSpy outputChanged(&output, SIGNAL(changed())); QVERIFY(outputChanged.isValid()); auto o = registry.bindOutput(announced.first().first().value(), announced.first().last().value()); QVERIFY(!KWayland::Client::Output::get(o)); output.setup(o); QCOMPARE(KWayland::Client::Output::get(o), &output); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.geometry(), QRect(100, 50, 1024, 768)); QCOMPARE(output.globalPosition(), QPoint(100, 50)); QCOMPARE(output.manufacturer(), QStringLiteral("org.kde.kwin")); QCOMPARE(output.model(), QStringLiteral("none")); QCOMPARE(output.physicalSize(), QSize(200, 100)); QCOMPARE(output.pixelSize(), QSize(1024, 768)); QCOMPARE(output.refreshRate(), 60000); QCOMPARE(output.scale(), 1); // for xwayland output it's unknown QCOMPARE(output.subPixel(), KWayland::Client::Output::SubPixel::Unknown); // for xwayland transform is normal QCOMPARE(output.transform(), KWayland::Client::Output::Transform::Normal); } void TestWaylandOutput::testModeChanges() { // verify the server modes using namespace KWayland::Server; const auto serverModes = m_serverOutput->modes(); QCOMPARE(serverModes.count(), 3); QCOMPARE(serverModes.at(0).size, QSize(800, 600)); QCOMPARE(serverModes.at(1).size, QSize(1024, 768)); QCOMPARE(serverModes.at(2).size, QSize(1280, 1024)); QCOMPARE(serverModes.at(0).refreshRate, 60000); QCOMPARE(serverModes.at(1).refreshRate, 60000); QCOMPARE(serverModes.at(2).refreshRate, 90000); QCOMPARE(serverModes.at(0).flags, OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred)); QCOMPARE(serverModes.at(1).flags, OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current)); QCOMPARE(serverModes.at(2).flags, OutputInterface::ModeFlags()); using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(announced.wait()); KWayland::Client::Output output; QSignalSpy outputChanged(&output, SIGNAL(changed())); QVERIFY(outputChanged.isValid()); QSignalSpy modeAddedSpy(&output, SIGNAL(modeAdded(KWayland::Client::Output::Mode))); QVERIFY(modeAddedSpy.isValid()); output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(modeAddedSpy.count(), 3); QCOMPARE(modeAddedSpy.at(0).first().value().size, QSize(800, 600)); QCOMPARE(modeAddedSpy.at(0).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(0).first().value().flags, Output::Mode::Flags(Output::Mode::Flag::Preferred)); QCOMPARE(modeAddedSpy.at(0).first().value().output, QPointer(&output)); QCOMPARE(modeAddedSpy.at(1).first().value().size, QSize(1280, 1024)); QCOMPARE(modeAddedSpy.at(1).first().value().refreshRate, 90000); QCOMPARE(modeAddedSpy.at(1).first().value().flags, Output::Mode::Flags(Output::Mode::Flag::None)); QCOMPARE(modeAddedSpy.at(1).first().value().output, QPointer(&output)); QCOMPARE(modeAddedSpy.at(2).first().value().size, QSize(1024, 768)); QCOMPARE(modeAddedSpy.at(2).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(2).first().value().flags, Output::Mode::Flags(Output::Mode::Flag::Current)); QCOMPARE(modeAddedSpy.at(2).first().value().output, QPointer(&output)); const QList &modes = output.modes(); QCOMPARE(modes.size(), 3); QCOMPARE(modes.at(0), modeAddedSpy.at(0).first().value()); QCOMPARE(modes.at(1), modeAddedSpy.at(1).first().value()); QCOMPARE(modes.at(2), modeAddedSpy.at(2).first().value()); QCOMPARE(output.pixelSize(), QSize(1024, 768)); // change the current mode outputChanged.clear(); QSignalSpy modeChangedSpy(&output, &KWayland::Client::Output::modeChanged); QVERIFY(modeChangedSpy.isValid()); m_serverOutput->setCurrentMode(QSize(800, 600)); QVERIFY(modeChangedSpy.wait()); if (modeChangedSpy.size() == 1) { QVERIFY(modeChangedSpy.wait()); } QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(1024, 768)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, Output::Mode::Flags()); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.last().first().value().flags, Output::Mode::Flags(Output::Mode::Flag::Current | Output::Mode::Flag::Preferred)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(800, 600)); const QList &modes2 = output.modes(); QCOMPARE(modes2.at(0).size, QSize(1280, 1024)); QCOMPARE(modes2.at(0).refreshRate, 90000); QCOMPARE(modes2.at(0).flags, Output::Mode::Flag::None); QCOMPARE(modes2.at(1).size, QSize(1024, 768)); QCOMPARE(modes2.at(1).refreshRate, 60000); QCOMPARE(modes2.at(1).flags, Output::Mode::Flag::None); QCOMPARE(modes2.at(2).size, QSize(800, 600)); QCOMPARE(modes2.at(2).refreshRate, 60000); QCOMPARE(modes2.at(2).flags, Output::Mode::Flag::Current | Output::Mode::Flag::Preferred); // change once more outputChanged.clear(); modeChangedSpy.clear(); m_serverOutput->setCurrentMode(QSize(1280, 1024), 90000); QCOMPARE(m_serverOutput->refreshRate(), 90000); QVERIFY(modeChangedSpy.wait()); if (modeChangedSpy.size() == 1) { QVERIFY(modeChangedSpy.wait()); } QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, Output::Mode::Flags(Output::Mode::Flag::Preferred)); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(1280, 1024)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 90000); QCOMPARE(modeChangedSpy.last().first().value().flags, Output::Mode::Flags(Output::Mode::Flag::Current)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(1280, 1024)); } void TestWaylandOutput::testScaleChange() { KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(announced.wait()); KWayland::Client::Output output; QSignalSpy outputChanged(&output, SIGNAL(changed())); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 1); // change the scale outputChanged.clear(); QCOMPARE(m_serverOutput->scale(), 1); QSignalSpy serverScaleChanged(m_serverOutput, &KWayland::Server::OutputInterface::scaleChanged); QVERIFY(serverScaleChanged.isValid()); m_serverOutput->setScale(2); QCOMPARE(m_serverOutput->scale(), 2); QCOMPARE(serverScaleChanged.count(), 1); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 2); // changing to same value should not trigger m_serverOutput->setScale(2); QCOMPARE(serverScaleChanged.count(), 1); QVERIFY(!outputChanged.wait(100)); // change once more outputChanged.clear(); m_serverOutput->setScale(4); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 4); } void TestWaylandOutput::testSubPixel_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("none") << Output::SubPixel::None << OutputInterface::SubPixel::None; QTest::newRow("horizontal/rgb") << Output::SubPixel::HorizontalRGB << OutputInterface::SubPixel::HorizontalRGB; QTest::newRow("horizontal/bgr") << Output::SubPixel::HorizontalBGR << OutputInterface::SubPixel::HorizontalBGR; QTest::newRow("vertical/rgb") << Output::SubPixel::VerticalRGB << OutputInterface::SubPixel::VerticalRGB; QTest::newRow("vertical/bgr") << Output::SubPixel::VerticalBGR << OutputInterface::SubPixel::VerticalBGR; } void TestWaylandOutput::testSubPixel() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputInterface::SubPixel, actual); QCOMPARE(m_serverOutput->subPixel(), OutputInterface::SubPixel::Unknown); QSignalSpy serverSubPixelChangedSpy(m_serverOutput, &OutputInterface::subPixelChanged); QVERIFY(serverSubPixelChangedSpy.isValid()); m_serverOutput->setSubPixel(actual); QCOMPARE(m_serverOutput->subPixel(), actual); QCOMPARE(serverSubPixelChangedSpy.count(), 1); // changing to same value should not trigger the signal m_serverOutput->setSubPixel(actual); QCOMPARE(serverSubPixelChangedSpy.count(), 1); KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(announced.wait()); KWayland::Client::Output output; QSignalSpy outputChanged(&output, SIGNAL(changed())); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); if (outputChanged.isEmpty()) { QVERIFY(outputChanged.wait()); } QTEST(output.subPixel(), "expected"); // change back to unknown outputChanged.clear(); m_serverOutput->setSubPixel(OutputInterface::SubPixel::Unknown); QCOMPARE(m_serverOutput->subPixel(), OutputInterface::SubPixel::Unknown); QCOMPARE(serverSubPixelChangedSpy.count(), 2); if (outputChanged.isEmpty()) { QVERIFY(outputChanged.wait()); } QCOMPARE(output.subPixel(), Output::SubPixel::Unknown); } void TestWaylandOutput::testTransform_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("90") << Output::Transform::Rotated90 << OutputInterface::Transform::Rotated90; QTest::newRow("180") << Output::Transform::Rotated180 << OutputInterface::Transform::Rotated180; QTest::newRow("270") << Output::Transform::Rotated270 << OutputInterface::Transform::Rotated270; QTest::newRow("Flipped") << Output::Transform::Flipped << OutputInterface::Transform::Flipped; QTest::newRow("Flipped 90") << Output::Transform::Flipped90 << OutputInterface::Transform::Flipped90; QTest::newRow("Flipped 180") << Output::Transform::Flipped180 << OutputInterface::Transform::Flipped180; QTest::newRow("Flipped 280") << Output::Transform::Flipped270 << OutputInterface::Transform::Flipped270; } void TestWaylandOutput::testTransform() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputInterface::Transform, actual); QCOMPARE(m_serverOutput->transform(), OutputInterface::Transform::Normal); QSignalSpy serverTransformChangedSpy(m_serverOutput, &OutputInterface::transformChanged); QVERIFY(serverTransformChangedSpy.isValid()); m_serverOutput->setTransform(actual); QCOMPARE(m_serverOutput->transform(), actual); QCOMPARE(serverTransformChangedSpy.count(), 1); // changing to same should not trigger signal m_serverOutput->setTransform(actual); QCOMPARE(serverTransformChangedSpy.count(), 1); KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(announced.wait()); KWayland::Client::Output *output = registry.createOutput(announced.first().first().value(), announced.first().last().value(), ®istry); QSignalSpy outputChanged(output, SIGNAL(changed())); QVERIFY(outputChanged.isValid()); wl_display_flush(m_connection->display()); if (outputChanged.isEmpty()) { QVERIFY(outputChanged.wait()); } QTEST(output->transform(), "expected"); // change back to normal outputChanged.clear(); m_serverOutput->setTransform(OutputInterface::Transform::Normal); QCOMPARE(m_serverOutput->transform(), OutputInterface::Transform::Normal); QCOMPARE(serverTransformChangedSpy.count(), 2); if (outputChanged.isEmpty()) { QVERIFY(outputChanged.wait()); } QCOMPARE(output->transform(), Output::Transform::Normal); } void TestWaylandOutput::testDpms_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("client"); QTest::addColumn("server"); QTest::newRow("Standby") << Dpms::Mode::Standby << OutputInterface::DpmsMode::Standby; QTest::newRow("Suspend") << Dpms::Mode::Suspend << OutputInterface::DpmsMode::Suspend; QTest::newRow("Off") << Dpms::Mode::Off << OutputInterface::DpmsMode::Off; } void TestWaylandOutput::testDpms() { using namespace KWayland::Client; using namespace KWayland::Server; m_display->createDpmsManager()->create(); // set Dpms on the Output QSignalSpy serverDpmsSupportedChangedSpy(m_serverOutput, &OutputInterface::dpmsSupportedChanged); QVERIFY(serverDpmsSupportedChangedSpy.isValid()); QCOMPARE(m_serverOutput->isDpmsSupported(), false); m_serverOutput->setDpmsSupported(true); QCOMPARE(serverDpmsSupportedChangedSpy.count(), 1); QCOMPARE(m_serverOutput->isDpmsSupported(), true); KWayland::Client::Registry registry; registry.setEventQueue(m_queue); QSignalSpy announced(®istry, &Registry::interfacesAnnounced); QVERIFY(announced.isValid()); QSignalSpy dpmsAnnouncedSpy(®istry, &Registry::dpmsAnnounced); QVERIFY(dpmsAnnouncedSpy.isValid()); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_connection->flush(); QVERIFY(announced.wait()); QCOMPARE(dpmsAnnouncedSpy.count(), 1); Output *output = registry.createOutput(registry.interface(Registry::Interface::Output).name, registry.interface(Registry::Interface::Output).version, ®istry); DpmsManager *dpmsManager = registry.createDpmsManager(dpmsAnnouncedSpy.first().first().value(), dpmsAnnouncedSpy.first().last().value(), ®istry); QVERIFY(dpmsManager->isValid()); Dpms *dpms = dpmsManager->getDpms(output, ®istry); QSignalSpy clientDpmsSupportedChangedSpy(dpms, &Dpms::supportedChanged); QVERIFY(clientDpmsSupportedChangedSpy.isValid()); QVERIFY(dpms->isValid()); QCOMPARE(dpms->isSupported(), false); QCOMPARE(dpms->mode(), Dpms::Mode::On); m_connection->flush(); QVERIFY(clientDpmsSupportedChangedSpy.wait()); QCOMPARE(clientDpmsSupportedChangedSpy.count(), 1); QCOMPARE(dpms->isSupported(), true); // and let's change to suspend QSignalSpy serverDpmsModeChangedSpy(m_serverOutput, &OutputInterface::dpmsModeChanged); QVERIFY(serverDpmsModeChangedSpy.isValid()); QSignalSpy clientDpmsModeChangedSpy(dpms, &Dpms::modeChanged); QVERIFY(clientDpmsModeChangedSpy.isValid()); QCOMPARE(m_serverOutput->dpmsMode(), OutputInterface::DpmsMode::On); QFETCH(OutputInterface::DpmsMode, server); m_serverOutput->setDpmsMode(server); QCOMPARE(m_serverOutput->dpmsMode(), server); QCOMPARE(serverDpmsModeChangedSpy.count(), 1); QVERIFY(clientDpmsModeChangedSpy.wait()); QCOMPARE(clientDpmsModeChangedSpy.count(), 1); QTEST(dpms->mode(), "client"); // test supported changed QSignalSpy supportedChangedSpy(dpms, &Dpms::supportedChanged); QVERIFY(supportedChangedSpy.isValid()); m_serverOutput->setDpmsSupported(false); QVERIFY(supportedChangedSpy.wait()); QCOMPARE(supportedChangedSpy.count(), 1); QVERIFY(!dpms->isSupported()); m_serverOutput->setDpmsSupported(true); QVERIFY(supportedChangedSpy.wait()); QCOMPARE(supportedChangedSpy.count(), 2); QVERIFY(dpms->isSupported()); // and switch back to on m_serverOutput->setDpmsMode(OutputInterface::DpmsMode::On); QVERIFY(clientDpmsModeChangedSpy.wait()); QCOMPARE(clientDpmsModeChangedSpy.count(), 2); QCOMPARE(dpms->mode(), Dpms::Mode::On); } void TestWaylandOutput::testDpmsRequestMode_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("client"); QTest::addColumn("server"); QTest::newRow("Standby") << Dpms::Mode::Standby << OutputInterface::DpmsMode::Standby; QTest::newRow("Suspend") << Dpms::Mode::Suspend << OutputInterface::DpmsMode::Suspend; QTest::newRow("Off") << Dpms::Mode::Off << OutputInterface::DpmsMode::Off; QTest::newRow("On") << Dpms::Mode::On << OutputInterface::DpmsMode::On; } void TestWaylandOutput::testDpmsRequestMode() { // this test verifies that requesting a dpms change from client side emits the signal on server side using namespace KWayland::Client; using namespace KWayland::Server; // setup code m_display->createDpmsManager()->create(); // set Dpms on the Output QSignalSpy serverDpmsSupportedChangedSpy(m_serverOutput, &OutputInterface::dpmsSupportedChanged); QVERIFY(serverDpmsSupportedChangedSpy.isValid()); QCOMPARE(m_serverOutput->isDpmsSupported(), false); m_serverOutput->setDpmsSupported(true); QCOMPARE(serverDpmsSupportedChangedSpy.count(), 1); QCOMPARE(m_serverOutput->isDpmsSupported(), true); KWayland::Client::Registry registry; registry.setEventQueue(m_queue); QSignalSpy announced(®istry, &Registry::interfacesAnnounced); QVERIFY(announced.isValid()); QSignalSpy dpmsAnnouncedSpy(®istry, &Registry::dpmsAnnounced); QVERIFY(dpmsAnnouncedSpy.isValid()); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_connection->flush(); QVERIFY(announced.wait()); QCOMPARE(dpmsAnnouncedSpy.count(), 1); Output *output = registry.createOutput(registry.interface(Registry::Interface::Output).name, registry.interface(Registry::Interface::Output).version, ®istry); DpmsManager *dpmsManager = registry.createDpmsManager(dpmsAnnouncedSpy.first().first().value(), dpmsAnnouncedSpy.first().last().value(), ®istry); QVERIFY(dpmsManager->isValid()); Dpms *dpms = dpmsManager->getDpms(output, ®istry); // and test request mode QSignalSpy modeRequestedSpy(m_serverOutput, &OutputInterface::dpmsModeRequested); QVERIFY(modeRequestedSpy.isValid()); QFETCH(Dpms::Mode, client); dpms->requestMode(client); QVERIFY(modeRequestedSpy.wait()); QTEST(modeRequestedSpy.last().first().value(), "server"); } QTEST_GUILESS_MAIN(TestWaylandOutput) #include "test_wayland_output.moc" diff --git a/autotests/client/test_wayland_outputdevice.cpp b/autotests/client/test_wayland_outputdevice.cpp index c96d894..ab019a7 100644 --- a/autotests/client/test_wayland_outputdevice.cpp +++ b/autotests/client/test_wayland_outputdevice.cpp @@ -1,710 +1,696 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/outputdevice.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/outputdevice_interface.h" // Wayland #include using namespace KWayland::Client; using namespace KWayland::Server; class TestWaylandOutputDevice : public QObject { Q_OBJECT public: explicit TestWaylandOutputDevice(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testRegistry(); void testModeChanges(); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) void testScaleChange_legacy(); #endif void testScaleChange(); void testColorCurvesChange(); void testSubPixel_data(); void testSubPixel(); void testTransform_data(); void testTransform(); void testEnabled(); void testEdid(); void testId(); void testDone(); private: KWayland::Server::Display *m_display; KWayland::Server::OutputDeviceInterface *m_serverOutputDevice; QByteArray m_edid; QString m_serialNumber; QString m_eidaId; KWayland::Server::OutputDeviceInterface::ColorCurves m_initColorCurves; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); TestWaylandOutputDevice::TestWaylandOutputDevice(QObject *parent) : QObject(parent) , m_display(nullptr) , m_serverOutputDevice(nullptr) , m_connection(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandOutputDevice::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_serverOutputDevice = m_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_edid = QByteArray::fromBase64("AP///////wAQrBbwTExLQQ4WAQOANCB46h7Frk80sSYOUFSlSwCBgKlA0QBxTwEBAQEBAQEBKDyAoHCwI0AwIDYABkQhAAAaAAAA/wBGNTI1TTI0NUFLTEwKAAAA/ABERUxMIFUyNDEwCiAgAAAA/QA4TB5REQAKICAgICAgAToCAynxUJAFBAMCBxYBHxITFCAVEQYjCQcHZwMMABAAOC2DAQAA4wUDAQI6gBhxOC1AWCxFAAZEIQAAHgEdgBhxHBYgWCwlAAZEIQAAngEdAHJR0B4gbihVAAZEIQAAHowK0Iog4C0QED6WAAZEIQAAGAAAAAAAAAAAAAAAAAAAPg=="); m_serverOutputDevice->setEdid(m_edid); m_serialNumber = "23498723948723"; m_serverOutputDevice->setSerialNumber(m_serialNumber); m_eidaId = "asdffoo"; m_serverOutputDevice->setEisaId(m_eidaId); m_initColorCurves.red.clear(); m_initColorCurves.green.clear(); m_initColorCurves.blue.clear(); // 8 bit color ramps for (int i = 0; i < 256; i++) { quint16 val = (double)i / 255 * UINT16_MAX; m_initColorCurves.red << val ; m_initColorCurves.green << val ; } // 10 bit color ramp for (int i = 0; i < 320; i++) { m_initColorCurves.blue << (double)i / 319 * UINT16_MAX; } m_serverOutputDevice->setColorCurves(m_initColorCurves); m_serverOutputDevice->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); } void TestWaylandOutputDevice::cleanup() { if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_serverOutputDevice; m_serverOutputDevice = nullptr; delete m_display; m_display = nullptr; } void TestWaylandOutputDevice::testRegistry() { m_serverOutputDevice->setGlobalPosition(QPoint(100, 50)); m_serverOutputDevice->setPhysicalSize(QSize(200, 100)); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QVERIFY(!output.isValid()); QCOMPARE(output.uuid(), QByteArray()); QCOMPARE(output.geometry(), QRect()); QCOMPARE(output.globalPosition(), QPoint()); QCOMPARE(output.manufacturer(), QString()); QCOMPARE(output.model(), QString()); QCOMPARE(output.physicalSize(), QSize()); QCOMPARE(output.pixelSize(), QSize()); QCOMPARE(output.refreshRate(), 0); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output.scale(), 1); #endif QCOMPARE(output.scaleF(), 1.0); QCOMPARE(output.colorCurves().red, QVector()); QCOMPARE(output.colorCurves().green, QVector()); QCOMPARE(output.colorCurves().blue, QVector()); QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output.edid(), QByteArray()); QCOMPARE(output.eisaId(), QString()); QCOMPARE(output.serialNumber(), QString()); QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.geometry(), QRect(100, 50, 1024, 768)); QCOMPARE(output.globalPosition(), QPoint(100, 50)); QCOMPARE(output.manufacturer(), QStringLiteral("org.kde.kwin")); QCOMPARE(output.model(), QStringLiteral("none")); QCOMPARE(output.physicalSize(), QSize(200, 100)); QCOMPARE(output.pixelSize(), QSize(1024, 768)); QCOMPARE(output.refreshRate(), 60000); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output.scale(), 1); #endif QCOMPARE(output.scaleF(), 1.0); QCOMPARE(output.colorCurves().red, m_initColorCurves.red); QCOMPARE(output.colorCurves().green, m_initColorCurves.green); QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue); // for xwayland output it's unknown QCOMPARE(output.subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); // for xwayland transform is normal QCOMPARE(output.transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output.edid(), m_edid); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output.uuid(), QByteArray("1337")); QCOMPARE(output.serialNumber(), m_serialNumber); QCOMPARE(output.eisaId(), m_eidaId); } void TestWaylandOutputDevice::testModeChanges() { using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChanged.isValid()); QSignalSpy modeAddedSpy(&output, &KWayland::Client::OutputDevice::modeAdded); QVERIFY(modeAddedSpy.isValid()); QSignalSpy doneSpy(&output, &KWayland::Client::OutputDevice::done); QVERIFY(doneSpy.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(doneSpy.wait()); QCOMPARE(modeAddedSpy.count(), 3); QCOMPARE(modeAddedSpy.at(0).first().value().size, QSize(800, 600)); QCOMPARE(modeAddedSpy.at(0).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(0).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Preferred)); QCOMPARE(modeAddedSpy.at(0).first().value().output, QPointer(&output)); QVERIFY(modeAddedSpy.at(0).first().value().id > -1); QCOMPARE(modeAddedSpy.at(1).first().value().size, QSize(1280, 1024)); QCOMPARE(modeAddedSpy.at(1).first().value().refreshRate, 90000); QCOMPARE(modeAddedSpy.at(1).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::None)); QCOMPARE(modeAddedSpy.at(1).first().value().output, QPointer(&output)); QVERIFY(modeAddedSpy.at(1).first().value().id > -1); QCOMPARE(modeAddedSpy.at(2).first().value().size, QSize(1024, 768)); QCOMPARE(modeAddedSpy.at(2).first().value().refreshRate, 60000); QCOMPARE(modeAddedSpy.at(2).first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current)); QCOMPARE(modeAddedSpy.at(2).first().value().output, QPointer(&output)); const QList &modes = output.modes(); QVERIFY(modeAddedSpy.at(2).first().value().id > -1); QCOMPARE(modes.size(), 3); QCOMPARE(modes.at(0), modeAddedSpy.at(0).first().value()); QCOMPARE(modes.at(1), modeAddedSpy.at(1).first().value()); QCOMPARE(modes.at(2), modeAddedSpy.at(2).first().value()); QCOMPARE(output.pixelSize(), QSize(1024, 768)); // change the current mode outputChanged.clear(); QSignalSpy modeChangedSpy(&output, &KWayland::Client::OutputDevice::modeChanged); QVERIFY(modeChangedSpy.isValid()); m_serverOutputDevice->setCurrentMode(0); QVERIFY(doneSpy.wait()); QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(1024, 768)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, OutputDevice::Mode::Flags()); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.last().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current | OutputDevice::Mode::Flag::Preferred)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(800, 600)); const QList &modes2 = output.modes(); QCOMPARE(modes2.at(0).size, QSize(1280, 1024)); QCOMPARE(modes2.at(0).refreshRate, 90000); QCOMPARE(modes2.at(0).flags, OutputDevice::Mode::Flag::None); QCOMPARE(modes2.at(1).size, QSize(1024, 768)); QCOMPARE(modes2.at(1).refreshRate, 60000); QCOMPARE(modes2.at(1).flags, OutputDevice::Mode::Flag::None); QCOMPARE(modes2.at(2).size, QSize(800, 600)); QCOMPARE(modes2.at(2).refreshRate, 60000); QCOMPARE(modes2.at(2).flags, OutputDevice::Mode::Flag::Current | OutputDevice::Mode::Flag::Preferred); // change once more outputChanged.clear(); modeChangedSpy.clear(); m_serverOutputDevice->setCurrentMode(2); QVERIFY(doneSpy.wait()); QCOMPARE(modeChangedSpy.size(), 2); // the one which lost the current flag QCOMPARE(modeChangedSpy.first().first().value().size, QSize(800, 600)); QCOMPARE(modeChangedSpy.first().first().value().refreshRate, 60000); QCOMPARE(modeChangedSpy.first().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Preferred)); // the one which got the current flag QCOMPARE(modeChangedSpy.last().first().value().size, QSize(1280, 1024)); QCOMPARE(modeChangedSpy.last().first().value().refreshRate, 90000); QCOMPARE(modeChangedSpy.last().first().value().flags, OutputDevice::Mode::Flags(OutputDevice::Mode::Flag::Current)); QVERIFY(!outputChanged.isEmpty()); QCOMPARE(output.pixelSize(), QSize(1280, 1024)); } #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) void TestWaylandOutputDevice::testScaleChange_legacy() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 1); QCOMPARE(output.scaleF(), 1.0); // change the scale outputChanged.clear(); m_serverOutputDevice->setScale(2); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 2); QCOMPARE(output.scaleF(), 2.0); //check we're forward compatible // change once more outputChanged.clear(); m_serverOutputDevice->setScale(4); QVERIFY(outputChanged.wait()); QCOMPARE(output.scale(), 4); QCOMPARE(output.scaleF(), 4.0); } #endif void TestWaylandOutputDevice::testScaleChange() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.scaleF(), 1.0); // change the scale outputChanged.clear(); m_serverOutputDevice->setScaleF(2.2); QVERIFY(outputChanged.wait()); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output.scale(), 2); //check backwards compatibility works #endif QCOMPARE(wl_fixed_from_double(output.scaleF()), wl_fixed_from_double(2.2)); // change once more outputChanged.clear(); m_serverOutputDevice->setScaleF(4.9); QVERIFY(outputChanged.wait()); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output.scale(), 5); #endif QCOMPARE(wl_fixed_from_double(output.scaleF()), wl_fixed_from_double(4.9)); } void TestWaylandOutputDevice::testColorCurvesChange() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, m_initColorCurves.red); QCOMPARE(output.colorCurves().green, m_initColorCurves.green); QCOMPARE(output.colorCurves().blue, m_initColorCurves.blue); // change the color curves outputChanged.clear(); KWayland::Server::OutputDeviceInterface::ColorCurves cc; cc.red = QVector(256, 0); cc.green = QVector(256, UINT16_MAX); cc.blue = QVector(320, 1); m_serverOutputDevice->setColorCurves(cc); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, cc.red); QCOMPARE(output.colorCurves().green, cc.green); QCOMPARE(output.colorCurves().blue, cc.blue); // change once more outputChanged.clear(); cc.red = QVector(256, 0); cc.green = QVector(256, UINT16_MAX); cc.blue = QVector(320, UINT16_MAX); m_serverOutputDevice->setColorCurves(cc); QVERIFY(outputChanged.wait()); QCOMPARE(output.colorCurves().red, cc.red); QCOMPARE(output.colorCurves().green, cc.green); QCOMPARE(output.colorCurves().blue, cc.blue); } void TestWaylandOutputDevice::testSubPixel_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("none") << OutputDevice::SubPixel::None << OutputDeviceInterface::SubPixel::None; QTest::newRow("horizontal/rgb") << OutputDevice::SubPixel::HorizontalRGB << OutputDeviceInterface::SubPixel::HorizontalRGB; QTest::newRow("horizontal/bgr") << OutputDevice::SubPixel::HorizontalBGR << OutputDeviceInterface::SubPixel::HorizontalBGR; QTest::newRow("vertical/rgb") << OutputDevice::SubPixel::VerticalRGB << OutputDeviceInterface::SubPixel::VerticalRGB; QTest::newRow("vertical/bgr") << OutputDevice::SubPixel::VerticalBGR << OutputDeviceInterface::SubPixel::VerticalBGR; } void TestWaylandOutputDevice::testSubPixel() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputDeviceInterface::SubPixel, actual); m_serverOutputDevice->setSubPixel(actual); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QTEST(output.subPixel(), "expected"); // change back to unknown outputChanged.clear(); m_serverOutputDevice->setSubPixel(OutputDeviceInterface::SubPixel::Unknown); QVERIFY(outputChanged.wait()); QCOMPARE(output.subPixel(), OutputDevice::SubPixel::Unknown); } void TestWaylandOutputDevice::testTransform_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("expected"); QTest::addColumn("actual"); QTest::newRow("90") << OutputDevice::Transform::Rotated90 << OutputDeviceInterface::Transform::Rotated90; QTest::newRow("180") << OutputDevice::Transform::Rotated180 << OutputDeviceInterface::Transform::Rotated180; QTest::newRow("270") << OutputDevice::Transform::Rotated270 << OutputDeviceInterface::Transform::Rotated270; QTest::newRow("Flipped") << OutputDevice::Transform::Flipped << OutputDeviceInterface::Transform::Flipped; QTest::newRow("Flipped 90") << OutputDevice::Transform::Flipped90 << OutputDeviceInterface::Transform::Flipped90; QTest::newRow("Flipped 180") << OutputDevice::Transform::Flipped180 << OutputDeviceInterface::Transform::Flipped180; QTest::newRow("Flipped 280") << OutputDevice::Transform::Flipped270 << OutputDeviceInterface::Transform::Flipped270; } void TestWaylandOutputDevice::testTransform() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(OutputDeviceInterface::Transform, actual); m_serverOutputDevice->setTransform(actual); KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice *output = registry.createOutputDevice(announced.first().first().value(), announced.first().last().value(), ®istry); QSignalSpy outputChanged(output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QTEST(output->transform(), "expected"); // change back to normal outputChanged.clear(); m_serverOutputDevice->setTransform(OutputDeviceInterface::Transform::Normal); QVERIFY(outputChanged.wait()); QCOMPARE(output->transform(), OutputDevice::Transform::Normal); } void TestWaylandOutputDevice::testEnabled() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); QSignalSpy changed(&output, &KWayland::Client::OutputDevice::changed); QSignalSpy enabledChanged(&output, &KWayland::Client::OutputDevice::enabledChanged); QVERIFY(enabledChanged.isValid()); m_serverOutputDevice->setEnabled(OutputDeviceInterface::Enablement::Disabled); QVERIFY(enabledChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Disabled); if (changed.count() != enabledChanged.count()) { QVERIFY(changed.wait()); } QCOMPARE(changed.count(), enabledChanged.count()); m_serverOutputDevice->setEnabled(OutputDeviceInterface::Enablement::Enabled); QVERIFY(enabledChanged.wait()); QCOMPARE(output.enabled(), OutputDevice::Enablement::Enabled); if (changed.count() != enabledChanged.count()) { QVERIFY(changed.wait()); } QCOMPARE(changed.count(), enabledChanged.count()); } void TestWaylandOutputDevice::testEdid() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QCOMPARE(output.edid(), QByteArray()); QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.edid(), m_edid); } void TestWaylandOutputDevice::testId() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputChanged(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputChanged.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output.uuid(), QByteArray("1337")); QSignalSpy idChanged(&output, &KWayland::Client::OutputDevice::uuidChanged); QVERIFY(idChanged.isValid()); m_serverOutputDevice->setUuid("42"); QVERIFY(idChanged.wait()); QCOMPARE(idChanged.first().first().toByteArray(), QByteArray("42")); idChanged.clear(); QCOMPARE(output.uuid(), QByteArray("42")); m_serverOutputDevice->setUuid("4711"); QVERIFY(idChanged.wait()); QCOMPARE(idChanged.first().first().toByteArray(), QByteArray("4711")); idChanged.clear(); QCOMPARE(output.uuid(), QByteArray("4711")); } void TestWaylandOutputDevice::testDone() { KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy announced(®istry, &KWayland::Client::Registry::outputDeviceAnnounced); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); wl_display_flush(m_connection->display()); QVERIFY(interfacesAnnouncedSpy.wait()); KWayland::Client::OutputDevice output; QSignalSpy outputDone(&output, &KWayland::Client::OutputDevice::done); QVERIFY(outputDone.isValid()); output.setup(registry.bindOutputDevice(announced.first().first().value(), announced.first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputDone.wait()); } QTEST_GUILESS_MAIN(TestWaylandOutputDevice) #include "test_wayland_outputdevice.moc" diff --git a/autotests/client/test_wayland_outputmanagement.cpp b/autotests/client/test_wayland_outputmanagement.cpp index de61650..6790ac6 100644 --- a/autotests/client/test_wayland_outputmanagement.cpp +++ b/autotests/client/test_wayland_outputmanagement.cpp @@ -1,552 +1,538 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/outputdevice.h" #include "../../src/client/outputconfiguration.h" #include "../../src/client/outputmanagement.h" #include "../../src/client/output.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/shell_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/outputconfiguration_interface.h" #include "../../src/server/outputdevice_interface.h" #include "../../src/server/outputmanagement_interface.h" // Wayland #include using namespace KWayland::Client; using namespace KWayland::Server; class TestWaylandOutputManagement : public QObject { Q_OBJECT public: explicit TestWaylandOutputManagement(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void createConfig(); void testBasicMemoryManagement(); void testMultipleSettings(); void testConfigFailed(); void testApplied(); void testFailed(); void testExampleConfig(); void testScale(); void testRemoval(); private: void createOutputDevices(); void testEnable(); void applyPendingChanges(KWayland::Server::OutputConfigurationInterface *configurationInterface); KWayland::Server::Display *m_display; KWayland::Server::OutputManagementInterface *m_outputManagementInterface; QList m_serverOutputs; KWayland::Client::Registry *m_registry = nullptr; KWayland::Client::OutputDevice *m_outputDevice = nullptr; KWayland::Client::OutputManagement *m_outputManagement = nullptr; KWayland::Client::OutputConfiguration *m_outputConfiguration = nullptr; QList m_clientOutputs; QList m_modes; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread; QSignalSpy *m_announcedSpy; QSignalSpy *m_omSpy; QSignalSpy *m_configSpy; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); TestWaylandOutputManagement::TestWaylandOutputManagement(QObject *parent) : QObject(parent) , m_display(nullptr) , m_outputManagementInterface(nullptr) , m_connection(nullptr) , m_queue(nullptr) , m_thread(nullptr) , m_announcedSpy(nullptr) { qRegisterMetaType(); } void TestWaylandOutputManagement::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); auto shell = m_display->createShell(this); shell->create(); auto comp = m_display->createCompositor(this); comp->create(); auto outputDeviceInterface = m_display->createOutputDevice(this); OutputDeviceInterface::Mode m0; m0.id = 0; m0.size = QSize(800, 600); m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); outputDeviceInterface->addMode(m0); OutputDeviceInterface::Mode m1; m1.id = 1; m1.size = QSize(1024, 768); outputDeviceInterface->addMode(m1); OutputDeviceInterface::Mode m2; m2.id = 2; m2.size = QSize(1280, 1024); m2.refreshRate = 90000; outputDeviceInterface->addMode(m2); OutputDeviceInterface::Mode m3; m3.id = 3; m3.size = QSize(1920, 1080); m3.flags = OutputDeviceInterface::ModeFlags(); m3.refreshRate = 100000; outputDeviceInterface->addMode(m3); m_modes << m0 << m1 << m2 << m3; outputDeviceInterface->setCurrentMode(1); outputDeviceInterface->setGlobalPosition(QPoint(0, 1920)); outputDeviceInterface->create(); m_serverOutputs << outputDeviceInterface; m_outputManagementInterface = m_display->createOutputManagement(this); m_outputManagementInterface->create(); QVERIFY(m_outputManagementInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); m_announcedSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputManagementAnnounced); m_omSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputDeviceAnnounced); QVERIFY(m_announcedSpy->isValid()); QVERIFY(m_omSpy->isValid()); m_registry->create(m_connection->display()); QVERIFY(m_registry->isValid()); m_registry->setEventQueue(m_queue); m_registry->setup(); wl_display_flush(m_connection->display()); QVERIFY(m_announcedSpy->wait()); QCOMPARE(m_announcedSpy->count(), 1); m_outputManagement = m_registry->createOutputManagement(m_announcedSpy->first().first().value(), m_announcedSpy->first().last().value()); createOutputDevices(); } void TestWaylandOutputManagement::cleanup() { if (m_outputConfiguration) { delete m_outputConfiguration; m_outputConfiguration = nullptr; } if (m_outputManagement) { delete m_outputManagement; m_outputManagement = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } if (m_outputManagementInterface) { delete m_outputManagementInterface; m_outputManagementInterface = nullptr; } delete m_display; m_display = nullptr; m_serverOutputs.clear(); m_clientOutputs.clear(); } void TestWaylandOutputManagement::applyPendingChanges(KWayland::Server::OutputConfigurationInterface *configurationInterface) { auto changes = configurationInterface->changes(); for (auto outputdevice: changes.keys()) { auto c = changes[outputdevice]; if (c->enabledChanged()) { outputdevice->setEnabled(c->enabled()); } if (c->modeChanged()) { outputdevice->setCurrentMode(c->mode()); } if (c->transformChanged()) { outputdevice->setTransform(c->transform()); } if (c->positionChanged()) { outputdevice->setGlobalPosition(c->position()); } if (c->scaleChanged()) { outputdevice->setScaleF(c->scaleF()); } if (c->colorCurvesChanged()) { outputdevice->setColorCurves(c->colorCurves()); } } } void TestWaylandOutputManagement::createOutputDevices() { QCOMPARE(m_omSpy->count(), 1); QCOMPARE(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputDevice).count(), m_serverOutputs.count()); auto output = new KWayland::Client::OutputDevice(); QVERIFY(!output->isValid()); QCOMPARE(output->geometry(), QRect()); QCOMPARE(output->globalPosition(), QPoint()); QCOMPARE(output->manufacturer(), QString()); QCOMPARE(output->model(), QString()); QCOMPARE(output->physicalSize(), QSize()); QCOMPARE(output->pixelSize(), QSize()); QCOMPARE(output->refreshRate(), 0); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output->scale(), 1); #endif QCOMPARE(output->scaleF(), 1.0); QCOMPARE(output->colorCurves().red, QVector()); QCOMPARE(output->colorCurves().green, QVector()); QCOMPARE(output->colorCurves().blue, QVector()); QCOMPARE(output->subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); QCOMPARE(output->transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output->edid(), QByteArray()); QCOMPARE(output->uuid(), QByteArray()); QSignalSpy outputChanged(output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChanged.isValid()); output->setup(m_registry->bindOutputDevice(m_omSpy->first().first().value(), m_omSpy->first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output->globalPosition(), QPoint(0, 1920)); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); m_clientOutputs << output; m_outputDevice = output; QVERIFY(m_outputManagement->isValid()); } void TestWaylandOutputManagement::testBasicMemoryManagement() { createConfig(); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); KWayland::Server::OutputConfigurationInterface *configurationInterface = nullptr; connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=, &configurationInterface](KWayland::Server::OutputConfigurationInterface *c) { configurationInterface = c; }); m_outputConfiguration->apply(); QVERIFY(serverApplySpy.wait()); QVERIFY(configurationInterface); QSignalSpy interfaceDeletedSpy(configurationInterface, &QObject::destroyed); delete m_outputConfiguration; m_outputConfiguration = nullptr; QVERIFY(interfaceDeletedSpy.wait()); } void TestWaylandOutputManagement::testRemoval() { QSignalSpy outputManagementRemovedSpy(m_registry, &KWayland::Client::Registry::outputManagementRemoved); QVERIFY(outputManagementRemovedSpy.isValid()); delete m_outputManagementInterface; m_outputManagementInterface = nullptr; QVERIFY(outputManagementRemovedSpy.wait(200)); QCOMPARE(outputManagementRemovedSpy.first().first(), m_announcedSpy->first().first()); QVERIFY(!m_registry->hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); } void TestWaylandOutputManagement::createConfig() { m_outputConfiguration = m_outputManagement->createConfiguration(); } void TestWaylandOutputManagement::testApplied() { createConfig(); QVERIFY(m_outputConfiguration->isValid()); QSignalSpy appliedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *configurationInterface) { configurationInterface->setApplied(); }); m_outputConfiguration->apply(); QVERIFY(appliedSpy.wait(200)); QCOMPARE(appliedSpy.count(), 1); } void TestWaylandOutputManagement::testFailed() { createConfig(); QVERIFY(m_outputConfiguration->isValid()); QSignalSpy failedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::failed); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *configurationInterface) { configurationInterface->setFailed(); }); m_outputConfiguration->apply(); QVERIFY(failedSpy.wait(200)); QCOMPARE(failedSpy.count(), 1); } void TestWaylandOutputManagement::testEnable() { createConfig(); auto config = m_outputConfiguration; QVERIFY(config->isValid()); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); QSignalSpy enabledChanged(output, &KWayland::Client::OutputDevice::enabledChanged); QVERIFY(enabledChanged.isValid()); config->setEnabled(output, OutputDevice::Enablement::Disabled); QVERIFY(!enabledChanged.wait(200)); QCOMPARE(enabledChanged.count(), 0); // Reset config->setEnabled(output, OutputDevice::Enablement::Disabled); config->apply(); } void TestWaylandOutputManagement::testMultipleSettings() { createConfig(); auto config = m_outputConfiguration; QVERIFY(config->isValid()); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); KWayland::Server::OutputConfigurationInterface *configurationInterface; connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=, &configurationInterface](KWayland::Server::OutputConfigurationInterface *c) { applyPendingChanges(c); configurationInterface = c; }); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); QVERIFY(serverApplySpy.isValid()); config->setMode(output, m_modes.first().id); config->setTransform(output, OutputDevice::Transform::Rotated90); config->setPosition(output, QPoint(13, 37)); config->setScaleF(output, 2.0); const auto zeroVector = QVector(256, 0); config->setColorCurves(output, zeroVector, zeroVector, zeroVector); config->setEnabled(output, OutputDevice::Enablement::Disabled); config->apply(); QVERIFY(serverApplySpy.wait(200)); QCOMPARE(serverApplySpy.count(), 1); configurationInterface->setApplied(); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); QCOMPARE(configAppliedSpy.count(), 1); QCOMPARE(outputChangedSpy.count(), 6); config->setMode(output, m_modes.at(1).id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(0, 1920)); config->setScaleF(output, 1.0); const auto oneVector = QVector(256, 1); config->setColorCurves(output, oneVector, oneVector, oneVector); config->setEnabled(output, OutputDevice::Enablement::Enabled); config->apply(); QVERIFY(serverApplySpy.wait(200)); QCOMPARE(serverApplySpy.count(), 2); configurationInterface->setApplied(); QVERIFY(configAppliedSpy.wait(200)); QCOMPARE(configAppliedSpy.count(), 2); QCOMPARE(outputChangedSpy.count(), 12); } void TestWaylandOutputManagement::testConfigFailed() { createConfig(); auto config = m_outputConfiguration; auto s_o = m_serverOutputs.first(); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QVERIFY(config->isValid()); QVERIFY(s_o->isValid()); QVERIFY(output->isValid()); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); QVERIFY(serverApplySpy.isValid()); QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChangedSpy.isValid()); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); QVERIFY(configAppliedSpy.isValid()); QSignalSpy configFailedSpy(config, &KWayland::Client::OutputConfiguration::failed); QVERIFY(configFailedSpy.isValid()); config->setMode(output, m_modes.last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(-1, -1)); config->apply(); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { c->setFailed(); }); QVERIFY(serverApplySpy.wait(200)); // Artificially make the server fail to apply the settings // Make sure the applied signal never comes, and that failed has been received QVERIFY(!configAppliedSpy.wait(200)); QCOMPARE(configFailedSpy.count(), 1); QCOMPARE(configAppliedSpy.count(), 0); } void TestWaylandOutputManagement::testExampleConfig() { createConfig(); auto config = m_outputConfiguration; KWayland::Client::OutputDevice *output = m_clientOutputs.first(); config->setMode(output, m_clientOutputs.first()->modes().last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(-1, -1)); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { c->setApplied(); }); config->apply(); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); } void TestWaylandOutputManagement::testScale() { createConfig(); auto config = m_outputConfiguration; KWayland::Client::OutputDevice *output = m_clientOutputs.first(); config->setScaleF(output, 2.3); config->apply(); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { applyPendingChanges(c); c->setApplied(); }); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output->scale(), 2); //test backwards compatibility #endif QCOMPARE(wl_fixed_from_double(output->scaleF()), wl_fixed_from_double(2.3)); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) config->setScale(output, 3); config->apply(); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); //will be setApplied using the connect above QCOMPARE(output->scale(), 3); QCOMPARE(output->scaleF(), 3.0); //test forward compatibility #endif } QTEST_GUILESS_MAIN(TestWaylandOutputManagement) #include "test_wayland_outputmanagement.moc" diff --git a/autotests/client/test_wayland_region.cpp b/autotests/client/test_wayland_region.cpp index c223ed3..6c5d6fb 100644 --- a/autotests/client/test_wayland_region.cpp +++ b/autotests/client/test_wayland_region.cpp @@ -1,319 +1,305 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" class TestRegion : public QObject { Q_OBJECT public: explicit TestRegion(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testCreateWithRegion(); void testCreateUniquePtr(); void testAdd(); void testRemove(); void testDestroy(); void testDisconnect(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-region-0"); TestRegion::TestRegion(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestRegion::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); } void TestRegion::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestRegion::testCreate() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); QVERIFY(regionCreatedSpy.isValid()); QScopedPointer region(m_compositor->createRegion()); QCOMPARE(region->region(), QRegion()); QVERIFY(regionCreatedSpy.wait()); QCOMPARE(regionCreatedSpy.count(), 1); auto serverRegion = regionCreatedSpy.first().first().value(); QVERIFY(serverRegion); QCOMPARE(serverRegion->region(), QRegion()); QCOMPARE(serverRegion->global(), m_compositorInterface); } void TestRegion::testCreateWithRegion() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); QVERIFY(regionCreatedSpy.isValid()); QScopedPointer region(m_compositor->createRegion(QRegion(0, 0, 10, 20), nullptr)); QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); QVERIFY(regionCreatedSpy.wait()); QCOMPARE(regionCreatedSpy.count(), 1); auto serverRegion = regionCreatedSpy.first().first().value(); QVERIFY(serverRegion); QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); QVERIFY(serverRegion->parentResource()); } void TestRegion::testCreateUniquePtr() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); QVERIFY(regionCreatedSpy.isValid()); std::unique_ptr region(m_compositor->createRegion(QRegion(0, 0, 10, 20))); QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); QVERIFY(regionCreatedSpy.wait()); QCOMPARE(regionCreatedSpy.count(), 1); auto serverRegion = regionCreatedSpy.first().first().value(); QVERIFY(serverRegion); QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); } void TestRegion::testAdd() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); QVERIFY(regionCreatedSpy.isValid()); QScopedPointer region(m_compositor->createRegion()); QVERIFY(regionCreatedSpy.wait()); auto serverRegion = regionCreatedSpy.first().first().value(); QSignalSpy regionChangedSpy(serverRegion, SIGNAL(regionChanged(QRegion))); QVERIFY(regionChangedSpy.isValid()); // adding a QRect region->add(QRect(0, 0, 10, 20)); QCOMPARE(region->region(), QRegion(0, 0, 10, 20)); QVERIFY(regionChangedSpy.wait()); QCOMPARE(regionChangedSpy.count(), 1); QCOMPARE(regionChangedSpy.last().first().value(), QRegion(0, 0, 10, 20)); QCOMPARE(serverRegion->region(), QRegion(0, 0, 10, 20)); // adding a QRegion region->add(QRegion(5, 5, 10, 50)); QRegion compareRegion(0, 0, 10, 20); compareRegion = compareRegion.united(QRect(5, 5, 10, 50)); QCOMPARE(region->region(), compareRegion); QVERIFY(regionChangedSpy.wait()); QCOMPARE(regionChangedSpy.count(), 2); QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); QCOMPARE(serverRegion->region(), compareRegion); } void TestRegion::testRemove() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy regionCreatedSpy(m_compositorInterface, SIGNAL(regionCreated(KWayland::Server::RegionInterface*))); QVERIFY(regionCreatedSpy.isValid()); std::unique_ptr region(m_compositor->createRegion(QRegion(0, 0, 100, 200))); QVERIFY(regionCreatedSpy.wait()); auto serverRegion = regionCreatedSpy.first().first().value(); QSignalSpy regionChangedSpy(serverRegion, SIGNAL(regionChanged(QRegion))); QVERIFY(regionChangedSpy.isValid()); // subtract a QRect region->subtract(QRect(0, 0, 10, 20)); QRegion compareRegion(0, 0, 100, 200); compareRegion = compareRegion.subtracted(QRect(0, 0, 10, 20)); QCOMPARE(region->region(), compareRegion); QVERIFY(regionChangedSpy.wait()); QCOMPARE(regionChangedSpy.count(), 1); QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); QCOMPARE(serverRegion->region(), compareRegion); // subtracting a QRegion region->subtract(QRegion(5, 5, 10, 50)); compareRegion = compareRegion.subtracted(QRect(5, 5, 10, 50)); QCOMPARE(region->region(), compareRegion); QVERIFY(regionChangedSpy.wait()); QCOMPARE(regionChangedSpy.count(), 2); QCOMPARE(regionChangedSpy.last().first().value(), compareRegion); QCOMPARE(serverRegion->region(), compareRegion); } void TestRegion::testDestroy() { using namespace KWayland::Client; QScopedPointer region(m_compositor->createRegion()); connect(m_connection, &ConnectionThread::connectionDied, region.data(), &Region::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(region->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the region should be destroyed; QVERIFY(!region->isValid()); // calling destroy again should not fail region->destroy(); } void TestRegion::testDisconnect() { // this test verifies that the server side correctly tears down the resources when the client disconnects using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer r(m_compositor->createRegion()); QVERIFY(!r.isNull()); QVERIFY(r->isValid()); QSignalSpy regionCreatedSpy(m_compositorInterface, &CompositorInterface::regionCreated); QVERIFY(regionCreatedSpy.isValid()); QVERIFY(regionCreatedSpy.wait()); auto serverRegion = regionCreatedSpy.first().first().value(); // destroy client QSignalSpy clientDisconnectedSpy(serverRegion->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy regionDestroyedSpy(serverRegion, &QObject::destroyed); QVERIFY(regionDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(regionDestroyedSpy.count(), 0); QVERIFY(regionDestroyedSpy.wait()); QCOMPARE(regionDestroyedSpy.count(), 1); r->destroy(); m_compositor->destroy(); m_queue->destroy(); } QTEST_GUILESS_MAIN(TestRegion) #include "test_wayland_region.moc" diff --git a/autotests/client/test_wayland_registry.cpp b/autotests/client/test_wayland_registry.cpp index 079c38c..789274a 100644 --- a/autotests/client/test_wayland_registry.cpp +++ b/autotests/client/test_wayland_registry.cpp @@ -1,881 +1,867 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/blur.h" #include "../../src/client/contrast.h" #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/dpms.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/output.h" #include "../../src/client/pointerconstraints.h" #include "../../src/client/pointergestures.h" #include "../../src/client/idleinhibit.h" #include "../../src/client/seat.h" #include "../../src/client/relativepointer.h" #include "../../src/client/server_decoration.h" #include "../../src/client/shell.h" #include "../../src/client/surface.h" #include "../../src/client/subcompositor.h" #include "../../src/client/xdgshell.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/dpms_interface.h" #include "../../src/server/idleinhibit_interface.h" #include "../../src/server/output_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" #include "../../src/server/blur_interface.h" #include "../../src/server/contrast_interface.h" #include "../../src/server/server_decoration_interface.h" #include "../../src/server/slide_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/outputmanagement_interface.h" #include "../../src/server/outputdevice_interface.h" #include "../../src/server/pointerconstraints_interface.h" #include "../../src/server/pointergestures_interface.h" #include "../../src/server/textinput_interface.h" #include "../../src/server/xdgshell_interface.h" #include "../../src/server/relativepointer_interface.h" // Wayland #include #include #include #include #include #include #include #include #include #include "../../src/compat/wayland-xdg-shell-v5-client-protocol.h" class TestWaylandRegistry : public QObject { Q_OBJECT public: explicit TestWaylandRegistry(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testBindCompositor(); void testBindShell(); void testBindOutput(); void testBindShm(); void testBindSeat(); void testBindSubCompositor(); void testBindDataDeviceManager(); void testBindBlurManager(); void testBindContrastManager(); void testBindSlideManager(); void testBindDpmsManager(); void testBindServerSideDecorationManager(); void testBindTextInputManagerUnstableV0(); void testBindTextInputManagerUnstableV2(); void testBindXdgShellUnstableV5(); void testBindRelativePointerManagerUnstableV1(); void testBindPointerGesturesUnstableV1(); void testBindPointerConstraintsUnstableV1(); void testBindIdleIhibitManagerUnstableV1(); void testGlobalSync(); void testGlobalSyncThreaded(); void testRemoval(); void testOutOfSyncRemoval(); void testDestroy(); void testAnnounceMultiple(); void testAnnounceMultipleOutputDevices(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositor; KWayland::Server::OutputInterface *m_output; KWayland::Server::OutputDeviceInterface *m_outputDevice; KWayland::Server::SeatInterface *m_seat; KWayland::Server::ShellInterface *m_shell; KWayland::Server::SubCompositorInterface *m_subcompositor; KWayland::Server::DataDeviceManagerInterface *m_dataDeviceManager; KWayland::Server::OutputManagementInterface *m_outputManagement; KWayland::Server::ServerSideDecorationManagerInterface *m_serverSideDecorationManager; KWayland::Server::TextInputManagerInterface *m_textInputManagerV0; KWayland::Server::TextInputManagerInterface *m_textInputManagerV2; KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5; KWayland::Server::RelativePointerManagerInterface *m_relativePointerV1; KWayland::Server::PointerGesturesInterface *m_pointerGesturesV1; KWayland::Server::PointerConstraintsInterface *m_pointerConstraintsV1; KWayland::Server::BlurManagerInterface *m_blur; KWayland::Server::ContrastManagerInterface *m_contrast; KWayland::Server::IdleInhibitManagerInterface *m_idleInhibit; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0"); TestWaylandRegistry::TestWaylandRegistry(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositor(nullptr) , m_output(nullptr) , m_outputDevice(nullptr) , m_seat(nullptr) , m_shell(nullptr) , m_subcompositor(nullptr) , m_dataDeviceManager(nullptr) , m_outputManagement(nullptr) , m_serverSideDecorationManager(nullptr) , m_textInputManagerV0(nullptr) , m_textInputManagerV2(nullptr) , m_xdgShellUnstableV5(nullptr) , m_relativePointerV1(nullptr) , m_pointerGesturesV1(nullptr) , m_pointerConstraintsV1(nullptr) , m_blur(nullptr) , m_contrast(nullptr) , m_idleInhibit(nullptr) { } void TestWaylandRegistry::init() { m_display = new KWayland::Server::Display(); m_display->setSocketName(s_socketName); m_display->start(); m_display->createShm(); m_compositor = m_display->createCompositor(); m_compositor->create(); m_output = m_display->createOutput(); m_output->create(); m_seat = m_display->createSeat(); m_seat->create(); m_shell = m_display->createShell(); m_shell->create(); m_subcompositor = m_display->createSubCompositor(); m_subcompositor->create(); m_dataDeviceManager = m_display->createDataDeviceManager(); m_dataDeviceManager->create(); m_outputManagement = m_display->createOutputManagement(); m_outputManagement->create(); m_outputDevice = m_display->createOutputDevice(); m_outputDevice->create(); QVERIFY(m_outputManagement->isValid()); m_blur = m_display->createBlurManager(this); m_blur->create(); m_contrast = m_display->createContrastManager(this); m_contrast->create(); m_display->createSlideManager(this)->create(); m_display->createDpmsManager()->create(); m_serverSideDecorationManager = m_display->createServerSideDecorationManager(); m_serverSideDecorationManager->create(); m_textInputManagerV0 = m_display->createTextInputManager(KWayland::Server::TextInputInterfaceVersion::UnstableV0); QCOMPARE(m_textInputManagerV0->interfaceVersion(), KWayland::Server::TextInputInterfaceVersion::UnstableV0); m_textInputManagerV0->create(); m_textInputManagerV2 = m_display->createTextInputManager(KWayland::Server::TextInputInterfaceVersion::UnstableV2); QCOMPARE(m_textInputManagerV2->interfaceVersion(), KWayland::Server::TextInputInterfaceVersion::UnstableV2); m_textInputManagerV2->create(); m_xdgShellUnstableV5 = m_display->createXdgShell(KWayland::Server::XdgShellInterfaceVersion::UnstableV5); m_xdgShellUnstableV5->create(); QCOMPARE(m_xdgShellUnstableV5->interfaceVersion(), KWayland::Server::XdgShellInterfaceVersion::UnstableV5); m_relativePointerV1 = m_display->createRelativePointerManager(KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); m_relativePointerV1->create(); QCOMPARE(m_relativePointerV1->interfaceVersion(), KWayland::Server::RelativePointerInterfaceVersion::UnstableV1); m_pointerGesturesV1 = m_display->createPointerGestures(KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); m_pointerGesturesV1->create(); QCOMPARE(m_pointerGesturesV1->interfaceVersion(), KWayland::Server::PointerGesturesInterfaceVersion::UnstableV1); m_pointerConstraintsV1 = m_display->createPointerConstraints(KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1); m_pointerConstraintsV1->create(); QCOMPARE(m_pointerConstraintsV1->interfaceVersion(), KWayland::Server::PointerConstraintsInterfaceVersion::UnstableV1); m_idleInhibit = m_display->createIdleInhibitManager(KWayland::Server::IdleInhibitManagerInterfaceVersion::UnstableV1); m_idleInhibit->create(); QCOMPARE(m_idleInhibit->interfaceVersion(), KWayland::Server::IdleInhibitManagerInterfaceVersion::UnstableV1); } void TestWaylandRegistry::cleanup() { delete m_display; m_display = nullptr; } void TestWaylandRegistry::testCreate() { KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); KWayland::Client::Registry registry; QVERIFY(!registry.isValid()); registry.create(connection.display()); QVERIFY(registry.isValid()); registry.release(); QVERIFY(!registry.isValid()); } #define TEST_BIND(iface, signalName, bindMethod, destroyFunction) \ KWayland::Client::ConnectionThread connection; \ QSignalSpy connectedSpy(&connection, SIGNAL(connected())); \ connection.setSocketName(s_socketName); \ connection.initConnection(); \ QVERIFY(connectedSpy.wait()); \ \ KWayland::Client::Registry registry; \ /* before registry is created, we cannot bind the interface*/ \ QVERIFY(!registry.bindMethod(0, 0)); \ \ QVERIFY(!registry.isValid()); \ registry.create(&connection); \ QVERIFY(registry.isValid()); \ /* created but not yet connected still results in no bind */ \ QVERIFY(!registry.bindMethod(0, 0)); \ /* interface information should be empty */ \ QVERIFY(registry.interfaces(iface).isEmpty()); \ QCOMPARE(registry.interface(iface).name, 0u); \ QCOMPARE(registry.interface(iface).version, 0u); \ \ /* now let's register */ \ QSignalSpy announced(®istry, signalName); \ QVERIFY(announced.isValid()); \ registry.setup(); \ wl_display_flush(connection.display()); \ QVERIFY(announced.wait()); \ const quint32 name = announced.first().first().value(); \ const quint32 version = announced.first().last().value(); \ QCOMPARE(registry.interfaces(iface).count(), 1); \ QCOMPARE(registry.interfaces(iface).first().name, name); \ QCOMPARE(registry.interfaces(iface).first().version, version); \ QCOMPARE(registry.interface(iface).name, name); \ QCOMPARE(registry.interface(iface).version, version); \ \ /* registry should know about the interface now */ \ QVERIFY(registry.hasInterface(iface)); \ QVERIFY(!registry.bindMethod(name+1, version)); \ QVERIFY(registry.bindMethod(name, version+1)); \ auto *c = registry.bindMethod(name, version); \ QVERIFY(c); \ destroyFunction(c); \ void TestWaylandRegistry::testBindCompositor() { TEST_BIND(KWayland::Client::Registry::Interface::Compositor, SIGNAL(compositorAnnounced(quint32,quint32)), bindCompositor, wl_compositor_destroy) } void TestWaylandRegistry::testBindShell() { TEST_BIND(KWayland::Client::Registry::Interface::Shell, SIGNAL(shellAnnounced(quint32,quint32)), bindShell, free) } void TestWaylandRegistry::testBindOutput() { TEST_BIND(KWayland::Client::Registry::Interface::Output, SIGNAL(outputAnnounced(quint32,quint32)), bindOutput, wl_output_destroy) } void TestWaylandRegistry::testBindSeat() { TEST_BIND(KWayland::Client::Registry::Interface::Seat, SIGNAL(seatAnnounced(quint32,quint32)), bindSeat, wl_seat_destroy) } void TestWaylandRegistry::testBindShm() { TEST_BIND(KWayland::Client::Registry::Interface::Shm, SIGNAL(shmAnnounced(quint32,quint32)), bindShm, wl_shm_destroy) } void TestWaylandRegistry::testBindSubCompositor() { TEST_BIND(KWayland::Client::Registry::Interface::SubCompositor, SIGNAL(subCompositorAnnounced(quint32,quint32)), bindSubCompositor, wl_subcompositor_destroy) } void TestWaylandRegistry::testBindDataDeviceManager() { TEST_BIND(KWayland::Client::Registry::Interface::DataDeviceManager, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32)), bindDataDeviceManager, wl_data_device_manager_destroy) } void TestWaylandRegistry::testBindBlurManager() { TEST_BIND(KWayland::Client::Registry::Interface::Blur, SIGNAL(blurAnnounced(quint32,quint32)), bindBlurManager, free) } void TestWaylandRegistry::testBindContrastManager() { TEST_BIND(KWayland::Client::Registry::Interface::Contrast, SIGNAL(contrastAnnounced(quint32,quint32)), bindContrastManager, free) } void TestWaylandRegistry::testBindSlideManager() { TEST_BIND(KWayland::Client::Registry::Interface::Slide, SIGNAL(slideAnnounced(quint32,quint32)), bindSlideManager, free) } void TestWaylandRegistry::testBindDpmsManager() { TEST_BIND(KWayland::Client::Registry::Interface::Dpms, SIGNAL(dpmsAnnounced(quint32,quint32)), bindDpmsManager, org_kde_kwin_dpms_manager_destroy) } void TestWaylandRegistry::testBindServerSideDecorationManager() { TEST_BIND(KWayland::Client::Registry::Interface::ServerSideDecorationManager, SIGNAL(serverSideDecorationManagerAnnounced(quint32,quint32)), bindServerSideDecorationManager, org_kde_kwin_server_decoration_manager_destroy) } void TestWaylandRegistry::testBindTextInputManagerUnstableV0() { TEST_BIND(KWayland::Client::Registry::Interface::TextInputManagerUnstableV0, SIGNAL(textInputManagerUnstableV0Announced(quint32,quint32)), bindTextInputManagerUnstableV0, wl_text_input_manager_destroy) } void TestWaylandRegistry::testBindTextInputManagerUnstableV2() { TEST_BIND(KWayland::Client::Registry::Interface::TextInputManagerUnstableV2, SIGNAL(textInputManagerUnstableV2Announced(quint32,quint32)), bindTextInputManagerUnstableV2, zwp_text_input_manager_v2_destroy) } void TestWaylandRegistry::testBindXdgShellUnstableV5() { TEST_BIND(KWayland::Client::Registry::Interface::XdgShellUnstableV5, SIGNAL(xdgShellUnstableV5Announced(quint32,quint32)), bindXdgShellUnstableV5, zxdg_shell_v5_destroy) } void TestWaylandRegistry::testBindRelativePointerManagerUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1, SIGNAL(relativePointerManagerUnstableV1Announced(quint32,quint32)), bindRelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1_destroy) } void TestWaylandRegistry::testBindPointerGesturesUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1, SIGNAL(pointerGesturesUnstableV1Announced(quint32,quint32)), bindPointerGesturesUnstableV1, zwp_pointer_gestures_v1_destroy) } void TestWaylandRegistry::testBindPointerConstraintsUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::PointerConstraintsUnstableV1, SIGNAL(pointerConstraintsUnstableV1Announced(quint32,quint32)), bindPointerConstraintsUnstableV1, zwp_pointer_constraints_v1_destroy) } void TestWaylandRegistry::testBindIdleIhibitManagerUnstableV1() { TEST_BIND(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1, SIGNAL(idleInhibitManagerUnstableV1Announced(quint32,quint32)), bindIdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1_destroy) } #undef TEST_BIND void TestWaylandRegistry::testRemoval() { using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); KWayland::Client::Registry registry; QSignalSpy shmAnnouncedSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); QVERIFY(shmAnnouncedSpy.isValid()); QSignalSpy compositorAnnouncedSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorAnnouncedSpy.isValid()); QSignalSpy outputAnnouncedSpy(®istry, SIGNAL(outputAnnounced(quint32,quint32))); QVERIFY(outputAnnouncedSpy.isValid()); QSignalSpy outputDeviceAnnouncedSpy(®istry, SIGNAL(outputDeviceAnnounced(quint32,quint32))); QVERIFY(outputDeviceAnnouncedSpy.isValid()); QSignalSpy shellAnnouncedSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QVERIFY(shellAnnouncedSpy.isValid()); QSignalSpy seatAnnouncedSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QVERIFY(seatAnnouncedSpy.isValid()); QSignalSpy subCompositorAnnouncedSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32))); QVERIFY(subCompositorAnnouncedSpy.isValid()); QSignalSpy outputManagementAnnouncedSpy(®istry, SIGNAL(outputManagementAnnounced(quint32,quint32))); QVERIFY(outputManagementAnnouncedSpy.isValid()); QSignalSpy serverSideDecorationManagerAnnouncedSpy(®istry, &Registry::serverSideDecorationManagerAnnounced); QVERIFY(serverSideDecorationManagerAnnouncedSpy.isValid()); QSignalSpy blurAnnouncedSpy(®istry, &Registry::blurAnnounced); QVERIFY(blurAnnouncedSpy.isValid()); QSignalSpy idleInhibitManagerUnstableV1AnnouncedSpy(®istry, &Registry::idleInhibitManagerUnstableV1Announced); QVERIFY(idleInhibitManagerUnstableV1AnnouncedSpy.isValid()); QVERIFY(!registry.isValid()); registry.create(connection.display()); registry.setup(); QVERIFY(shmAnnouncedSpy.wait()); QVERIFY(!compositorAnnouncedSpy.isEmpty()); QVERIFY(!outputAnnouncedSpy.isEmpty()); QVERIFY(!outputDeviceAnnouncedSpy.isEmpty()); QVERIFY(!shellAnnouncedSpy.isEmpty()); QVERIFY(!seatAnnouncedSpy.isEmpty()); QVERIFY(!subCompositorAnnouncedSpy.isEmpty()); QVERIFY(!outputManagementAnnouncedSpy.isEmpty()); QVERIFY(!serverSideDecorationManagerAnnouncedSpy.isEmpty()); QVERIFY(!blurAnnouncedSpy.isEmpty()); QVERIFY(!idleInhibitManagerUnstableV1AnnouncedSpy.isEmpty()); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Compositor)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Output)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::OutputDevice)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Seat)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shell)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Shm)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor)); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::FullscreenShell)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::ServerSideDecorationManager)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::Blur)); QVERIFY(registry.hasInterface(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1)); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Compositor).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Output).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::OutputDevice).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Seat).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Shell).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Shm).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::SubCompositor).isEmpty()); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::FullscreenShell).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::ServerSideDecorationManager).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::Blur).isEmpty()); QVERIFY(!registry.interfaces(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).isEmpty()); QSignalSpy seatRemovedSpy(®istry, SIGNAL(seatRemoved(quint32))); QVERIFY(seatRemovedSpy.isValid()); Seat *seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, ®istry); Shell *shell = registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version, ®istry); Output *output = registry.createOutput(registry.interface(Registry::Interface::Output).name, registry.interface(Registry::Interface::Output).version, ®istry); Compositor *compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, ®istry); SubCompositor *subcompositor = registry.createSubCompositor(registry.interface(Registry::Interface::SubCompositor).name, registry.interface(Registry::Interface::SubCompositor).version, ®istry); ServerSideDecorationManager *serverSideDeco = registry.createServerSideDecorationManager(registry.interface(Registry::Interface::ServerSideDecorationManager).name, registry.interface(Registry::Interface::ServerSideDecorationManager).version, ®istry); BlurManager *blurManager = registry.createBlurManager(registry.interface(Registry::Interface::Blur).name, registry.interface(Registry::Interface::Blur).version, ®istry); auto idleInhibitManager = registry.createIdleInhibitManager(registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).name, registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).version, ®istry); connection.flush(); m_display->dispatchEvents(); QSignalSpy seatObjectRemovedSpy(seat, &Seat::removed); QVERIFY(seatObjectRemovedSpy.isValid()); QSignalSpy shellObjectRemovedSpy(shell, &Shell::removed); QVERIFY(shellObjectRemovedSpy.isValid()); QSignalSpy outputObjectRemovedSpy(output, &Output::removed); QVERIFY(outputObjectRemovedSpy.isValid()); QSignalSpy compositorObjectRemovedSpy(compositor, &Compositor::removed); QVERIFY(compositorObjectRemovedSpy.isValid()); QSignalSpy subcompositorObjectRemovedSpy(subcompositor, &SubCompositor::removed); QVERIFY(subcompositorObjectRemovedSpy.isValid()); QSignalSpy idleInhibitManagerObjectRemovedSpy(idleInhibitManager, &IdleInhibitManager::removed); QVERIFY(idleInhibitManagerObjectRemovedSpy.isValid()); delete m_seat; QVERIFY(seatRemovedSpy.wait()); QCOMPARE(seatRemovedSpy.first().first(), seatAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Seat)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Seat).isEmpty()); QCOMPARE(seatObjectRemovedSpy.count(), 1); QSignalSpy shellRemovedSpy(®istry, SIGNAL(shellRemoved(quint32))); QVERIFY(shellRemovedSpy.isValid()); delete m_shell; QVERIFY(shellRemovedSpy.wait()); QCOMPARE(shellRemovedSpy.first().first(), shellAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Shell)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Shell).isEmpty()); QCOMPARE(shellObjectRemovedSpy.count(), 1); QSignalSpy outputRemovedSpy(®istry, SIGNAL(outputRemoved(quint32))); QVERIFY(outputRemovedSpy.isValid()); delete m_output; QVERIFY(outputRemovedSpy.wait()); QCOMPARE(outputRemovedSpy.first().first(), outputAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Output)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Output).isEmpty()); QCOMPARE(outputObjectRemovedSpy.count(), 1); QSignalSpy outputDeviceRemovedSpy(®istry, SIGNAL(outputDeviceRemoved(quint32))); QVERIFY(outputDeviceRemovedSpy.isValid()); delete m_outputDevice; QVERIFY(outputDeviceRemovedSpy.wait()); QCOMPARE(outputDeviceRemovedSpy.first().first(), outputDeviceAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::OutputDevice)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::OutputDevice).isEmpty()); QSignalSpy compositorRemovedSpy(®istry, SIGNAL(compositorRemoved(quint32))); QVERIFY(compositorRemovedSpy.isValid()); delete m_compositor; QVERIFY(compositorRemovedSpy.wait()); QCOMPARE(compositorRemovedSpy.first().first(), compositorAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Compositor)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Compositor).isEmpty()); QCOMPARE(compositorObjectRemovedSpy.count(), 1); QSignalSpy subCompositorRemovedSpy(®istry, SIGNAL(subCompositorRemoved(quint32))); QVERIFY(subCompositorRemovedSpy.isValid()); delete m_subcompositor; QVERIFY(subCompositorRemovedSpy.wait()); QCOMPARE(subCompositorRemovedSpy.first().first(), subCompositorAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::SubCompositor)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::SubCompositor).isEmpty()); QCOMPARE(subcompositorObjectRemovedSpy.count(), 1); QSignalSpy outputManagementRemovedSpy(®istry, SIGNAL(outputManagementRemoved(quint32))); QVERIFY(outputManagementRemovedSpy.isValid()); delete m_outputManagement; QVERIFY(outputManagementRemovedSpy.wait()); QCOMPARE(outputManagementRemovedSpy.first().first(), outputManagementAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); QSignalSpy serverSideDecoManagerRemovedSpy(®istry, &Registry::serverSideDecorationManagerRemoved); QVERIFY(serverSideDecoManagerRemovedSpy.isValid()); QSignalSpy serverSideDecoManagerObjectRemovedSpy(serverSideDeco, &ServerSideDecorationManager::removed); QVERIFY(serverSideDecoManagerObjectRemovedSpy.isValid()); delete m_serverSideDecorationManager; QVERIFY(serverSideDecoManagerRemovedSpy.wait()); QCOMPARE(serverSideDecoManagerRemovedSpy.first().first(), serverSideDecorationManagerAnnouncedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::ServerSideDecorationManager)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::ServerSideDecorationManager).isEmpty()); QCOMPARE(serverSideDecoManagerObjectRemovedSpy.count(), 1); QSignalSpy blurRemovedSpy(®istry, &Registry::blurRemoved); QVERIFY(blurRemovedSpy.isValid()); QSignalSpy blurObjectRemovedSpy(blurManager, &BlurManager::removed); QVERIFY(blurObjectRemovedSpy.isValid()); delete m_blur; QVERIFY(blurRemovedSpy.wait()); QCOMPARE(blurRemovedSpy.first().first(), blurRemovedSpy.first().first()); QVERIFY(!registry.hasInterface(KWayland::Client::Registry::Interface::Blur)); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::Blur).isEmpty()); QCOMPARE(blurObjectRemovedSpy.count(), 1); QSignalSpy idleInhibitManagerUnstableV1RemovedSpy(®istry, &Registry::idleInhibitManagerUnstableV1Removed); QVERIFY(idleInhibitManagerUnstableV1RemovedSpy.isValid()); delete m_idleInhibit; QVERIFY(idleInhibitManagerUnstableV1RemovedSpy.wait()); QCOMPARE(idleInhibitManagerUnstableV1RemovedSpy.first().first(), idleInhibitManagerUnstableV1AnnouncedSpy.first().first()); QVERIFY(registry.interfaces(KWayland::Client::Registry::Interface::IdleInhibitManagerUnstableV1).isEmpty()); QCOMPARE(idleInhibitManagerObjectRemovedSpy.count(), 1); // cannot test shmRemoved as there is no functionality for it // verify everything has been removed only once QCOMPARE(seatObjectRemovedSpy.count(), 1); QCOMPARE(shellObjectRemovedSpy.count(), 1); QCOMPARE(outputObjectRemovedSpy.count(), 1); QCOMPARE(compositorObjectRemovedSpy.count(), 1); QCOMPARE(subcompositorObjectRemovedSpy.count(), 1); QCOMPARE(serverSideDecoManagerObjectRemovedSpy.count(), 1); QCOMPARE(blurObjectRemovedSpy.count(), 1); QCOMPARE(idleInhibitManagerObjectRemovedSpy.count(), 1); } void TestWaylandRegistry::testOutOfSyncRemoval() { //This tests the following sequence of events //server announces global //client binds to that global //server removes the global //(simultaneously) the client legimitely uses the bound resource to the global //client then gets the server events...it should no-op, not be a protocol error using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); KWayland::Client::Registry registry; QVERIFY(!registry.isValid()); registry.create(connection.display()); registry.setup(); QSignalSpy blurAnnouncedSpy(®istry, &Registry::blurAnnounced); QSignalSpy contrastAnnouncedSpy(®istry, &Registry::blurAnnounced); blurAnnouncedSpy.wait(); contrastAnnouncedSpy.wait(); BlurManager *blurManager = registry.createBlurManager(registry.interface(Registry::Interface::Blur).name, registry.interface(Registry::Interface::Blur).version, ®istry); ContrastManager *contrastManager = registry.createContrastManager(registry.interface(Registry::Interface::Contrast).name, registry.interface(Registry::Interface::Contrast).version, ®istry); connection.flush(); m_display->dispatchEvents(); QScopedPointer compositor(registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version)); QScopedPointer surface(compositor->createSurface()); QVERIFY(surface); //remove blur QSignalSpy blurRemovedSpy(®istry, &Registry::blurRemoved); delete m_blur; //client hasn't processed the event yet QVERIFY(blurRemovedSpy.count() == 0); //use the in the client blurManager->createBlur(surface.data(), nullptr); //now process events, QVERIFY(blurRemovedSpy.wait()); QVERIFY(blurRemovedSpy.count() == 1); //remove background contrast QSignalSpy contrastRemovedSpy(®istry, &Registry::contrastRemoved); delete m_contrast; //client hasn't processed the event yet QVERIFY(contrastRemovedSpy.count() == 0); //use the in the client contrastManager->createContrast(surface.data(), nullptr); //now process events, QVERIFY(contrastRemovedSpy.wait()); QVERIFY(contrastRemovedSpy.count() == 1); } void TestWaylandRegistry::testDestroy() { using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.setSocketName(s_socketName); connection.initConnection(); QVERIFY(connectedSpy.wait()); Registry registry; QSignalSpy shellAnnouncedSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QVERIFY(!registry.isValid()); registry.create(&connection); registry.setup(); QVERIFY(registry.isValid()); //create some arbitrary Interface shellAnnouncedSpy.wait(); QScopedPointer shell(registry.createShell(registry.interface(Registry::Interface::Shell).name, registry.interface(Registry::Interface::Shell).version, ®istry)); QSignalSpy connectionDiedSpy(&connection, SIGNAL(connectionDied())); QSignalSpy registryDiedSpy(®istry, SIGNAL(registryDestroyed())); QVERIFY(connectionDiedSpy.isValid()); QVERIFY(registryDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); QVERIFY(connectionDiedSpy.count() == 1); QVERIFY(registryDiedSpy.count() == 1); // now the registry should be destroyed; QVERIFY(!registry.isValid()); // calling destroy again should not fail registry.destroy(); shell->destroy(); } void TestWaylandRegistry::testGlobalSync() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.initConnection(); QVERIFY(connectedSpy.wait()); Registry registry; QSignalSpy syncSpy(®istry, SIGNAL(interfacesAnnounced())); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); registry.destroy(); } void TestWaylandRegistry::testGlobalSyncThreaded() { // More complex case, use a ConnectionThread, in a different Thread, // and our own EventQueue using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QThread thread; connection.moveToThread(&thread); thread.start(); QSignalSpy connectedSpy(&connection, SIGNAL(connected())); connection.initConnection(); QVERIFY(connectedSpy.wait()); EventQueue queue; queue.setup(&connection); Registry registry; QSignalSpy syncSpy(®istry, SIGNAL(interfacesAnnounced())); registry.setEventQueue(&queue); registry.create(&connection); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); registry.destroy(); thread.quit(); thread.wait(); } void TestWaylandRegistry::testAnnounceMultiple() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); Registry registry; QSignalSpy syncSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(syncSpy.isValid()); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); // we should have one output now QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 1); QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); QVERIFY(outputAnnouncedSpy.isValid()); m_display->createOutput()->create(); QVERIFY(outputAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.first().last().value()); auto output = m_display->createOutput(); output->create(); QVERIFY(outputAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 3); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.last().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.last().last().value()); QSignalSpy outputRemovedSpy(®istry, &Registry::outputRemoved); QVERIFY(outputRemovedSpy.isValid()); delete output; QVERIFY(outputRemovedSpy.wait()); QCOMPARE(outputRemovedSpy.first().first().value(), outputAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::Output).last().version, outputAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::Output).name, outputAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::Output).version, outputAnnouncedSpy.first().last().value()); } void TestWaylandRegistry::testAnnounceMultipleOutputDevices() { using namespace KWayland::Client; ConnectionThread connection; connection.setSocketName(s_socketName); QSignalSpy connectedSpy(&connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); connection.initConnection(); QVERIFY(connectedSpy.wait()); connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, &connection, [&connection] { wl_display_flush(connection.display()); } ); Registry registry; QSignalSpy syncSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(syncSpy.isValid()); // Most simple case: don't even use the ConnectionThread, // just its display. registry.create(connection.display()); registry.setup(); QVERIFY(syncSpy.wait()); QCOMPARE(syncSpy.count(), 1); // we should have one output now QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 1); QSignalSpy outputDeviceAnnouncedSpy(®istry, &Registry::outputDeviceAnnounced); QVERIFY(outputDeviceAnnouncedSpy.isValid()); m_display->createOutputDevice()->create(); QVERIFY(outputDeviceAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.first().last().value()); auto outputDevice = m_display->createOutputDevice(); outputDevice->create(); QVERIFY(outputDeviceAnnouncedSpy.wait()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 3); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.last().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.last().last().value()); QSignalSpy outputDeviceRemovedSpy(®istry, &Registry::outputDeviceRemoved); QVERIFY(outputDeviceRemovedSpy.isValid()); delete outputDevice; QVERIFY(outputDeviceRemovedSpy.wait()); QCOMPARE(outputDeviceRemovedSpy.first().first().value(), outputDeviceAnnouncedSpy.last().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).count(), 2); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interfaces(Registry::Interface::OutputDevice).last().version, outputDeviceAnnouncedSpy.first().last().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).name, outputDeviceAnnouncedSpy.first().first().value()); QCOMPARE(registry.interface(Registry::Interface::OutputDevice).version, outputDeviceAnnouncedSpy.first().last().value()); } QTEST_GUILESS_MAIN(TestWaylandRegistry) #include "test_wayland_registry.moc" diff --git a/autotests/client/test_wayland_seat.cpp b/autotests/client/test_wayland_seat.cpp index f12f30e..f40891f 100644 --- a/autotests/client/test_wayland_seat.cpp +++ b/autotests/client/test_wayland_seat.cpp @@ -1,2410 +1,2396 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/datadevice.h" #include "../../src/client/datadevicemanager.h" #include "../../src/client/datasource.h" #include "../../src/client/event_queue.h" #include "../../src/client/keyboard.h" #include "../../src/client/pointer.h" #include "../../src/client/pointergestures.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/relativepointer.h" #include "../../src/client/seat.h" #include "../../src/client/shm_pool.h" #include "../../src/client/subcompositor.h" #include "../../src/client/subsurface.h" #include "../../src/client/touch.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/datadevicemanager_interface.h" #include "../../src/server/display.h" #include "../../src/server/keyboard_interface.h" #include "../../src/server/pointer_interface.h" #include "../../src/server/pointergestures_interface.h" #include "../../src/server/relativepointer_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include #include // System #include #include class TestWaylandSeat : public QObject { Q_OBJECT public: explicit TestWaylandSeat(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testName(); void testCapabilities_data(); void testCapabilities(); void testPointer(); void testPointerTransformation_data(); void testPointerTransformation(); void testPointerButton_data(); void testPointerButton(); void testPointerSubSurfaceTree(); void testPointerSwipeGesture_data(); void testPointerSwipeGesture(); void testPointerPinchGesture_data(); void testPointerPinchGesture(); void testPointerAxis(); void testKeyboardSubSurfaceTreeFromPointer(); void testCursor(); void testCursorDamage(); void testKeyboard(); void testCast(); void testDestroy(); void testSelection(); void testSelectionNoDataSource(); void testDataDeviceForKeyboardSurface(); void testTouch(); void testDisconnect(); void testPointerEnterOnUnboundSurface(); // TODO: add test for keymap private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::SeatInterface *m_seatInterface; KWayland::Server::SubCompositorInterface *m_subCompositorInterface; KWayland::Server::RelativePointerManagerInterface *m_relativePointerManagerInterface; KWayland::Server::PointerGesturesInterface *m_pointerGesturesInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::Seat *m_seat; KWayland::Client::ShmPool *m_shm; KWayland::Client::SubCompositor * m_subCompositor; KWayland::Client::RelativePointerManager *m_relativePointerManager; KWayland::Client::PointerGestures *m_pointerGestures; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-seat-0"); TestWaylandSeat::TestWaylandSeat(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_seatInterface(nullptr) , m_subCompositorInterface(nullptr) , m_relativePointerManagerInterface(nullptr) , m_pointerGesturesInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_seat(nullptr) , m_shm(nullptr) , m_subCompositor(nullptr) , m_relativePointerManager(nullptr) , m_pointerGestures(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandSeat::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_subCompositorInterface = m_display->createSubCompositor(m_display); QVERIFY(m_subCompositorInterface); m_subCompositorInterface->create(); QVERIFY(m_subCompositorInterface->isValid()); m_relativePointerManagerInterface = m_display->createRelativePointerManager(RelativePointerInterfaceVersion::UnstableV1, m_display); QVERIFY(m_relativePointerManagerInterface); m_relativePointerManagerInterface->create(); QVERIFY(m_relativePointerManagerInterface->isValid()); m_pointerGesturesInterface = m_display->createPointerGestures(PointerGesturesInterfaceVersion::UnstableV1, m_display); QVERIFY(m_pointerGesturesInterface); m_pointerGesturesInterface->create(); QVERIFY(m_pointerGesturesInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); m_queue->setup(m_connection); KWayland::Client::Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(compositorSpy.wait()); m_seatInterface = m_display->createSeat(); QVERIFY(m_seatInterface); m_seatInterface->setName(QStringLiteral("seat0")); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); QVERIFY(seatSpy.wait()); m_compositor = new KWayland::Client::Compositor(this); m_compositor->setup(registry.bindCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value())); QVERIFY(m_compositor->isValid()); m_seat = registry.createSeat(seatSpy.first().first().value(), seatSpy.first().last().value(), this); QSignalSpy nameSpy(m_seat, SIGNAL(nameChanged(QString))); QVERIFY(nameSpy.wait()); m_shm = new KWayland::Client::ShmPool(this); m_shm->setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); QVERIFY(m_shm->isValid()); m_subCompositor = registry.createSubCompositor(registry.interface(KWayland::Client::Registry::Interface::SubCompositor).name, registry.interface(KWayland::Client::Registry::Interface::SubCompositor).version, this); QVERIFY(m_subCompositor->isValid()); m_relativePointerManager = registry.createRelativePointerManager(registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).name, registry.interface(KWayland::Client::Registry::Interface::RelativePointerManagerUnstableV1).version, this); QVERIFY(m_relativePointerManager->isValid()); m_pointerGestures = registry.createPointerGestures(registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).name, registry.interface(KWayland::Client::Registry::Interface::PointerGesturesUnstableV1).version, this); QVERIFY(m_pointerGestures->isValid()); } void TestWaylandSeat::cleanup() { if (m_pointerGestures) { delete m_pointerGestures; m_pointerGestures = nullptr; } if (m_relativePointerManager) { delete m_relativePointerManager; m_relativePointerManager = nullptr; } if (m_subCompositor) { delete m_subCompositor; m_subCompositor = nullptr; } if (m_shm) { delete m_shm; m_shm = nullptr; } if (m_seat) { delete m_seat; m_seat = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_compositorInterface; m_compositorInterface = nullptr; delete m_seatInterface; m_seatInterface = nullptr; delete m_subCompositorInterface; m_subCompositorInterface = nullptr; delete m_relativePointerManagerInterface; m_relativePointerManagerInterface = nullptr; delete m_pointerGesturesInterface; m_pointerGesturesInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandSeat::testName() { // no name set yet QCOMPARE(m_seat->name(), QStringLiteral("seat0")); QSignalSpy spy(m_seat, SIGNAL(nameChanged(QString))); QVERIFY(spy.isValid()); const QString name = QStringLiteral("foobar"); m_seatInterface->setName(name); QVERIFY(spy.wait()); QCOMPARE(m_seat->name(), name); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().first().toString(), name); } void TestWaylandSeat::testCapabilities_data() { QTest::addColumn("pointer"); QTest::addColumn("keyboard"); QTest::addColumn("touch"); QTest::newRow("none") << false << false << false; QTest::newRow("pointer") << true << false << false; QTest::newRow("keyboard") << false << true << false; QTest::newRow("touch") << false << false << true; QTest::newRow("pointer/keyboard") << true << true << false; QTest::newRow("pointer/touch") << true << false << true; QTest::newRow("keyboard/touch") << false << true << true; QTest::newRow("all") << true << true << true; } void TestWaylandSeat::testCapabilities() { QVERIFY(!m_seat->hasPointer()); QVERIFY(!m_seat->hasKeyboard()); QVERIFY(!m_seat->hasTouch()); QFETCH(bool, pointer); QFETCH(bool, keyboard); QFETCH(bool, touch); QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasPointer(pointer); m_seatInterface->setHasKeyboard(keyboard); m_seatInterface->setHasTouch(touch); // do processing QCOMPARE(pointerSpy.wait(1000), pointer); QCOMPARE(pointerSpy.isEmpty(), !pointer); if (!pointerSpy.isEmpty()) { QCOMPARE(pointerSpy.first().first().toBool(), pointer); } if (keyboardSpy.isEmpty()) { QCOMPARE(keyboardSpy.wait(1000), keyboard); } QCOMPARE(keyboardSpy.isEmpty(), !keyboard); if (!keyboardSpy.isEmpty()) { QCOMPARE(keyboardSpy.first().first().toBool(), keyboard); } if (touchSpy.isEmpty()) { QCOMPARE(touchSpy.wait(1000), touch); } QCOMPARE(touchSpy.isEmpty(), !touch); if (!touchSpy.isEmpty()) { QCOMPARE(touchSpy.first().first().toBool(), touch); } QCOMPARE(m_seat->hasPointer(), pointer); QCOMPARE(m_seat->hasKeyboard(), keyboard); QCOMPARE(m_seat->hasTouch(), touch); } void TestWaylandSeat::testPointer() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy focusedPointerChangedSpy(m_seatInterface, &SeatInterface::focusedPointerChanged); QVERIFY(focusedPointerChangedSpy.isValid()); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QCOMPARE(focusedPointerChangedSpy.count(), 1); QVERIFY(!focusedPointerChangedSpy.first().first().value()); // no pointer yet QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); Pointer *p = m_seat->createPointer(m_seat); QSignalSpy frameSpy(p, &Pointer::frame); QVERIFY(frameSpy.isValid()); const Pointer &cp = *p; QVERIFY(p->isValid()); QScopedPointer relativePointer(m_relativePointerManager->createRelativePointer(p)); QVERIFY(relativePointer->isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, SIGNAL(pointerCreated(KWayland::Server::PointerInterface*))); QVERIFY(pointerCreatedSpy.isValid()); // once the pointer is created it should be set as the focused pointer QVERIFY(pointerCreatedSpy.wait()); QVERIFY(m_seatInterface->focusedPointer()); QCOMPARE(pointerCreatedSpy.first().first().value(), m_seatInterface->focusedPointer()); QCOMPARE(focusedPointerChangedSpy.count(), 2); QCOMPARE(focusedPointerChangedSpy.last().first().value(), m_seatInterface->focusedPointer()); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 1); m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 3); QVERIFY(!focusedPointerChangedSpy.last().first().value()); serverSurface->client()->flush(); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 2); QSignalSpy enteredSpy(p, SIGNAL(entered(quint32,QPointF))); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(p, SIGNAL(left(quint32))); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(p, SIGNAL(motion(QPointF,quint32))); QVERIFY(motionSpy.isValid()); QSignalSpy axisSpy(p, SIGNAL(axisChanged(quint32,KWayland::Client::Pointer::Axis,qreal))); QVERIFY(axisSpy.isValid()); QSignalSpy buttonSpy(p, SIGNAL(buttonStateChanged(quint32,quint32,quint32,KWayland::Client::Pointer::ButtonState))); QVERIFY(buttonSpy.isValid()); QSignalSpy relativeMotionSpy(relativePointer.data(), &RelativePointer::relativeMotion); QVERIFY(relativeMotionSpy.isValid()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), m_display->serial()); QCOMPARE(enteredSpy.first().last().toPoint(), QPoint(10, 3)); QCOMPARE(frameSpy.count(), 3); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); QCOMPARE(focusedPointerChangedSpy.count(), 4); QCOMPARE(focusedPointerChangedSpy.last().first().value(), serverPointer); // test motion m_seatInterface->setTimestamp(1); m_seatInterface->setPointerPos(QPoint(10, 16)); QVERIFY(motionSpy.wait()); QCOMPARE(frameSpy.count(), 4); QCOMPARE(motionSpy.first().first().toPoint(), QPoint(0, 1)); QCOMPARE(motionSpy.first().last().value(), quint32(1)); // test relative motion m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); QVERIFY(relativeMotionSpy.wait()); QCOMPARE(relativeMotionSpy.count(), 1); QCOMPARE(frameSpy.count(), 5); QCOMPARE(relativeMotionSpy.first().at(0).toSizeF(), QSizeF(1, 2)); QCOMPARE(relativeMotionSpy.first().at(1).toSizeF(), QSizeF(3, 4)); QCOMPARE(relativeMotionSpy.first().at(2).value(), quint64(-1)); // test axis m_seatInterface->setTimestamp(2); m_seatInterface->pointerAxis(Qt::Horizontal, 10); QVERIFY(axisSpy.wait()); QCOMPARE(frameSpy.count(), 6); m_seatInterface->setTimestamp(3); m_seatInterface->pointerAxis(Qt::Vertical, 20); QVERIFY(axisSpy.wait()); QCOMPARE(frameSpy.count(), 7); QCOMPARE(axisSpy.first().at(0).value(), quint32(2)); QCOMPARE(axisSpy.first().at(1).value(), Pointer::Axis::Horizontal); QCOMPARE(axisSpy.first().at(2).value(), qreal(10)); QCOMPARE(axisSpy.last().at(0).value(), quint32(3)); QCOMPARE(axisSpy.last().at(1).value(), Pointer::Axis::Vertical); QCOMPARE(axisSpy.last().at(2).value(), qreal(20)); // test button m_seatInterface->setTimestamp(4); m_seatInterface->pointerButtonPressed(1); QVERIFY(buttonSpy.wait()); QCOMPARE(frameSpy.count(), 8); QCOMPARE(buttonSpy.at(0).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(5); m_seatInterface->pointerButtonPressed(2); QVERIFY(buttonSpy.wait()); QCOMPARE(frameSpy.count(), 9); QCOMPARE(buttonSpy.at(1).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(6); m_seatInterface->pointerButtonReleased(2); QVERIFY(buttonSpy.wait()); QCOMPARE(frameSpy.count(), 10); QCOMPARE(buttonSpy.at(2).at(0).value(), m_display->serial()); m_seatInterface->setTimestamp(7); m_seatInterface->pointerButtonReleased(1); QVERIFY(buttonSpy.wait()); QCOMPARE(frameSpy.count(), 11); QCOMPARE(buttonSpy.count(), 4); // timestamp QCOMPARE(buttonSpy.at(0).at(1).value(), quint32(4)); // button QCOMPARE(buttonSpy.at(0).at(2).value(), quint32(1)); QCOMPARE(buttonSpy.at(0).at(3).value(), KWayland::Client::Pointer::ButtonState::Pressed); // timestamp QCOMPARE(buttonSpy.at(1).at(1).value(), quint32(5)); // button QCOMPARE(buttonSpy.at(1).at(2).value(), quint32(2)); QCOMPARE(buttonSpy.at(1).at(3).value(), KWayland::Client::Pointer::ButtonState::Pressed); QCOMPARE(buttonSpy.at(2).at(0).value(), m_seatInterface->pointerButtonSerial(2)); // timestamp QCOMPARE(buttonSpy.at(2).at(1).value(), quint32(6)); // button QCOMPARE(buttonSpy.at(2).at(2).value(), quint32(2)); QCOMPARE(buttonSpy.at(2).at(3).value(), KWayland::Client::Pointer::ButtonState::Released); QCOMPARE(buttonSpy.at(3).at(0).value(), m_seatInterface->pointerButtonSerial(1)); // timestamp QCOMPARE(buttonSpy.at(3).at(1).value(), quint32(7)); // button QCOMPARE(buttonSpy.at(3).at(2).value(), quint32(1)); QCOMPARE(buttonSpy.at(3).at(3).value(), KWayland::Client::Pointer::ButtonState::Released); // leave the surface m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 5); QVERIFY(leftSpy.wait()); QCOMPARE(frameSpy.count(), 12); QCOMPARE(leftSpy.first().first().value(), m_display->serial()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); // now a relative motion should not be sent to the relative pointer m_seatInterface->relativePointerMotion(QSizeF(1, 2), QSizeF(3, 4), quint64(-1)); QVERIFY(!relativeMotionSpy.wait(500)); // enter it again m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(0, 0)); QCOMPARE(focusedPointerChangedSpy.count(), 6); QVERIFY(enteredSpy.wait()); QCOMPARE(frameSpy.count(), 13); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); // send another relative motion event m_seatInterface->relativePointerMotion(QSizeF(4, 5), QSizeF(6, 7), quint64(1)); QVERIFY(relativeMotionSpy.wait()); QCOMPARE(relativeMotionSpy.count(), 2); QCOMPARE(relativeMotionSpy.last().at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(relativeMotionSpy.last().at(1).toSizeF(), QSizeF(6, 7)); QCOMPARE(relativeMotionSpy.last().at(2).value(), quint64(1)); // destroy the focused pointer QSignalSpy unboundSpy(serverPointer, &Resource::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverPointer, &Resource::destroyed); QVERIFY(destroyedSpy.isValid()); delete p; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); // now test that calling into the methods in Seat does not crash QCOMPARE(m_seatInterface->focusedPointer(), serverPointer); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); m_seatInterface->setTimestamp(8); m_seatInterface->setPointerPos(QPoint(10, 15)); m_seatInterface->setTimestamp(9); m_seatInterface->pointerButtonPressed(1); m_seatInterface->setTimestamp(10); m_seatInterface->pointerButtonReleased(1); m_seatInterface->setTimestamp(11); m_seatInterface->pointerAxis(Qt::Horizontal, 10); m_seatInterface->setTimestamp(12); m_seatInterface->pointerAxis(Qt::Vertical, 20); m_seatInterface->setFocusedPointerSurface(nullptr); QCOMPARE(focusedPointerChangedSpy.count(), 7); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(focusedPointerChangedSpy.count(), 8); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedPointer()); // and now destroy QVERIFY(destroyedSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 1); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedPointer()); // create a pointer again p = m_seat->createPointer(m_seat); QVERIFY(focusedPointerChangedSpy.wait()); QCOMPARE(focusedPointerChangedSpy.count(), 9); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QSignalSpy entered2Spy(p, &Pointer::entered); QVERIFY(entered2Spy.wait()); QCOMPARE(p->enteredSurface(), s); QSignalSpy leftSpy2(p, &Pointer::left); QVERIFY(leftSpy2.isValid()); delete s; QVERIFY(!p->enteredSurface()); QVERIFY(leftSpy2.wait()); QCOMPARE(focusedPointerChangedSpy.count(), 10); QVERIFY(!m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); } void TestWaylandSeat::testPointerTransformation_data() { QTest::addColumn("enterTransformation"); // global position at 20/18 QTest::addColumn("expectedEnterPoint"); // global position at 10/16 QTest::addColumn("expectedMovePoint"); QMatrix4x4 tm; tm.translate(-10, -15); QTest::newRow("translation") << tm << QPointF(10, 3) << QPointF(0, 1); QMatrix4x4 sm; sm.scale(2, 2); QTest::newRow("scale") << sm << QPointF(40, 36) << QPointF(20, 32); QMatrix4x4 rotate; rotate.rotate(90, 0, 0, 1); QTest::newRow("rotate") << rotate << QPointF(-18, 20) << QPointF(-16, 10); } void TestWaylandSeat::testPointerTransformation() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setPointerPos(QPoint(20, 18)); QFETCH(QMatrix4x4, enterTransformation); m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation); QCOMPARE(m_seatInterface->focusedPointerSurfaceTransformation(), enterTransformation); // no pointer yet QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(!m_seatInterface->focusedPointer()); Pointer *p = m_seat->createPointer(m_seat); const Pointer &cp = *p; QVERIFY(p->isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, &SeatInterface::pointerCreated); QVERIFY(pointerCreatedSpy.isValid()); // once the pointer is created it should be set as the focused pointer QVERIFY(pointerCreatedSpy.wait()); QVERIFY(m_seatInterface->focusedPointer()); QCOMPARE(pointerCreatedSpy.first().first().value(), m_seatInterface->focusedPointer()); m_seatInterface->setFocusedPointerSurface(nullptr); serverSurface->client()->flush(); QTest::qWait(100); QSignalSpy enteredSpy(p, &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(p, &Pointer::left); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(p, &Pointer::motion); QVERIFY(motionSpy.isValid()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); m_seatInterface->setFocusedPointerSurface(serverSurface, enterTransformation); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), m_display->serial()); QTEST(enteredSpy.first().last().toPointF(), "expectedEnterPoint"); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); // test motion m_seatInterface->setTimestamp(1); m_seatInterface->setPointerPos(QPoint(10, 16)); QVERIFY(motionSpy.wait()); QTEST(motionSpy.first().first().toPointF(), "expectedMovePoint"); QCOMPARE(motionSpy.first().last().value(), quint32(1)); // leave the surface m_seatInterface->setFocusedPointerSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.first().first().value(), m_display->serial()); QVERIFY(!p->enteredSurface()); QVERIFY(!cp.enteredSurface()); // enter it again m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(p->enteredSurface(), s); QCOMPARE(cp.enteredSurface(), s); delete s; wl_display_flush(m_connection->display()); QTest::qWait(100); QVERIFY(!m_seatInterface->focusedPointerSurface()); } Q_DECLARE_METATYPE(Qt::MouseButton) void TestWaylandSeat::testPointerButton_data() { QTest::addColumn("qtButton"); QTest::addColumn("waylandButton"); QTest::newRow("left") << Qt::LeftButton << quint32(BTN_LEFT); QTest::newRow("right") << Qt::RightButton << quint32(BTN_RIGHT); QTest::newRow("mid") << Qt::MidButton << quint32(BTN_MIDDLE); QTest::newRow("middle") << Qt::MiddleButton << quint32(BTN_MIDDLE); QTest::newRow("back") << Qt::BackButton << quint32(BTN_BACK); QTest::newRow("x1") << Qt::XButton1 << quint32(BTN_BACK); QTest::newRow("extra1") << Qt::ExtraButton1 << quint32(BTN_BACK); QTest::newRow("forward") << Qt::ForwardButton << quint32(BTN_FORWARD); QTest::newRow("x2") << Qt::XButton2 << quint32(BTN_FORWARD); QTest::newRow("extra2") << Qt::ExtraButton2 << quint32(BTN_FORWARD); QTest::newRow("task") << Qt::TaskButton << quint32(BTN_TASK); QTest::newRow("extra3") << Qt::ExtraButton3 << quint32(BTN_TASK); QTest::newRow("extra4") << Qt::ExtraButton4 << quint32(BTN_EXTRA); QTest::newRow("extra5") << Qt::ExtraButton5 << quint32(BTN_SIDE); QTest::newRow("extra6") << Qt::ExtraButton6 << quint32(0x118); QTest::newRow("extra7") << Qt::ExtraButton7 << quint32(0x119); QTest::newRow("extra8") << Qt::ExtraButton8 << quint32(0x11a); QTest::newRow("extra9") << Qt::ExtraButton9 << quint32(0x11b); QTest::newRow("extra10") << Qt::ExtraButton10 << quint32(0x11c); QTest::newRow("extra11") << Qt::ExtraButton11 << quint32(0x11d); QTest::newRow("extra12") << Qt::ExtraButton12 << quint32(0x11e); QTest::newRow("extra13") << Qt::ExtraButton13 << quint32(0x11f); } void TestWaylandSeat::testPointerButton() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); QSignalSpy buttonChangedSpy(p.data(), SIGNAL(buttonStateChanged(quint32,quint32,quint32,KWayland::Client::Pointer::ButtonState))); QVERIFY(buttonChangedSpy.isValid()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(m_seatInterface->focusedPointer()); QCoreApplication::processEvents(); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); PointerInterface *serverPointer = m_seatInterface->focusedPointer(); QVERIFY(serverPointer); QFETCH(Qt::MouseButton, qtButton); QFETCH(quint32, waylandButton); quint32 msec = QDateTime::currentMSecsSinceEpoch(); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false); m_seatInterface->setTimestamp(msec); m_seatInterface->pointerButtonPressed(qtButton); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), true); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), true); QVERIFY(buttonChangedSpy.wait()); QCOMPARE(buttonChangedSpy.count(), 1); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(waylandButton)); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(qtButton)); QCOMPARE(buttonChangedSpy.last().at(1).value(), msec); QCOMPARE(buttonChangedSpy.last().at(2).value(), waylandButton); QCOMPARE(buttonChangedSpy.last().at(3).value(), Pointer::ButtonState::Pressed); msec = QDateTime::currentMSecsSinceEpoch(); m_seatInterface->setTimestamp(QDateTime::currentMSecsSinceEpoch()); m_seatInterface->pointerButtonReleased(qtButton); QCOMPARE(m_seatInterface->isPointerButtonPressed(waylandButton), false); QCOMPARE(m_seatInterface->isPointerButtonPressed(qtButton), false); QVERIFY(buttonChangedSpy.wait()); QCOMPARE(buttonChangedSpy.count(), 2); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(waylandButton)); QCOMPARE(buttonChangedSpy.last().at(0).value(), m_seatInterface->pointerButtonSerial(qtButton)); QCOMPARE(buttonChangedSpy.last().at(1).value(), msec); QCOMPARE(buttonChangedSpy.last().at(2).value(), waylandButton); QCOMPARE(buttonChangedSpy.last().at(3).value(), Pointer::ButtonState::Released); } void TestWaylandSeat::testPointerSubSurfaceTree() { // this test verifies that pointer motion on a surface with sub-surfaces sends motion enter/leave to the sub-surface using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); // create a sub surface tree // parent surface (100, 100) with one sub surface taking the half of it's size (50, 100) // which has two further children (50, 50) which are overlapping QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer childSurface(m_compositor->createSurface()); QScopedPointer grandChild1Surface(m_compositor->createSurface()); QScopedPointer grandChild2Surface(m_compositor->createSurface()); QScopedPointer childSubSurface(m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data())); QScopedPointer grandChild1SubSurface(m_subCompositor->createSubSurface(grandChild1Surface.data(), childSurface.data())); QScopedPointer grandChild2SubSurface(m_subCompositor->createSubSurface(grandChild2Surface.data(), childSurface.data())); grandChild2SubSurface->setPosition(QPoint(0, 25)); // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { QImage image(size, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); s->commit(Surface::CommitFlag::None); }; render(grandChild2Surface.data(), QSize(50, 50)); render(grandChild1Surface.data(), QSize(50, 50)); render(childSurface.data(), QSize(50, 100)); render(parentSurface.data(), QSize(100, 100)); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface->isMapped()); // send in pointer events QSignalSpy enteredSpy(pointer.data(), &Pointer::entered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(pointer.data(), &Pointer::left); QVERIFY(leftSpy.isValid()); QSignalSpy motionSpy(pointer.data(), &Pointer::motion); QVERIFY(motionSpy.isValid()); // first to the grandChild2 in the overlapped area quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(motionSpy.count(), 0); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 25)); QCOMPARE(pointer->enteredSurface(), grandChild2Surface.data()); // a motion on grandchild2 m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 60)); QVERIFY(motionSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(motionSpy.count(), 1); QCOMPARE(motionSpy.last().first().toPointF(), QPointF(25, 35)); // motion which changes to childSurface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 80)); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(motionSpy.count(), 1); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(25, 80)); QCOMPARE(pointer->enteredSurface(), childSurface.data()); // a leave for the whole surface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setFocusedPointerSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 2); QCOMPARE(motionSpy.count(), 1); // a new enter on the main surface m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(75, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 3); QCOMPARE(leftSpy.count(), 2); QCOMPARE(motionSpy.count(), 1); QCOMPARE(enteredSpy.last().last().toPointF(), QPointF(75, 50)); QCOMPARE(pointer->enteredSurface(), parentSurface.data()); } void TestWaylandSeat::testPointerSwipeGesture_data() { QTest::addColumn("cancel"); QTest::addColumn("expectedEndCount"); QTest::addColumn("expectedCancelCount"); QTest::newRow("end") << false << 1 << 0; QTest::newRow("cancel") << true << 0 << 1; } void TestWaylandSeat::testPointerSwipeGesture() { using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer and pointer swipe gesture QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); QScopedPointer gesture(m_pointerGestures->createSwipeGesture(pointer.data())); QVERIFY(gesture); QVERIFY(gesture->isValid()); QVERIFY(gesture->surface().isNull()); QCOMPARE(gesture->fingerCount(), 0u); QSignalSpy startSpy(gesture.data(), &PointerSwipeGesture::started); QVERIFY(startSpy.isValid()); QSignalSpy updateSpy(gesture.data(), &PointerSwipeGesture::updated); QVERIFY(updateSpy.isValid()); QSignalSpy endSpy(gesture.data(), &PointerSwipeGesture::ended); QVERIFY(endSpy.isValid()); QSignalSpy cancelledSpy(gesture.data(), &PointerSwipeGesture::cancelled); QVERIFY(cancelledSpy.isValid()); // now create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(m_seatInterface->focusedPointer()); // send in the start quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerSwipeGesture(2); QVERIFY(startSpy.wait()); QCOMPARE(startSpy.count(), 1); QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); QCOMPARE(startSpy.first().at(1).value(), 1u); QCOMPARE(gesture->fingerCount(), 2u); QCOMPARE(gesture->surface().data(), surface.data()); // another start should not be possible m_seatInterface->startPointerSwipeGesture(2); QVERIFY(!startSpy.wait(500)); // send in some updates m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(2, 3)); QVERIFY(updateSpy.wait()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(4, 5)); QVERIFY(updateSpy.wait()); QCOMPARE(updateSpy.count(), 2); QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); QCOMPARE(updateSpy.at(0).at(1).value(), 2u); QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(updateSpy.at(1).at(1).value(), 3u); // now end or cancel QFETCH(bool, cancel); QSignalSpy *spy; m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerSwipeGesture(); spy = &cancelledSpy; } else { m_seatInterface->endPointerSwipeGesture(); spy = &endSpy; } QVERIFY(spy->wait()); QTEST(endSpy.count(), "expectedEndCount"); QTEST(cancelledSpy.count(), "expectedCancelCount"); QCOMPARE(spy->count(), 1); QCOMPARE(spy->first().at(0).value(), m_display->serial()); QCOMPARE(spy->first().at(1).value(), 4u); QCOMPARE(gesture->fingerCount(), 0u); QVERIFY(gesture->surface().isNull()); // now a start should be possible again m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerSwipeGesture(2); QVERIFY(startSpy.wait()); // unsetting the focused pointer surface should not change anything m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerSwipeGesture(QSizeF(6, 7)); QVERIFY(updateSpy.wait()); // and end m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerSwipeGesture(); } else { m_seatInterface->endPointerSwipeGesture(); } QVERIFY(spy->wait()); } void TestWaylandSeat::testPointerPinchGesture_data() { QTest::addColumn("cancel"); QTest::addColumn("expectedEndCount"); QTest::addColumn("expectedCancelCount"); QTest::newRow("end") << false << 1 << 0; QTest::newRow("cancel") << true << 0 << 1; } void TestWaylandSeat::testPointerPinchGesture() { using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer and pointer swipe gesture QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); QScopedPointer gesture(m_pointerGestures->createPinchGesture(pointer.data())); QVERIFY(gesture); QVERIFY(gesture->isValid()); QVERIFY(gesture->surface().isNull()); QCOMPARE(gesture->fingerCount(), 0u); QSignalSpy startSpy(gesture.data(), &PointerPinchGesture::started); QVERIFY(startSpy.isValid()); QSignalSpy updateSpy(gesture.data(), &PointerPinchGesture::updated); QVERIFY(updateSpy.isValid()); QSignalSpy endSpy(gesture.data(), &PointerPinchGesture::ended); QVERIFY(endSpy.isValid()); QSignalSpy cancelledSpy(gesture.data(), &PointerPinchGesture::cancelled); QVERIFY(cancelledSpy.isValid()); // now create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(m_seatInterface->focusedPointer()); // send in the start quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerPinchGesture(3); QVERIFY(startSpy.wait()); QCOMPARE(startSpy.count(), 1); QCOMPARE(startSpy.first().at(0).value(), m_display->serial()); QCOMPARE(startSpy.first().at(1).value(), 1u); QCOMPARE(gesture->fingerCount(), 3u); QCOMPARE(gesture->surface().data(), surface.data()); // another start should not be possible m_seatInterface->startPointerPinchGesture(3); QVERIFY(!startSpy.wait(500)); // send in some updates m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(2, 3), 2, 45); QVERIFY(updateSpy.wait()); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(4, 5), 1, 90); QVERIFY(updateSpy.wait()); QCOMPARE(updateSpy.count(), 2); QCOMPARE(updateSpy.at(0).at(0).toSizeF(), QSizeF(2, 3)); QCOMPARE(updateSpy.at(0).at(1).value(), 2u); QCOMPARE(updateSpy.at(0).at(2).value(), 45u); QCOMPARE(updateSpy.at(0).at(3).value(), 2u); QCOMPARE(updateSpy.at(1).at(0).toSizeF(), QSizeF(4, 5)); QCOMPARE(updateSpy.at(1).at(1).value(), 1u); QCOMPARE(updateSpy.at(1).at(2).value(), 90u); QCOMPARE(updateSpy.at(1).at(3).value(), 3u); // now end or cancel QFETCH(bool, cancel); QSignalSpy *spy; m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerPinchGesture(); spy = &cancelledSpy; } else { m_seatInterface->endPointerPinchGesture(); spy = &endSpy; } QVERIFY(spy->wait()); QTEST(endSpy.count(), "expectedEndCount"); QTEST(cancelledSpy.count(), "expectedCancelCount"); QCOMPARE(spy->count(), 1); QCOMPARE(spy->first().at(0).value(), m_display->serial()); QCOMPARE(spy->first().at(1).value(), 4u); QCOMPARE(gesture->fingerCount(), 0u); QVERIFY(gesture->surface().isNull()); // now a start should be possible again m_seatInterface->setTimestamp(timestamp++); m_seatInterface->startPointerPinchGesture(3); QVERIFY(startSpy.wait()); // unsetting the focused pointer surface should not change anything m_seatInterface->setFocusedPointerSurface(nullptr); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->updatePointerPinchGesture(QSizeF(6, 7), 2, -45); QVERIFY(updateSpy.wait()); // and end m_seatInterface->setTimestamp(timestamp++); if (cancel) { m_seatInterface->cancelPointerPinchGesture(); } else { m_seatInterface->endPointerPinchGesture(); } QVERIFY(spy->wait()); } void TestWaylandSeat::testPointerAxis() { using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); QVERIFY(pointer); // now create a surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedPointerSurface(serverSurface); QCOMPARE(m_seatInterface->focusedPointerSurface(), serverSurface); QVERIFY(m_seatInterface->focusedPointer()); QSignalSpy frameSpy(pointer.data(), &Pointer::frame); QVERIFY(frameSpy.isValid()); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 1); // let's scroll vertically QSignalSpy axisSourceSpy(pointer.data(), &Pointer::axisSourceChanged); QVERIFY(axisSourceSpy.isValid()); QSignalSpy axisSpy(pointer.data(), &Pointer::axisChanged); QVERIFY(axisSpy.isValid()); QSignalSpy axisDiscreteSpy(pointer.data(), &Pointer::axisDiscreteChanged); QVERIFY(axisDiscreteSpy.isValid()); QSignalSpy axisStoppedSpy(pointer.data(), &Pointer::axisStopped); QVERIFY(axisStoppedSpy.isValid()); quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxisV5(Qt::Vertical, 10, 1, PointerAxisSource::Wheel); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 2); QCOMPARE(axisSourceSpy.count(), 1); QCOMPARE(axisSourceSpy.last().at(0).value(), Pointer::AxisSource::Wheel); QCOMPARE(axisDiscreteSpy.count(), 1); QCOMPARE(axisDiscreteSpy.last().at(0).value(), Pointer::Axis::Vertical); QCOMPARE(axisDiscreteSpy.last().at(1).value(), 1); QCOMPARE(axisSpy.count(), 1); QCOMPARE(axisSpy.last().at(0).value(), quint32(1)); QCOMPARE(axisSpy.last().at(1).value(), Pointer::Axis::Vertical); QCOMPARE(axisSpy.last().at(2).value(), 10.0); QCOMPARE(axisStoppedSpy.count(), 0); // let's scroll using fingers m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxisV5(Qt::Horizontal, 42, 0, PointerAxisSource::Finger); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 3); QCOMPARE(axisSourceSpy.count(), 2); QCOMPARE(axisSourceSpy.last().at(0).value(), Pointer::AxisSource::Finger); QCOMPARE(axisDiscreteSpy.count(), 1); QCOMPARE(axisSpy.count(), 2); QCOMPARE(axisSpy.last().at(0).value(), quint32(2)); QCOMPARE(axisSpy.last().at(1).value(), Pointer::Axis::Horizontal); QCOMPARE(axisSpy.last().at(2).value(), 42.0); QCOMPARE(axisStoppedSpy.count(), 0); // lift the fingers off the device m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxisV5(Qt::Horizontal, 0, 0, PointerAxisSource::Finger); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 4); QCOMPARE(axisSourceSpy.count(), 3); QCOMPARE(axisSourceSpy.last().at(0).value(), Pointer::AxisSource::Finger); QCOMPARE(axisDiscreteSpy.count(), 1); QCOMPARE(axisSpy.count(), 2); QCOMPARE(axisStoppedSpy.count(), 1); QCOMPARE(axisStoppedSpy.last().at(0).value(), 3); QCOMPARE(axisStoppedSpy.last().at(1).value(), Pointer::Axis::Horizontal); // if the device is unknown, no axis_source event should be sent m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerAxisV5(Qt::Horizontal, 42, 1, PointerAxisSource::Unknown); QVERIFY(frameSpy.wait()); QCOMPARE(frameSpy.count(), 5); QCOMPARE(axisSourceSpy.count(), 3); QCOMPARE(axisDiscreteSpy.count(), 2); QCOMPARE(axisDiscreteSpy.last().at(0).value(), Pointer::Axis::Horizontal); QCOMPARE(axisDiscreteSpy.last().at(1).value(), 1); QCOMPARE(axisSpy.count(), 3); QCOMPARE(axisSpy.last().at(0).value(), quint32(4)); QCOMPARE(axisSpy.last().at(1).value(), Pointer::Axis::Horizontal); QCOMPARE(axisSpy.last().at(2).value(), 42.0); QCOMPARE(axisStoppedSpy.count(), 1); } void TestWaylandSeat::testKeyboardSubSurfaceTreeFromPointer() { // this test verifies that when clicking on a sub-surface the keyboard focus passes to it using namespace KWayland::Client; using namespace KWayland::Server; // first create the pointer QSignalSpy hasPointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerChangedSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(hasPointerChangedSpy.wait()); QScopedPointer pointer(m_seat->createPointer()); // and create keyboard QSignalSpy hasKeyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged); QVERIFY(hasKeyboardChangedSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(hasKeyboardChangedSpy.wait()); QScopedPointer keyboard(m_seat->createKeyboard()); // create a sub surface tree // parent surface (100, 100) with one sub surface taking the half of it's size (50, 100) // which has two further children (50, 50) which are overlapping QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer childSurface(m_compositor->createSurface()); QScopedPointer grandChild1Surface(m_compositor->createSurface()); QScopedPointer grandChild2Surface(m_compositor->createSurface()); QScopedPointer childSubSurface(m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data())); QScopedPointer grandChild1SubSurface(m_subCompositor->createSubSurface(grandChild1Surface.data(), childSurface.data())); QScopedPointer grandChild2SubSurface(m_subCompositor->createSubSurface(grandChild2Surface.data(), childSurface.data())); grandChild2SubSurface->setPosition(QPoint(0, 25)); // let's map the surfaces auto render = [this] (Surface *s, const QSize &size) { QImage image(size, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(QPoint(0, 0), size)); s->commit(Surface::CommitFlag::None); }; render(grandChild2Surface.data(), QSize(50, 50)); render(grandChild1Surface.data(), QSize(50, 50)); render(childSurface.data(), QSize(50, 100)); render(parentSurface.data(), QSize(100, 100)); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface->isMapped()); // pass keyboard focus to the main surface QSignalSpy enterSpy(keyboard.data(), &Keyboard::entered); QVERIFY(enterSpy.isValid()); QSignalSpy leftSpy(keyboard.data(), &Keyboard::left); QVERIFY(leftSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(enterSpy.wait()); QCOMPARE(enterSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); QCOMPARE(keyboard->enteredSurface(), parentSurface.data()); // now pass also pointer focus to the surface QSignalSpy pointerEnterSpy(pointer.data(), &Pointer::entered); QVERIFY(pointerEnterSpy.isValid()); quint32 timestamp = 1; m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setPointerPos(QPointF(25, 50)); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(pointerEnterSpy.wait()); QCOMPARE(pointerEnterSpy.count(), 1); // should not have affected the keyboard QCOMPARE(enterSpy.count(), 1); QCOMPARE(leftSpy.count(), 0); // let's click m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(Qt::LeftButton); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(Qt::LeftButton); QVERIFY(enterSpy.wait()); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(keyboard->enteredSurface(), grandChild2Surface.data()); // click on same surface should not trigger another enter m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonPressed(Qt::LeftButton); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->pointerButtonReleased(Qt::LeftButton); QVERIFY(!enterSpy.wait(200)); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); QCOMPARE(keyboard->enteredSurface(), grandChild2Surface.data()); // unfocus keyboard m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(leftSpy.wait()); QCOMPARE(enterSpy.count(), 2); QCOMPARE(leftSpy.count(), 2); } void TestWaylandSeat::testCursor() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QSignalSpy enteredSpy(p.data(), SIGNAL(entered(quint32,QPointF))); QVERIFY(enteredSpy.isValid()); m_seatInterface->setPointerPos(QPoint(20, 18)); m_seatInterface->setFocusedPointerSurface(serverSurface, QPoint(10, 15)); quint32 serial = m_seatInterface->display()->serial(); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.first().first().value(), serial); QVERIFY(m_seatInterface->focusedPointerSurface()); QVERIFY(m_seatInterface->focusedPointer()); QVERIFY(!m_seatInterface->focusedPointer()->cursor()); QSignalSpy cursorChangedSpy(m_seatInterface->focusedPointer(), SIGNAL(cursorChanged())); QVERIFY(cursorChangedSpy.isValid()); // just remove the pointer p->setCursor(nullptr); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(cursorChangedSpy.count(), 1); auto cursor = m_seatInterface->focusedPointer()->cursor(); QVERIFY(cursor); QVERIFY(!cursor->surface()); QCOMPARE(cursor->hotspot(), QPoint()); QCOMPARE(cursor->enteredSerial(), serial); QCOMPARE(cursor->pointer(), m_seatInterface->focusedPointer()); QSignalSpy hotspotChangedSpy(cursor, SIGNAL(hotspotChanged())); QVERIFY(hotspotChangedSpy.isValid()); QSignalSpy surfaceChangedSpy(cursor, SIGNAL(surfaceChanged())); QVERIFY(surfaceChangedSpy.isValid()); QSignalSpy enteredSerialChangedSpy(cursor, SIGNAL(enteredSerialChanged())); QVERIFY(enteredSerialChangedSpy.isValid()); QSignalSpy changedSpy(cursor, SIGNAL(changed())); QVERIFY(changedSpy.isValid()); // test changing hotspot p->setCursor(nullptr, QPoint(1, 2)); QVERIFY(hotspotChangedSpy.wait()); QCOMPARE(hotspotChangedSpy.count(), 1); QCOMPARE(changedSpy.count(), 1); QCOMPARE(cursorChangedSpy.count(), 2); QCOMPARE(cursor->hotspot(), QPoint(1, 2)); QVERIFY(enteredSerialChangedSpy.isEmpty()); QVERIFY(surfaceChangedSpy.isEmpty()); // set surface auto cursorSurface = m_compositor->createSurface(m_compositor); QVERIFY(cursorSurface->isValid()); p->setCursor(cursorSurface, QPoint(1, 2)); QVERIFY(surfaceChangedSpy.wait()); QCOMPARE(surfaceChangedSpy.count(), 1); QCOMPARE(changedSpy.count(), 2); QCOMPARE(cursorChangedSpy.count(), 3); QVERIFY(enteredSerialChangedSpy.isEmpty()); QCOMPARE(cursor->hotspot(), QPoint(1, 2)); QVERIFY(cursor->surface()); // and add an image to the surface QImage img(QSize(10, 20), QImage::Format_RGB32); img.fill(Qt::red); cursorSurface->attachBuffer(m_shm->createBuffer(img)); cursorSurface->damage(QRect(0, 0, 10, 20)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(changedSpy.wait()); QCOMPARE(changedSpy.count(), 3); QCOMPARE(cursorChangedSpy.count(), 4); QCOMPARE(surfaceChangedSpy.count(), 1); QCOMPARE(cursor->surface()->buffer()->data(), img); // and add another image to the surface QImage blue(QSize(10, 20), QImage::Format_ARGB32_Premultiplied); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 20)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(changedSpy.wait()); QCOMPARE(changedSpy.count(), 4); QCOMPARE(cursorChangedSpy.count(), 5); QCOMPARE(cursor->surface()->buffer()->data(), blue); p->hideCursor(); QVERIFY(surfaceChangedSpy.wait()); QCOMPARE(changedSpy.count(), 5); QCOMPARE(cursorChangedSpy.count(), 6); QCOMPARE(surfaceChangedSpy.count(), 2); QVERIFY(!cursor->surface()); } void TestWaylandSeat::testCursorDamage() { // this test verifies that damaging a cursor surface triggers a cursor changed on the server using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy pointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); // create pointer QScopedPointer p(m_seat->createPointer()); QVERIFY(p->isValid()); QSignalSpy enteredSpy(p.data(), &Pointer::entered); QVERIFY(enteredSpy.isValid()); // create surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // send enter to the surface m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(enteredSpy.wait()); // create a signal spy for the cursor changed signal auto pointer = m_seatInterface->focusedPointer(); QSignalSpy cursorChangedSpy(pointer, &PointerInterface::cursorChanged); QVERIFY(cursorChangedSpy.isValid()); // now let's set the cursor Surface *cursorSurface = m_compositor->createSurface(m_compositor); QVERIFY(cursorSurface); QImage red(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); red.fill(Qt::red); cursorSurface->attachBuffer(m_shm->createBuffer(red)); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(Surface::CommitFlag::None); p->setCursor(cursorSurface, QPoint(0, 0)); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(pointer->cursor()->surface()->buffer()->data(), red); // and damage the surface QImage blue(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); blue.fill(Qt::blue); cursorSurface->attachBuffer(m_shm->createBuffer(blue)); cursorSurface->damage(QRect(0, 0, 10, 10)); cursorSurface->commit(Surface::CommitFlag::None); QVERIFY(cursorChangedSpy.wait()); QCOMPARE(pointer->cursor()->surface()->buffer()->data(), blue); } void TestWaylandSeat::testKeyboard() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardSpy.wait()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // no keyboard yet QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); Keyboard *keyboard = m_seat->createKeyboard(m_seat); QSignalSpy repeatInfoSpy(keyboard, &Keyboard::keyRepeatChanged); QVERIFY(repeatInfoSpy.isValid()); const Keyboard &ckeyboard = *keyboard; QVERIFY(keyboard->isValid()); QCOMPARE(keyboard->isKeyRepeatEnabled(), false); QCOMPARE(keyboard->keyRepeatDelay(), 0); QCOMPARE(keyboard->keyRepeatRate(), 0); wl_display_flush(m_connection->display()); QTest::qWait(100); auto serverKeyboard = m_seatInterface->focusedKeyboard(); QVERIFY(serverKeyboard); // we should get the repeat info announced QCOMPARE(repeatInfoSpy.count(), 1); QCOMPARE(keyboard->isKeyRepeatEnabled(), false); QCOMPARE(keyboard->keyRepeatDelay(), 0); QCOMPARE(keyboard->keyRepeatRate(), 0); // let's change repeat in server m_seatInterface->setKeyRepeatInfo(25, 660); m_seatInterface->focusedKeyboard()->client()->flush(); QVERIFY(repeatInfoSpy.wait()); QCOMPARE(repeatInfoSpy.count(), 2); QCOMPARE(keyboard->isKeyRepeatEnabled(), true); QCOMPARE(keyboard->keyRepeatRate(), 25); QCOMPARE(keyboard->keyRepeatDelay(), 660); m_seatInterface->setTimestamp(1); m_seatInterface->keyPressed(KEY_K); m_seatInterface->setTimestamp(2); m_seatInterface->keyPressed(KEY_D); m_seatInterface->setTimestamp(3); m_seatInterface->keyPressed(KEY_E); QSignalSpy modifierSpy(keyboard, SIGNAL(modifiersChanged(quint32,quint32,quint32,quint32))); QVERIFY(modifierSpy.isValid()); QSignalSpy enteredSpy(keyboard, SIGNAL(entered(quint32))); QVERIFY(enteredSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard()->focusedSurface(), serverSurface); // we get the modifiers sent after the enter QVERIFY(modifierSpy.wait()); QCOMPARE(modifierSpy.count(), 1); QCOMPARE(modifierSpy.first().at(0).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(1).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(2).value(), quint32(0)); QCOMPARE(modifierSpy.first().at(3).value(), quint32(0)); QCOMPARE(enteredSpy.count(), 1); // TODO: get through API QCOMPARE(enteredSpy.first().first().value(), m_display->serial() - 1); QSignalSpy keyChangedSpy(keyboard, SIGNAL(keyChanged(quint32,KWayland::Client::Keyboard::KeyState,quint32))); QVERIFY(keyChangedSpy.isValid()); m_seatInterface->setTimestamp(4); m_seatInterface->keyReleased(KEY_E); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(5); m_seatInterface->keyReleased(KEY_D); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(6); m_seatInterface->keyReleased(KEY_K); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(7); m_seatInterface->keyPressed(KEY_F1); QVERIFY(keyChangedSpy.wait()); m_seatInterface->setTimestamp(8); m_seatInterface->keyReleased(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 5); QCOMPARE(keyChangedSpy.at(0).at(0).value(), quint32(KEY_E)); QCOMPARE(keyChangedSpy.at(0).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(0).at(2).value(), quint32(4)); QCOMPARE(keyChangedSpy.at(1).at(0).value(), quint32(KEY_D)); QCOMPARE(keyChangedSpy.at(1).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(1).at(2).value(), quint32(5)); QCOMPARE(keyChangedSpy.at(2).at(0).value(), quint32(KEY_K)); QCOMPARE(keyChangedSpy.at(2).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(2).at(2).value(), quint32(6)); QCOMPARE(keyChangedSpy.at(3).at(0).value(), quint32(KEY_F1)); QCOMPARE(keyChangedSpy.at(3).at(1).value(), Keyboard::KeyState::Pressed); QCOMPARE(keyChangedSpy.at(3).at(2).value(), quint32(7)); QCOMPARE(keyChangedSpy.at(4).at(0).value(), quint32(KEY_F1)); QCOMPARE(keyChangedSpy.at(4).at(1).value(), Keyboard::KeyState::Released); QCOMPARE(keyChangedSpy.at(4).at(2).value(), quint32(8)); // releasing a key which is already released should not set a key changed m_seatInterface->keyReleased(KEY_F1); QVERIFY(!keyChangedSpy.wait(200)); // let's press it again m_seatInterface->keyPressed(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 6); // press again should be ignored m_seatInterface->keyPressed(KEY_F1); QVERIFY(!keyChangedSpy.wait(200)); // and release m_seatInterface->keyReleased(KEY_F1); QVERIFY(keyChangedSpy.wait()); QCOMPARE(keyChangedSpy.count(), 7); m_seatInterface->updateKeyboardModifiers(1, 2, 3, 4); QVERIFY(modifierSpy.wait()); QCOMPARE(modifierSpy.count(), 2); QCOMPARE(modifierSpy.last().at(0).value(), quint32(1)); QCOMPARE(modifierSpy.last().at(1).value(), quint32(2)); QCOMPARE(modifierSpy.last().at(2).value(), quint32(3)); QCOMPARE(modifierSpy.last().at(3).value(), quint32(4)); QSignalSpy leftSpy(keyboard, SIGNAL(left(quint32))); QVERIFY(leftSpy.isValid()); m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(leftSpy.wait()); QCOMPARE(leftSpy.count(), 1); // TODO: get through API QCOMPARE(leftSpy.first().first().value(), m_display->serial() -1 ); QVERIFY(!keyboard->enteredSurface()); QVERIFY(!ckeyboard.enteredSurface()); // enter it again m_seatInterface->setFocusedKeyboardSurface(serverSurface); QVERIFY(modifierSpy.wait()); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard()->focusedSurface(), serverSurface); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(keyboard->enteredSurface(), s); QCOMPARE(ckeyboard.enteredSurface(), s); QSignalSpy serverSurfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(serverSurfaceDestroyedSpy.isValid()); QCOMPARE(keyboard->enteredSurface(), s); delete s; QVERIFY(!keyboard->enteredSurface()); QVERIFY(leftSpy.wait()); QCOMPARE(serverSurfaceDestroyedSpy.count(), 1); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(!serverKeyboard->focusedSurface()); // let's create a Surface again QScopedPointer s2(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); QCOMPARE(surfaceCreatedSpy.count(), 2); serverSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QCOMPARE(m_seatInterface->focusedKeyboard(), serverKeyboard); // delete the Keyboard QSignalSpy unboundSpy(serverKeyboard, &Resource::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverKeyboard, &Resource::destroyed); QVERIFY(destroyedSpy.isValid()); delete keyboard; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); // verify that calling into the Keyboard related functionality doesn't crash m_seatInterface->setTimestamp(9); m_seatInterface->keyPressed(KEY_F2); m_seatInterface->setTimestamp(10); m_seatInterface->keyReleased(KEY_F2); m_seatInterface->setKeyRepeatInfo(30, 560); m_seatInterface->setKeyRepeatInfo(25, 660); m_seatInterface->updateKeyboardModifiers(5, 6, 7, 8); m_seatInterface->setKeymap(open("/dev/null", O_RDONLY), 0); m_seatInterface->setFocusedKeyboardSurface(nullptr); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); // create a second Keyboard to verify that repeat info is announced properly Keyboard *keyboard2 = m_seat->createKeyboard(m_seat); QSignalSpy repeatInfoSpy2(keyboard2, &Keyboard::keyRepeatChanged); QVERIFY(repeatInfoSpy2.isValid()); QVERIFY(keyboard2->isValid()); QCOMPARE(keyboard2->isKeyRepeatEnabled(), false); QCOMPARE(keyboard2->keyRepeatDelay(), 0); QCOMPARE(keyboard2->keyRepeatRate(), 0); wl_display_flush(m_connection->display()); QVERIFY(repeatInfoSpy2.wait()); QCOMPARE(keyboard2->isKeyRepeatEnabled(), true); QCOMPARE(keyboard2->keyRepeatRate(), 25); QCOMPARE(keyboard2->keyRepeatDelay(), 660); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); serverKeyboard = m_seatInterface->focusedKeyboard(); QVERIFY(serverKeyboard); QSignalSpy keyboard2DestroyedSpy(serverKeyboard, &QObject::destroyed); QVERIFY(keyboard2DestroyedSpy.isValid()); delete keyboard2; QVERIFY(keyboard2DestroyedSpy.wait()); // this should have unset it on the server QVERIFY(!m_seatInterface->focusedKeyboard()); // but not the surface QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); } void TestWaylandSeat::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy seatSpy(®istry, SIGNAL(seatAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(seatSpy.wait()); Seat s; QVERIFY(!s.isValid()); auto wlSeat = registry.bindSeat(seatSpy.first().first().value(), seatSpy.first().last().value()); QVERIFY(wlSeat); s.setup(wlSeat); QVERIFY(s.isValid()); QCOMPARE((wl_seat*)s, wlSeat); const Seat &s2(s); QCOMPARE((wl_seat*)s2, wlSeat); } void TestWaylandSeat::testDestroy() { using namespace KWayland::Client; QSignalSpy keyboardSpy(m_seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); m_seatInterface->setHasKeyboard(true); QVERIFY(keyboardSpy.wait()); Keyboard *k = m_seat->createKeyboard(m_seat); QVERIFY(k->isValid()); QSignalSpy pointerSpy(m_seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); m_seatInterface->setHasPointer(true); QVERIFY(pointerSpy.wait()); Pointer *p = m_seat->createPointer(m_seat); QVERIFY(p->isValid()); QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasTouch(true); QVERIFY(touchSpy.wait()); Touch *t = m_seat->createTouch(m_seat); QVERIFY(t->isValid()); delete m_compositor; m_compositor = nullptr; connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_relativePointerManager, &RelativePointerManager::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_pointerGestures, &PointerGestures::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(m_seat->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; m_compositorInterface = nullptr; m_seatInterface = nullptr; m_subCompositorInterface = nullptr; m_relativePointerManagerInterface = nullptr; m_pointerGesturesInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the seat should be destroyed; QVERIFY(!m_seat->isValid()); QVERIFY(!k->isValid()); QVERIFY(!p->isValid()); QVERIFY(!t->isValid()); // calling destroy again should not fail m_seat->destroy(); k->destroy(); p->destroy(); t->destroy(); } void TestWaylandSeat::testSelection() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, SIGNAL(dataDeviceManagerAnnounced(quint32,quint32))); QVERIFY(dataDeviceManagerSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd1(ddm->getDataDevice(m_seat)); QVERIFY(dd1->isValid()); QSignalSpy selectionSpy(dd1.data(), SIGNAL(selectionOffered(KWayland::Client::DataOffer*))); QVERIFY(selectionSpy.isValid()); QSignalSpy selectionClearedSpy(dd1.data(), SIGNAL(selectionCleared())); QVERIFY(selectionClearedSpy.isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(!m_seatInterface->selection()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedKeyboard()); QVERIFY(selectionClearedSpy.wait()); QVERIFY(selectionSpy.isEmpty()); QVERIFY(!selectionClearedSpy.isEmpty()); selectionClearedSpy.clear(); QVERIFY(!m_seatInterface->selection()); // now let's try to set a selection - we have keyboard focus, so it should be sent to us QScopedPointer ds(ddm->createDataSource()); QVERIFY(ds->isValid()); ds->offer(QStringLiteral("text/plain")); dd1->setSelection(0, ds.data()); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 1); auto ddi = m_seatInterface->selection(); QVERIFY(ddi); auto df = selectionSpy.first().first().value(); QCOMPARE(df->offeredMimeTypes().count(), 1); QCOMPARE(df->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); // try to clear dd1->setSelection(0); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionClearedSpy.count(), 1); QCOMPARE(selectionSpy.count(), 1); // unset the keyboard focus m_seatInterface->setFocusedKeyboardSurface(nullptr); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); serverSurface->client()->flush(); QCoreApplication::processEvents(); QCoreApplication::processEvents(); // try to set Selection dd1->setSelection(0, ds.data()); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QCOMPARE(selectionSpy.count(), 1); // let's unset the selection on the seat m_seatInterface->setSelection(nullptr); // and pass focus back on our surface m_seatInterface->setFocusedKeyboardSurface(serverSurface); // we don't have a selection, so it should not send a selection QVERIFY(!selectionSpy.wait(100)); // now let's set it manually m_seatInterface->setSelection(ddi); QCOMPARE(m_seatInterface->selection(), ddi); QVERIFY(selectionSpy.wait()); QCOMPARE(selectionSpy.count(), 2); // setting the same again should not change m_seatInterface->setSelection(ddi); QVERIFY(!selectionSpy.wait(100)); // now clear it manually m_seatInterface->setSelection(nullptr); QVERIFY(selectionClearedSpy.wait()); QCOMPARE(selectionSpy.count(), 2); // create a second ddi and a data source QScopedPointer dd2(ddm->getDataDevice(m_seat)); QVERIFY(dd2->isValid()); QScopedPointer ds2(ddm->createDataSource()); QVERIFY(ds2->isValid()); ds2->offer(QStringLiteral("text/plain")); dd2->setSelection(0, ds2.data()); QVERIFY(selectionSpy.wait()); QSignalSpy cancelledSpy(ds2.data(), &DataSource::cancelled); QVERIFY(cancelledSpy.isValid()); m_seatInterface->setSelection(ddi); QVERIFY(cancelledSpy.wait()); // Copy already cleared selection, BUG 383054 ddi->sendSelection(ddi); } void TestWaylandSeat::testSelectionNoDataSource() { // this test verifies that the server doesn't crash when using setSelection with // a DataDevice which doesn't have a DataSource yet using namespace KWayland::Client; using namespace KWayland::Server; // first create the DataDevice QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(ddiCreatedSpy.isValid()); Registry registry; QSignalSpy dataDeviceManagerSpy(®istry, &Registry::dataDeviceManagerAnnounced); QVERIFY(dataDeviceManagerSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd(ddm->getDataDevice(m_seat)); QVERIFY(dd->isValid()); QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); // now create a surface and pass it keyboard focus QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(!m_seatInterface->selection()); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); // now let's set the selection m_seatInterface->setSelection(ddi); } void TestWaylandSeat::testDataDeviceForKeyboardSurface() { // this test verifies that the server does not crash when creating a datadevice for the focused keyboard surface // and the currentSelection does not have a DataSource. // to properly test the functionality this test requires a second client using namespace KWayland::Client; using namespace KWayland::Server; // create the DataDeviceManager QScopedPointer ddmi(m_display->createDataDeviceManager()); ddmi->create(); QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated); QVERIFY(ddiCreatedSpy.isValid()); // create a second Wayland client connection to use it for setSelection auto c = new ConnectionThread; QSignalSpy connectedSpy(c, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); c->setSocketName(s_socketName); auto thread = new QThread(this); c->moveToThread(thread); thread->start(); c->initConnection(); QVERIFY(connectedSpy.wait()); QScopedPointer queue(new EventQueue); queue->setup(c); QScopedPointer registry(new Registry); QSignalSpy interfacesAnnouncedSpy(registry.data(), &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry->setEventQueue(queue.data()); registry->create(c); QVERIFY(registry->isValid()); registry->setup(); QVERIFY(interfacesAnnouncedSpy.wait()); QScopedPointer seat(registry->createSeat(registry->interface(Registry::Interface::Seat).name, registry->interface(Registry::Interface::Seat).version)); QVERIFY(seat->isValid()); QScopedPointer ddm1(registry->createDataDeviceManager(registry->interface(Registry::Interface::DataDeviceManager).name, registry->interface(Registry::Interface::DataDeviceManager).version)); QVERIFY(ddm1->isValid()); // now create our first datadevice QScopedPointer dd1(ddm1->getDataDevice(seat.data())); QVERIFY(ddiCreatedSpy.wait()); auto ddi = ddiCreatedSpy.first().first().value(); QVERIFY(ddi); m_seatInterface->setSelection(ddi); // switch to other client // create a surface and pass it keyboard focus QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surface->isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); m_seatInterface->setFocusedKeyboardSurface(serverSurface); QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface); // now create a DataDevice Registry registry2; QSignalSpy dataDeviceManagerSpy(®istry2, &Registry::dataDeviceManagerAnnounced); QVERIFY(dataDeviceManagerSpy.isValid()); registry2.setEventQueue(m_queue); registry2.create(m_connection->display()); QVERIFY(registry2.isValid()); registry2.setup(); QVERIFY(dataDeviceManagerSpy.wait()); QScopedPointer ddm(registry2.createDataDeviceManager(dataDeviceManagerSpy.first().first().value(), dataDeviceManagerSpy.first().last().value())); QVERIFY(ddm->isValid()); QScopedPointer dd(ddm->getDataDevice(m_seat)); QVERIFY(dd->isValid()); QVERIFY(ddiCreatedSpy.wait()); // unset surface and set again m_seatInterface->setFocusedKeyboardSurface(nullptr); m_seatInterface->setFocusedKeyboardSurface(serverSurface); // and delete the connection thread again dd1.reset(); ddm1.reset(); seat.reset(); registry.reset(); queue.reset(); c->deleteLater(); thread->quit(); thread->wait(); delete thread; } void TestWaylandSeat::testTouch() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy touchSpy(m_seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); m_seatInterface->setHasTouch(true); QVERIFY(touchSpy.wait()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); Surface *s = m_compositor->createSurface(m_compositor); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); m_seatInterface->setFocusedTouchSurface(serverSurface); // no keyboard yet QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); QVERIFY(!m_seatInterface->focusedTouch()); QSignalSpy touchCreatedSpy(m_seatInterface, SIGNAL(touchCreated(KWayland::Server::TouchInterface*))); QVERIFY(touchCreatedSpy.isValid()); Touch *touch = m_seat->createTouch(m_seat); QVERIFY(touch->isValid()); QVERIFY(touchCreatedSpy.wait()); auto serverTouch = m_seatInterface->focusedTouch(); QVERIFY(serverTouch); QCOMPARE(touchCreatedSpy.first().first().value(), m_seatInterface->focusedTouch()); QSignalSpy sequenceStartedSpy(touch, SIGNAL(sequenceStarted(KWayland::Client::TouchPoint*))); QVERIFY(sequenceStartedSpy.isValid()); QSignalSpy sequenceEndedSpy(touch, SIGNAL(sequenceEnded())); QVERIFY(sequenceEndedSpy.isValid()); QSignalSpy sequenceCanceledSpy(touch, SIGNAL(sequenceCanceled())); QVERIFY(sequenceCanceledSpy.isValid()); QSignalSpy frameEndedSpy(touch, SIGNAL(frameEnded())); QVERIFY(frameEndedSpy.isValid()); QSignalSpy pointAddedSpy(touch, SIGNAL(pointAdded(KWayland::Client::TouchPoint*))); QVERIFY(pointAddedSpy.isValid()); QSignalSpy pointMovedSpy(touch, SIGNAL(pointMoved(KWayland::Client::TouchPoint*))); QVERIFY(pointMovedSpy.isValid()); QSignalSpy pointRemovedSpy(touch, SIGNAL(pointRemoved(KWayland::Client::TouchPoint*))); QVERIFY(pointRemovedSpy.isValid()); // try a few things m_seatInterface->setFocusedTouchSurfacePosition(QPointF(10, 20)); QCOMPARE(m_seatInterface->focusedTouchSurfacePosition(), QPointF(10, 20)); m_seatInterface->setTimestamp(1); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 0); QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 0); QCOMPARE(pointRemovedSpy.count(), 0); TouchPoint *tp = sequenceStartedSpy.first().first().value(); QVERIFY(tp); QCOMPARE(tp->downSerial(), m_seatInterface->display()->serial()); QCOMPARE(tp->id(), 0); QVERIFY(tp->isDown()); QCOMPARE(tp->position(), QPointF(5, 6)); QCOMPARE(tp->positions().size(), 1); QCOMPARE(tp->time(), 1u); QCOMPARE(tp->timestamps().count(), 1); QCOMPARE(tp->upSerial(), 0u); QCOMPARE(tp->surface().data(), s); QCOMPARE(touch->sequence().count(), 1); QCOMPARE(touch->sequence().first(), tp); // let's end the frame m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(frameEndedSpy.count(), 1); // move the one point m_seatInterface->setTimestamp(2); m_seatInterface->touchMove(0, QPointF(10, 20)); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 2); QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 0); QCOMPARE(pointMovedSpy.first().first().value(), tp); QCOMPARE(tp->id(), 0); QVERIFY(tp->isDown()); QCOMPARE(tp->position(), QPointF(0, 0)); QCOMPARE(tp->positions().size(), 2); QCOMPARE(tp->time(), 2u); QCOMPARE(tp->timestamps().count(), 2); QCOMPARE(tp->upSerial(), 0u); QCOMPARE(tp->surface().data(), s); // add onther point m_seatInterface->setTimestamp(3); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 3); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 0); QCOMPARE(touch->sequence().count(), 2); QCOMPARE(touch->sequence().first(), tp); TouchPoint *tp2 = pointAddedSpy.first().first().value(); QVERIFY(tp2); QCOMPARE(touch->sequence().last(), tp2); QCOMPARE(tp2->id(), 1); QVERIFY(tp2->isDown()); QCOMPARE(tp2->position(), QPointF(5, 6)); QCOMPARE(tp2->positions().size(), 1); QCOMPARE(tp2->time(), 3u); QCOMPARE(tp2->timestamps().count(), 1); QCOMPARE(tp2->upSerial(), 0u); QCOMPARE(tp2->surface().data(), s); // send it an up m_seatInterface->setTimestamp(4); m_seatInterface->touchUp(1); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 4); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.first().first().value(), tp2); QCOMPARE(tp2->id(), 1); QVERIFY(!tp2->isDown()); QCOMPARE(tp2->position(), QPointF(5, 6)); QCOMPARE(tp2->positions().size(), 1); QCOMPARE(tp2->time(), 4u); QCOMPARE(tp2->timestamps().count(), 2); QCOMPARE(tp2->upSerial(), m_seatInterface->display()->serial()); QCOMPARE(tp2->surface().data(), s); // send another down and up m_seatInterface->setTimestamp(5); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->touchFrame(); m_seatInterface->setTimestamp(6); m_seatInterface->touchUp(1); // and send an up for the first point m_seatInterface->touchUp(0); m_seatInterface->touchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 1); QCOMPARE(sequenceCanceledSpy.count(), 0); QCOMPARE(frameEndedSpy.count(), 6); QCOMPARE(pointAddedSpy.count(), 2); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 3); QCOMPARE(touch->sequence().count(), 3); QVERIFY(!touch->sequence().at(0)->isDown()); QVERIFY(!touch->sequence().at(1)->isDown()); QVERIFY(!touch->sequence().at(2)->isDown()); QVERIFY(!m_seatInterface->isTouchSequence()); // try cancel m_seatInterface->setFocusedTouchSurface(serverSurface, QPointF(15, 26)); m_seatInterface->setTimestamp(7); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); m_seatInterface->touchFrame(); m_seatInterface->cancelTouchSequence(); QVERIFY(sequenceCanceledSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 2); QCOMPARE(sequenceEndedSpy.count(), 1); QCOMPARE(sequenceCanceledSpy.count(), 1); QCOMPARE(frameEndedSpy.count(), 7); QCOMPARE(pointAddedSpy.count(), 2); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointRemovedSpy.count(), 3); QCOMPARE(touch->sequence().first()->position(), QPointF(0, 0)); // destroy touch on client side QSignalSpy unboundSpy(serverTouch, &TouchInterface::unbound); QVERIFY(unboundSpy.isValid()); QSignalSpy destroyedSpy(serverTouch, &TouchInterface::destroyed); QVERIFY(destroyedSpy.isValid()); delete touch; QVERIFY(unboundSpy.wait()); QCOMPARE(unboundSpy.count(), 1); QCOMPARE(destroyedSpy.count(), 0); QVERIFY(!serverTouch->resource()); // try to call into all the methods of the touch interface, should not crash QCOMPARE(m_seatInterface->focusedTouch(), serverTouch); m_seatInterface->setTimestamp(8); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 0); m_seatInterface->touchFrame(); m_seatInterface->touchMove(0, QPointF(0, 0)); QCOMPARE(m_seatInterface->touchDown(QPointF(15, 26)), 1); m_seatInterface->cancelTouchSequence(); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); // should have unset the focused touch QVERIFY(!m_seatInterface->focusedTouch()); // but not the focused touch surface QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); } void TestWaylandSeat::testDisconnect() { // this test verifies that disconnecting the client cleans up correctly using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy keyboardCreatedSpy(m_seatInterface, &SeatInterface::keyboardCreated); QVERIFY(keyboardCreatedSpy.isValid()); QSignalSpy pointerCreatedSpy(m_seatInterface, &SeatInterface::pointerCreated); QVERIFY(pointerCreatedSpy.isValid()); QSignalSpy touchCreatedSpy(m_seatInterface, &SeatInterface::touchCreated); QVERIFY(touchCreatedSpy.isValid()); // create the things we need m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); QSignalSpy touchSpy(m_seat, &Seat::hasTouchChanged); QVERIFY(touchSpy.isValid()); QVERIFY(touchSpy.wait()); QScopedPointer keyboard(m_seat->createKeyboard()); QVERIFY(!keyboard.isNull()); QVERIFY(keyboardCreatedSpy.wait()); auto serverKeyboard = keyboardCreatedSpy.first().first().value(); QVERIFY(serverKeyboard); QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); QVERIFY(pointerCreatedSpy.wait()); auto serverPointer = pointerCreatedSpy.first().first().value(); QVERIFY(serverPointer); QScopedPointer touch(m_seat->createTouch()); QVERIFY(!touch.isNull()); QVERIFY(touchCreatedSpy.wait()); auto serverTouch = touchCreatedSpy.first().first().value(); QVERIFY(serverTouch); // setup destroys QSignalSpy keyboardDestroyedSpy(serverKeyboard, &QObject::destroyed); QVERIFY(keyboardDestroyedSpy.isValid()); QSignalSpy pointerDestroyedSpy(serverPointer, &QObject::destroyed); QVERIFY(pointerDestroyedSpy.isValid()); QSignalSpy touchDestroyedSpy(serverTouch, &QObject::destroyed); QVERIFY(touchDestroyedSpy.isValid()); QSignalSpy clientDisconnectedSpy(serverKeyboard->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(keyboardDestroyedSpy.count(), 0); QCOMPARE(pointerDestroyedSpy.count(), 0); QCOMPARE(touchDestroyedSpy.count(), 0); QVERIFY(keyboardDestroyedSpy.wait()); QCOMPARE(keyboardDestroyedSpy.count(), 1); QCOMPARE(pointerDestroyedSpy.count(), 1); QCOMPARE(touchDestroyedSpy.count(), 1); keyboard->destroy(); pointer->destroy(); touch->destroy(); m_relativePointerManager->destroy(); m_pointerGestures->destroy(); m_compositor->destroy(); m_seat->destroy(); m_shm->destroy(); m_subCompositor->destroy(); m_queue->destroy(); } void TestWaylandSeat::testPointerEnterOnUnboundSurface() { using namespace KWayland::Client; using namespace KWayland::Server; // create the things we need m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); QSignalSpy pointerChangedSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(pointerChangedSpy.isValid()); QVERIFY(pointerChangedSpy.wait()); // create pointer and Surface QScopedPointer pointer(m_seat->createPointer()); QVERIFY(!pointer.isNull()); // create the surface QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // unbind the surface again QSignalSpy surfaceUnboundSpy(serverSurface, &SurfaceInterface::unbound); QVERIFY(surfaceUnboundSpy.isValid()); s.reset(); QVERIFY(surfaceUnboundSpy.wait()); QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface); QVERIFY(!clientErrorSpy.wait(100)); } QTEST_GUILESS_MAIN(TestWaylandSeat) #include "test_wayland_seat.moc" diff --git a/autotests/client/test_wayland_shell.cpp b/autotests/client/test_wayland_shell.cpp index 972254e..c48cbf3 100644 --- a/autotests/client/test_wayland_shell.cpp +++ b/autotests/client/test_wayland_shell.cpp @@ -1,922 +1,908 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/pointer.h" #include "../../src/client/seat.h" #include "../../src/client/shell.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include Q_DECLARE_METATYPE(Qt::Edges) class TestWaylandShell : public QObject { Q_OBJECT public: explicit TestWaylandShell(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testCreateMultiple(); void testFullscreen(); void testMaximize(); void testToplevel(); void testTransient_data(); void testTransient(); void testTransientPopup(); void testPing(); void testTitle(); void testWindowClass(); void testDestroy(); void testCast(); void testMove(); void testResize_data(); void testResize(); void testDisconnect(); void testWhileDestroying(); void testClientDisconnecting(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::ShellInterface *m_shellInterface; KWayland::Server::SeatInterface *m_seatInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::Shell *m_shell; KWayland::Client::Seat *m_seat; KWayland::Client::Pointer *m_pointer; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-shell-0"); TestWaylandShell::TestWaylandShell(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_shellInterface(nullptr) , m_seatInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_shell(nullptr) , m_seat(nullptr) , m_pointer(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandShell::initTestCase() { qRegisterMetaType(); } void TestWaylandShell::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_shellInterface = m_display->createShell(m_display); QVERIFY(m_shellInterface); m_shellInterface->create(); QVERIFY(m_shellInterface->isValid()); m_seatInterface = m_display->createSeat(m_display); QVERIFY(m_seatInterface); m_seatInterface->setHasPointer(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy shellSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QSignalSpy seatAnnouncedSpy(®istry, &Registry::seatAnnounced); QVERIFY(seatAnnouncedSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_compositor = new KWayland::Client::Compositor(this); m_compositor->setup(registry.bindCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value())); QVERIFY(m_compositor->isValid()); if (shellSpy.isEmpty()) { QVERIFY(shellSpy.wait()); } m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); QVERIFY(m_shell->isValid()); QVERIFY(!seatAnnouncedSpy.isEmpty()); m_seat = registry.createSeat(seatAnnouncedSpy.first().first().value(), seatAnnouncedSpy.first().last().value(), this); QVERIFY(seatAnnouncedSpy.isValid()); QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerSpy.isValid()); QVERIFY(hasPointerSpy.wait()); QVERIFY(hasPointerSpy.first().first().toBool()); m_pointer = m_seat->createPointer(m_seat); QVERIFY(m_pointer->isValid()); } void TestWaylandShell::cleanup() { if (m_pointer) { delete m_pointer; m_pointer = nullptr; } if (m_seat) { delete m_seat; m_seat = nullptr; } if (m_shell) { delete m_shell; m_shell = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_seatInterface; m_seatInterface = nullptr; delete m_shellInterface; m_shellInterface = nullptr; delete m_compositorInterface; m_compositorInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandShell::testCreateMultiple() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s1(m_compositor->createSurface()); QScopedPointer s2(m_compositor->createSurface()); QVERIFY(!s1.isNull()); QVERIFY(s1->isValid()); QVERIFY(!s2.isNull()); QVERIFY(s2->isValid()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QScopedPointer surface1(m_shell->createSurface(s1.data())); QVERIFY(!surface1.isNull()); QVERIFY(surface1->isValid()); QVERIFY(!ShellSurface::get(nullptr)); QCOMPARE(ShellSurface::get(*(surface1.data())), surface1.data()); QVERIFY(serverSurfaceSpy.wait()); QCOMPARE(serverSurfaceSpy.count(), 1); QScopedPointer surface2(m_shell->createSurface(s2.data())); QVERIFY(!surface2.isNull()); QVERIFY(surface2->isValid()); QCOMPARE(ShellSurface::get(*(surface2.data())), surface2.data()); QVERIFY(serverSurfaceSpy.wait()); QCOMPARE(serverSurfaceSpy.count(), 2); // try creating for one which already exist should not be possible QScopedPointer surface3(m_shell->createSurface(s2.data())); QVERIFY(!surface3.isNull()); QVERIFY(surface3->isValid()); QCOMPARE(ShellSurface::get(*(surface3.data())), surface3.data()); QVERIFY(!serverSurfaceSpy.wait(100)); QCOMPARE(serverSurfaceSpy.count(), 2); } void TestWaylandShell::testFullscreen() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QCOMPARE(serverSurface->shell(), m_shellInterface); QSignalSpy fullscreenSpy(serverSurface, SIGNAL(fullscreenChanged(bool))); QVERIFY(fullscreenSpy.isValid()); QVERIFY(!serverSurface->isFullscreen()); surface->setFullscreen(); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 1); QVERIFY(fullscreenSpy.first().first().toBool()); QVERIFY(serverSurface->isFullscreen()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to toplevel fullscreenSpy.clear(); wl_shell_surface_set_toplevel(*surface); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 1); QVERIFY(!fullscreenSpy.first().first().toBool()); QVERIFY(!serverSurface->isFullscreen()); } void TestWaylandShell::testMaximize() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QSignalSpy maximizedSpy(serverSurface, SIGNAL(maximizedChanged(bool))); QVERIFY(maximizedSpy.isValid()); QVERIFY(!serverSurface->isMaximized()); surface->setMaximized(); QVERIFY(maximizedSpy.wait()); QCOMPARE(maximizedSpy.count(), 1); QVERIFY(maximizedSpy.first().first().toBool()); QVERIFY(serverSurface->isMaximized()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to toplevel maximizedSpy.clear(); wl_shell_surface_set_toplevel(*surface); QVERIFY(maximizedSpy.wait()); QCOMPARE(maximizedSpy.count(), 1); QVERIFY(!maximizedSpy.first().first().toBool()); QVERIFY(!serverSurface->isMaximized()); } void TestWaylandShell::testToplevel() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QSignalSpy toplevelSpy(serverSurface, SIGNAL(toplevelChanged(bool))); QVERIFY(toplevelSpy.isValid()); surface->setFullscreen(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(!toplevelSpy.first().first().toBool()); toplevelSpy.clear(); surface->setToplevel(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(toplevelSpy.first().first().toBool()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to fullscreen toplevelSpy.clear(); surface->setFullscreen(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(!toplevelSpy.first().first().toBool()); } void TestWaylandShell::testTransient_data() { QTest::addColumn("keyboardFocus"); QTest::newRow("focus") << true; QTest::newRow("no focus") << false; } void TestWaylandShell::testTransient() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy acceptsKeyboardFocusChangedSpy(serverSurface, &ShellSurfaceInterface::acceptsKeyboardFocusChanged); QVERIFY(acceptsKeyboardFocusChangedSpy.isValid()); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), false); QCOMPARE(serverSurface->transientFor(), QPointer()); QCOMPARE(serverSurface->transientOffset(), QPoint()); QVERIFY(serverSurface->acceptsKeyboardFocus()); QVERIFY(acceptsKeyboardFocusChangedSpy.isEmpty()); QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); QVERIFY(transientSpy.isValid()); QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); QVERIFY(transientOffsetSpy.isValid()); QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); QVERIFY(transientForChangedSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); m_shell->createSurface(s2.data(), m_shell); serverSurfaceSpy.clear(); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface2 != serverSurface); QVERIFY(serverSurface2); QVERIFY(serverSurface2->acceptsKeyboardFocus()); QFETCH(bool, keyboardFocus); surface->setTransient(s2.data(), QPoint(10, 20), keyboardFocus ? ShellSurface::TransientFlag::Default : ShellSurface::TransientFlag::NoFocus); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.count(), 1); QCOMPARE(transientSpy.first().first().toBool(), true); QCOMPARE(transientOffsetSpy.count(), 1); QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); QCOMPARE(transientForChangedSpy.count(), 1); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), true); QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); QCOMPARE(serverSurface->acceptsKeyboardFocus(), keyboardFocus); QCOMPARE(acceptsKeyboardFocusChangedSpy.isEmpty(), keyboardFocus); QCOMPARE(acceptsKeyboardFocusChangedSpy.count(), keyboardFocus ? 0 : 1); QCOMPARE(serverSurface2->isToplevel(), true); QCOMPARE(serverSurface2->isPopup(), false); QCOMPARE(serverSurface2->isTransient(), false); QCOMPARE(serverSurface2->transientFor(), QPointer()); QCOMPARE(serverSurface2->transientOffset(), QPoint()); QVERIFY(serverSurface2->acceptsKeyboardFocus()); } void TestWaylandShell::testTransientPopup() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), false); QCOMPARE(serverSurface->transientFor(), QPointer()); QCOMPARE(serverSurface->transientOffset(), QPoint()); QVERIFY(serverSurface->acceptsKeyboardFocus()); QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); QVERIFY(transientSpy.isValid()); QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); QVERIFY(transientOffsetSpy.isValid()); QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); QVERIFY(transientForChangedSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); m_shell->createSurface(s2.data(), m_shell); serverSurfaceSpy.clear(); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface2 != serverSurface); QVERIFY(serverSurface2); QVERIFY(serverSurface2->acceptsKeyboardFocus()); // TODO: proper serial checking surface->setTransientPopup(s2.data(), m_seat, 1, QPoint(10, 20)); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.count(), 1); QCOMPARE(transientSpy.first().first().toBool(), true); QCOMPARE(transientOffsetSpy.count(), 1); QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); QCOMPARE(transientForChangedSpy.count(), 1); QCOMPARE(serverSurface->isToplevel(), false); QCOMPARE(serverSurface->isPopup(), true); QCOMPARE(serverSurface->isTransient(), true); QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); // TODO: honor the flag QCOMPARE(serverSurface->acceptsKeyboardFocus(), false); QCOMPARE(serverSurface2->isToplevel(), true); QCOMPARE(serverSurface2->isPopup(), false); QCOMPARE(serverSurface2->isTransient(), false); QCOMPARE(serverSurface2->transientFor(), QPointer()); QCOMPARE(serverSurface2->transientOffset(), QPoint()); // send popup done QSignalSpy popupDoneSpy(surface, &ShellSurface::popupDone); QVERIFY(popupDoneSpy.isValid()); serverSurface->popupDone(); QVERIFY(popupDoneSpy.wait()); } void TestWaylandShell::testPing() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy pingSpy(surface, SIGNAL(pinged())); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(!serverSurface->isPinged()); QSignalSpy pingTimeoutSpy(serverSurface, SIGNAL(pingTimeout())); QVERIFY(pingTimeoutSpy.isValid()); QSignalSpy pongSpy(serverSurface, SIGNAL(pongReceived())); QVERIFY(pongSpy.isValid()); serverSurface->ping(); QVERIFY(serverSurface->isPinged()); QVERIFY(pingSpy.wait()); wl_display_flush(m_connection->display()); if (pongSpy.isEmpty()) { QVERIFY(pongSpy.wait()); } QVERIFY(!pongSpy.isEmpty()); QVERIFY(pingTimeoutSpy.isEmpty()); // evil trick - timeout of zero will make it not get the pong serverSurface->setPingTimeout(0); pongSpy.clear(); pingTimeoutSpy.clear(); serverSurface->ping(); QTest::qWait(100); if (pingTimeoutSpy.isEmpty()) { QVERIFY(pingTimeoutSpy.wait()); } QCOMPARE(pingTimeoutSpy.count(), 1); QVERIFY(pongSpy.isEmpty()); } void TestWaylandShell::testTitle() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy titleSpy(serverSurface, SIGNAL(titleChanged(QString))); QVERIFY(titleSpy.isValid()); QString testTitle = QStringLiteral("fooBar"); QVERIFY(serverSurface->title().isNull()); surface->setTitle(testTitle); QVERIFY(titleSpy.wait()); QCOMPARE(serverSurface->title(), testTitle); QCOMPARE(titleSpy.first().first().toString(), testTitle); } void TestWaylandShell::testWindowClass() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy windowClassSpy(serverSurface, SIGNAL(windowClassChanged(QByteArray))); QVERIFY(windowClassSpy.isValid()); QByteArray testClass = QByteArrayLiteral("fooBar"); QVERIFY(serverSurface->windowClass().isNull()); surface->setWindowClass(testClass); QVERIFY(windowClassSpy.wait()); QCOMPARE(serverSurface->windowClass(), testClass); QCOMPARE(windowClassSpy.first().first().toByteArray(), testClass); // try setting it to same should not trigger the signal surface->setWindowClass(testClass); QVERIFY(!windowClassSpy.wait(100)); } void TestWaylandShell::testDestroy() { using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QVERIFY(surface->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_shell, &Shell::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_pointer, &Pointer::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, s.data(), &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; m_compositorInterface = nullptr; m_shellInterface = nullptr; m_seatInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); QVERIFY(!m_shell->isValid()); QVERIFY(!surface->isValid()); m_shell->destroy(); surface->destroy(); } void TestWaylandShell::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy shellSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(shellSpy.wait()); Shell s; auto wlShell = registry.bindShell(shellSpy.first().first().value(), shellSpy.first().last().value()); m_queue->addProxy(wlShell); QVERIFY(wlShell); QVERIFY(!s.isValid()); s.setup(wlShell); QVERIFY(s.isValid()); QCOMPARE((wl_shell*)s, wlShell); const Shell &s2(s); QCOMPARE((wl_shell*)s2, wlShell); } void TestWaylandShell::testMove() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy moveRequestedSpy(serverSurface, &ShellSurfaceInterface::moveRequested); QVERIFY(moveRequestedSpy.isValid()); QSignalSpy pointerButtonChangedSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(pointerButtonChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface->surface()); m_seatInterface->pointerButtonPressed(Qt::LeftButton); QVERIFY(pointerButtonChangedSpy.wait()); surface->requestMove(m_seat, pointerButtonChangedSpy.first().first().value()); QVERIFY(moveRequestedSpy.wait()); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.first().at(0).value(), m_seatInterface); QCOMPARE(moveRequestedSpy.first().at(1).value(), m_seatInterface->pointerButtonSerial(Qt::LeftButton)); } void TestWaylandShell::testResize_data() { QTest::addColumn("resizeEdge"); QTest::addColumn("expectedEdge"); QTest::newRow("None") << Qt::Edges() << Qt::Edges(); QTest::newRow("Top") << Qt::Edges(Qt::TopEdge) << Qt::Edges(Qt::TopEdge); QTest::newRow("Bottom") << Qt::Edges(Qt::BottomEdge) << Qt::Edges(Qt::BottomEdge); QTest::newRow("Left") << Qt::Edges(Qt::LeftEdge) << Qt::Edges(Qt::LeftEdge); QTest::newRow("Right") << Qt::Edges(Qt::RightEdge) << Qt::Edges(Qt::RightEdge); QTest::newRow("Top Left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << Qt::Edges(Qt::TopEdge | Qt::LeftEdge); QTest::newRow("Top Right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << Qt::Edges(Qt::TopEdge | Qt::RightEdge); QTest::newRow("Bottom Left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge); QTest::newRow("Bottom Right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << Qt::Edges(Qt::BottomEdge | Qt::RightEdge); // invalid combinations QTest::newRow("Top Bottom") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge) << Qt::Edges(); QTest::newRow("Left Right") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge) << Qt::Edges(); QTest::newRow("Top Bottom Right") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge | Qt::RightEdge) << Qt::Edges(); QTest::newRow("Top Bottom Left") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge | Qt::LeftEdge) << Qt::Edges(); QTest::newRow("Left Right Top") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::TopEdge) << Qt::Edges(); QTest::newRow("Left Right Bottom") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::BottomEdge) << Qt::Edges(); QTest::newRow("All") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::BottomEdge | Qt::TopEdge) << Qt::Edges(); } void TestWaylandShell::testResize() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy resizeRequestedSpy(serverSurface, &ShellSurfaceInterface::resizeRequested); QVERIFY(resizeRequestedSpy.isValid()); QSignalSpy pointerButtonChangedSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(pointerButtonChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface->surface()); m_seatInterface->pointerButtonPressed(Qt::LeftButton); QVERIFY(pointerButtonChangedSpy.wait()); QFETCH(Qt::Edges, resizeEdge); surface->requestResize(m_seat, pointerButtonChangedSpy.first().first().value(), resizeEdge); QVERIFY(resizeRequestedSpy.wait()); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.first().at(0).value(), m_seatInterface); QCOMPARE(resizeRequestedSpy.first().at(1).value(), m_seatInterface->pointerButtonSerial(Qt::LeftButton)); QTEST(resizeRequestedSpy.first().at(2).value(), "expectedEdge"); } void TestWaylandShell::testDisconnect() { // this test verifies that the server side correctly tears down the resources when the client disconnects using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QScopedPointer surface(m_shell->createSurface(s.data(), m_shell)); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); // destroy client QSignalSpy clientDisconnectedSpy(serverSurface->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy shellSurfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(shellSurfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(shellSurfaceDestroyedSpy.count(), 0); // it's already unbound, but the shell surface is not yet destroyed QVERIFY(!serverSurface->resource()); // ensure we don't crash when accessing it in this state serverSurface->ping(); serverSurface->requestSize(QSize(1, 2)); QVERIFY(shellSurfaceDestroyedSpy.wait()); QCOMPARE(shellSurfaceDestroyedSpy.count(), 1); s->destroy(); surface->destroy(); m_shell->destroy(); m_compositor->destroy(); m_pointer->destroy(); m_seat->destroy(); m_queue->destroy(); } void TestWaylandShell::testWhileDestroying() { // this test tries to hit a condition that a Surface gets created with an ID which was already // used for a previous Surface. For each Surface we try to create a ShellSurface. // Even if there was a Surface in the past with the same ID, it should create the ShellSurface using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // create ShellSurface QSignalSpy shellSurfaceCreatedSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_shell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); // now try to create more surfaces QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); for (int i = 0; i < 100; i++) { s.reset(); ps.reset(); s.reset(m_compositor->createSurface()); ps.reset(m_shell->createSurface(s.data())); QVERIFY(surfaceCreatedSpy.wait()); } QVERIFY(clientErrorSpy.isEmpty()); QVERIFY(!clientErrorSpy.wait(100)); QVERIFY(clientErrorSpy.isEmpty()); } void TestWaylandShell::testClientDisconnecting() { // this test tries to request a new surface size while the client is actually already destroyed // see BUG: 370232 using namespace KWayland::Client; using namespace KWayland::Server; // create ShellSurface QScopedPointer s(m_compositor->createSurface()); QSignalSpy shellSurfaceCreatedSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_shell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); auto serverShellSurface = shellSurfaceCreatedSpy.first().first().value(); QVERIFY(serverShellSurface); QSignalSpy shellSurfaceUnboundSpy(serverShellSurface, &Resource::unbound); QVERIFY(shellSurfaceUnboundSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); QScopedPointer ps2(m_shell->createSurface(s2.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); auto serverShellSurface2 = shellSurfaceCreatedSpy.last().first().value(); QVERIFY(serverShellSurface2); connect(serverShellSurface, &Resource::unbound, this, [serverShellSurface, serverShellSurface2] { serverShellSurface2->requestSize(QSize(100, 200)); } ); m_connection->deleteLater(); m_connection = nullptr; m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; QVERIFY(shellSurfaceUnboundSpy.wait()); ps->destroy(); s->destroy(); ps2->destroy(); s2->destroy(); m_pointer->destroy(); m_seat->destroy(); m_shell->destroy(); m_compositor->destroy(); m_queue->destroy(); } QTEST_GUILESS_MAIN(TestWaylandShell) #include "test_wayland_shell.moc" diff --git a/autotests/client/test_wayland_slide.cpp b/autotests/client/test_wayland_slide.cpp index 72e5dc2..c4014c3 100644 --- a/autotests/client/test_wayland_slide.cpp +++ b/autotests/client/test_wayland_slide.cpp @@ -1,216 +1,202 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/slide.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/slide_interface.h" using namespace KWayland::Client; class TestSlide : public QObject { Q_OBJECT public: explicit TestSlide(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testSurfaceDestroy(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::SlideManagerInterface *m_slideManagerInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::SlideManager *m_slideManager; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-slide-0"); TestSlide::TestSlide(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestSlide::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy slideSpy(®istry, &Registry::slideAnnounced); QVERIFY(slideSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_slideManagerInterface = m_display->createSlideManager(m_display); m_slideManagerInterface->create(); QVERIFY(m_slideManagerInterface->isValid()); QVERIFY(slideSpy.wait()); m_slideManager = registry.createSlideManager(slideSpy.first().first().value(), slideSpy.first().last().value(), this); } void TestSlide::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_slideManager) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_slideManagerInterface) CLEANUP(m_display) #undef CLEANUP } void TestSlide::testCreate() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy slideChanged(serverSurface, SIGNAL(slideOnShowHideChanged())); auto slide = m_slideManager->createSlide(surface.data(), surface.data()); slide->setLocation(KWayland::Client::Slide::Location::Top); slide->setOffset(15); slide->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(slideChanged.wait()); QCOMPARE(serverSurface->slideOnShowHide()->location(), KWayland::Server::SlideInterface::Location::Top); QCOMPARE(serverSurface->slideOnShowHide()->offset(), 15); // and destroy QSignalSpy destroyedSpy(serverSurface->slideOnShowHide().data(), &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete slide; QVERIFY(destroyedSpy.wait()); } void TestSlide::testSurfaceDestroy() { using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy slideChanged(serverSurface, &SurfaceInterface::slideOnShowHideChanged); QVERIFY(slideChanged.isValid()); QScopedPointer slide(m_slideManager->createSlide(surface.data())); slide->commit(); surface->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(slideChanged.wait()); auto serverSlide = serverSurface->slideOnShowHide(); QVERIFY(!serverSlide.isNull()); // destroy the parent surface QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); QSignalSpy slideDestroyedSpy(serverSlide.data(), &QObject::destroyed); QVERIFY(slideDestroyedSpy.isValid()); surface.reset(); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(slideDestroyedSpy.isEmpty()); // destroy the slide slide.reset(); QVERIFY(slideDestroyedSpy.wait()); } QTEST_GUILESS_MAIN(TestSlide) #include "test_wayland_slide.moc" diff --git a/autotests/client/test_wayland_subcompositor.cpp b/autotests/client/test_wayland_subcompositor.cpp index effe95f..646e8bb 100644 --- a/autotests/client/test_wayland_subcompositor.cpp +++ b/autotests/client/test_wayland_subcompositor.cpp @@ -1,173 +1,159 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/subcompositor.h" #include "../../src/server/display.h" #include "../../src/server/subcompositor_interface.h" class TestSubCompositor : public QObject { Q_OBJECT public: explicit TestSubCompositor(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testDestroy(); void testCast(); private: KWayland::Server::Display *m_display; KWayland::Server::SubCompositorInterface *m_subcompositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::SubCompositor *m_subCompositor; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-subcompositor-0"); TestSubCompositor::TestSubCompositor(QObject *parent) : QObject(parent) , m_display(nullptr) , m_subcompositorInterface(nullptr) , m_connection(nullptr) , m_subCompositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestSubCompositor::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32))); QVERIFY(subCompositorSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_subcompositorInterface = m_display->createSubCompositor(m_display); QVERIFY(m_subcompositorInterface); m_subcompositorInterface->create(); QVERIFY(m_subcompositorInterface->isValid()); QVERIFY(subCompositorSpy.wait()); m_subCompositor = registry.createSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value(), this); } void TestSubCompositor::cleanup() { if (m_subCompositor) { delete m_subCompositor; m_subCompositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestSubCompositor::testDestroy() { using namespace KWayland::Client; connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QVERIFY(m_subCompositor->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!m_subCompositor->isValid()); // calling destroy again should not fail m_subCompositor->destroy(); } void TestSubCompositor::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(subCompositorSpy.wait()); SubCompositor c; auto wlSubComp = registry.bindSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value()); c.setup(wlSubComp); QCOMPARE((wl_subcompositor*)c, wlSubComp); const SubCompositor &c2(c); QCOMPARE((wl_subcompositor*)c2, wlSubComp); } QTEST_GUILESS_MAIN(TestSubCompositor) #include "test_wayland_subcompositor.moc" diff --git a/autotests/client/test_wayland_subsurface.cpp b/autotests/client/test_wayland_subsurface.cpp index 654b63e..6da8f36 100644 --- a/autotests/client/test_wayland_subsurface.cpp +++ b/autotests/client/test_wayland_subsurface.cpp @@ -1,1111 +1,1097 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/client/subcompositor.h" #include "../../src/client/subsurface.h" #include "../../src/client/surface.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/subcompositor_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include class TestSubSurface : public QObject { Q_OBJECT public: explicit TestSubSurface(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testMode(); void testPosition(); void testPlaceAbove(); void testPlaceBelow(); void testDestroy(); void testCast(); void testSyncMode(); void testDeSyncMode(); void testMainSurfaceFromTree(); void testRemoveSurface(); void testMappingOfSurfaceTree(); void testSurfaceAt(); void testDestroyAttachedBuffer(); void testDestroyParentSurface(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::SubCompositorInterface *m_subcompositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shm; KWayland::Client::SubCompositor *m_subCompositor; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-subsurface-0"); TestSubSurface::TestSubSurface(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_subcompositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_shm(nullptr) , m_subCompositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestSubSurface::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QSignalSpy subCompositorSpy(®istry, SIGNAL(subCompositorAnnounced(quint32,quint32))); QVERIFY(subCompositorSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_subcompositorInterface = m_display->createSubCompositor(m_display); QVERIFY(m_subcompositorInterface); m_subcompositorInterface->create(); QVERIFY(m_subcompositorInterface->isValid()); QVERIFY(subCompositorSpy.wait()); m_subCompositor = registry.createSubCompositor(subCompositorSpy.first().first().value(), subCompositorSpy.first().last().value(), this); if (compositorSpy.isEmpty()) { QVERIFY(compositorSpy.wait()); } m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_shm = registry.createShmPool(registry.interface(KWayland::Client::Registry::Interface::Shm).name, registry.interface(KWayland::Client::Registry::Interface::Shm).version, this); QVERIFY(m_shm->isValid()); } void TestSubSurface::cleanup() { if (m_shm) { delete m_shm; m_shm = nullptr; } if (m_subCompositor) { delete m_subCompositor; m_subCompositor = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestSubSurface::testCreate() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(surfaceCreatedSpy.isValid()); // create two Surfaces QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); surfaceCreatedSpy.clear(); QScopedPointer parent(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); SurfaceInterface *serverParentSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverParentSurface); QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*))); QVERIFY(subSurfaceCreatedSpy.isValid()); // create subSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface); QVERIFY(serverSubSurface->parentSurface()); QCOMPARE(serverSubSurface->parentSurface().data(), serverParentSurface); QCOMPARE(serverSubSurface->surface().data(), serverSurface); QCOMPARE(serverSurface->subSurface().data(), serverSubSurface); QCOMPARE(serverSubSurface->mainSurface().data(), serverParentSurface); // children are only added after committing the surface QEXPECT_FAIL("", "Incorrect adding of child windows to workaround QtWayland behavior", Continue); QCOMPARE(serverParentSurface->childSubSurfaces().count(), 0); // so let's commit the surface, to apply the stacking change parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverParentSurface->childSubSurfaces().count(), 1); QCOMPARE(serverParentSurface->childSubSurfaces().first().data(), serverSubSurface); // and let's destroy it again QSignalSpy destroyedSpy(serverSubSurface, SIGNAL(destroyed(QObject*))); QVERIFY(destroyedSpy.isValid()); subSurface.reset(); QVERIFY(destroyedSpy.wait()); QCOMPARE(serverSurface->subSurface(), QPointer()); // only applied after next commit QEXPECT_FAIL("", "Incorrect removing of child windows to workaround QtWayland behavior", Continue); QCOMPARE(serverParentSurface->childSubSurfaces().count(), 1); // but the surface should be invalid if (!serverParentSurface->childSubSurfaces().isEmpty()) { QVERIFY(serverParentSurface->childSubSurfaces().first().isNull()); } // committing the state should solve it parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverParentSurface->childSubSurfaces().count(), 0); } void TestSubSurface::testMode() { using namespace KWayland::Client; using namespace KWayland::Server; // create two Surface QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*))); QVERIFY(subSurfaceCreatedSpy.isValid()); // create the SubSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface); // both client and server subsurface should be in synchronized mode QCOMPARE(subSurface->mode(), SubSurface::Mode::Synchronized); QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Synchronized); // verify that we can change to desynchronized QSignalSpy modeChangedSpy(serverSubSurface, SIGNAL(modeChanged(KWayland::Server::SubSurfaceInterface::Mode))); QVERIFY(modeChangedSpy.isValid()); subSurface->setMode(SubSurface::Mode::Desynchronized); QCOMPARE(subSurface->mode(), SubSurface::Mode::Desynchronized); QVERIFY(modeChangedSpy.wait()); QCOMPARE(modeChangedSpy.first().first().value(), SubSurfaceInterface::Mode::Desynchronized); QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Desynchronized); // setting the same again won't change subSurface->setMode(SubSurface::Mode::Desynchronized); QCOMPARE(subSurface->mode(), SubSurface::Mode::Desynchronized); // not testing the signal, we do that after changing to synchronized // and change back to synchronized subSurface->setMode(SubSurface::Mode::Synchronized); QCOMPARE(subSurface->mode(), SubSurface::Mode::Synchronized); QVERIFY(modeChangedSpy.wait()); QCOMPARE(modeChangedSpy.count(), 2); QCOMPARE(modeChangedSpy.first().first().value(), SubSurfaceInterface::Mode::Desynchronized); QCOMPARE(modeChangedSpy.last().first().value(), SubSurfaceInterface::Mode::Synchronized); QCOMPARE(serverSubSurface->mode(), SubSurfaceInterface::Mode::Synchronized); } void TestSubSurface::testPosition() { using namespace KWayland::Client; using namespace KWayland::Server; // create two Surface QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*))); QVERIFY(subSurfaceCreatedSpy.isValid()); // create the SubSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface); // create a signalspy QSignalSpy subsurfaceTreeChanged(serverSubSurface->parentSurface().data(), &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subsurfaceTreeChanged.isValid()); // both client and server should have a default position QCOMPARE(subSurface->position(), QPoint()); QCOMPARE(serverSubSurface->position(), QPoint()); QSignalSpy positionChangedSpy(serverSubSurface, SIGNAL(positionChanged(QPoint))); QVERIFY(positionChangedSpy.isValid()); // changing the position should not trigger a direct update on server side subSurface->setPosition(QPoint(10, 20)); QCOMPARE(subSurface->position(), QPoint(10, 20)); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface->position(), QPoint()); // changing once more subSurface->setPosition(QPoint(20, 30)); QCOMPARE(subSurface->position(), QPoint(20, 30)); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface->position(), QPoint()); // committing the parent surface should update the position parent->commit(Surface::CommitFlag::None); QCOMPARE(subsurfaceTreeChanged.count(), 0); QVERIFY(positionChangedSpy.wait()); QCOMPARE(positionChangedSpy.count(), 1); QCOMPARE(positionChangedSpy.first().first().toPoint(), QPoint(20, 30)); QCOMPARE(serverSubSurface->position(), QPoint(20, 30)); QCOMPARE(subsurfaceTreeChanged.count(), 1); } void TestSubSurface::testPlaceAbove() { using namespace KWayland::Client; using namespace KWayland::Server; // create needed Surfaces (one parent, three client QScopedPointer surface1(m_compositor->createSurface()); QScopedPointer surface2(m_compositor->createSurface()); QScopedPointer surface3(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*))); QVERIFY(subSurfaceCreatedSpy.isValid()); // create the SubSurfaces for surface of parent QScopedPointer subSurface1(m_subCompositor->createSubSurface(QPointer(surface1.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface1 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface1); subSurfaceCreatedSpy.clear(); QScopedPointer subSurface2(m_subCompositor->createSubSurface(QPointer(surface2.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface2 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface2); subSurfaceCreatedSpy.clear(); QScopedPointer subSurface3(m_subCompositor->createSubSurface(QPointer(surface3.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface3 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface3); subSurfaceCreatedSpy.clear(); // so far the stacking order should still be empty QEXPECT_FAIL("", "Incorrect adding of child windows to workaround QtWayland behavior", Continue); QVERIFY(serverSubSurface1->parentSurface()->childSubSurfaces().isEmpty()); // committing the parent should create the stacking order parent->commit(Surface::CommitFlag::None); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3); // raising subsurface1 should place it to top of stack subSurface1->raise(); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); // but as long as parent is not committed it shouldn't change on server side QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); // after commit it's changed parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1); // try placing 3 above 1, should result in 2, 1, 3 subSurface3->placeAbove(QPointer(subSurface1.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3); // try placing 3 above 2, should result in 2, 3, 1 subSurface3->placeAbove(QPointer(subSurface2.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1); // try placing 1 above 3 - shouldn't change subSurface1->placeAbove(QPointer(subSurface3.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1); // and 2 above 3 - > 3, 2, 1 subSurface2->placeAbove(QPointer(subSurface3.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface1); } void TestSubSurface::testPlaceBelow() { using namespace KWayland::Client; using namespace KWayland::Server; // create needed Surfaces (one parent, three client QScopedPointer surface1(m_compositor->createSurface()); QScopedPointer surface2(m_compositor->createSurface()); QScopedPointer surface3(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); QSignalSpy subSurfaceCreatedSpy(m_subcompositorInterface, SIGNAL(subSurfaceCreated(KWayland::Server::SubSurfaceInterface*))); QVERIFY(subSurfaceCreatedSpy.isValid()); // create the SubSurfaces for surface of parent QScopedPointer subSurface1(m_subCompositor->createSubSurface(QPointer(surface1.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface1 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface1); subSurfaceCreatedSpy.clear(); QScopedPointer subSurface2(m_subCompositor->createSubSurface(QPointer(surface2.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface2 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface2); subSurfaceCreatedSpy.clear(); QScopedPointer subSurface3(m_subCompositor->createSubSurface(QPointer(surface3.data()), QPointer(parent.data()))); QVERIFY(subSurfaceCreatedSpy.wait()); SubSurfaceInterface *serverSubSurface3 = subSurfaceCreatedSpy.first().first().value(); QVERIFY(serverSubSurface3); subSurfaceCreatedSpy.clear(); // so far the stacking order should still be empty QEXPECT_FAIL("", "Incorrect adding of child windows to workaround QtWayland behavior", Continue); QVERIFY(serverSubSurface1->parentSurface()->childSubSurfaces().isEmpty()); // committing the parent should create the stacking order parent->commit(Surface::CommitFlag::None); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3); // lowering subsurface3 should place it to the bottom of stack subSurface3->lower(); // ensure it's processed on server side wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); // but as long as parent is not committed it shouldn't change on server side QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); // after commit it's changed parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2); // place 1 below 3 -> 1, 3, 2 subSurface1->placeBelow(QPointer(subSurface3.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2); // 2 below 3 -> 1, 2, 3 subSurface2->placeBelow(QPointer(subSurface3.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3); // 1 below 2 -> shouldn't change subSurface1->placeBelow(QPointer(subSurface2.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface2); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface3); // and 3 below 1 -> 3, 1, 2 subSurface3->placeBelow(QPointer(subSurface1.data())); parent->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().count(), 3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(0).data(), serverSubSurface3); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(1).data(), serverSubSurface1); QCOMPARE(serverSubSurface1->parentSurface()->childSubSurfaces().at(2).data(), serverSubSurface2); } void TestSubSurface::testDestroy() { using namespace KWayland::Client; // create two Surfaces QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); // create subSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_subCompositor, &SubCompositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); connect(m_connection, &ConnectionThread::connectionDied, surface.data(), &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, parent.data(), &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, subSurface.data(), &SubSurface::destroy); QVERIFY(subSurface->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!subSurface->isValid()); // calling destroy again should not fail subSurface->destroy(); } void TestSubSurface::testCast() { using namespace KWayland::Client; // create two Surfaces QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parent(m_compositor->createSurface()); // create subSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QCOMPARE(SubSurface::get(*(subSurface.data())), QPointer(subSurface.data())); } void TestSubSurface::testSyncMode() { // this test verifies that state is only applied when the parent surface commits its pending state using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(childSurface); QScopedPointer parent(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto parentSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(parentSurface); QSignalSpy subSurfaceTreeChangedSpy(parentSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subSurfaceTreeChangedSpy.isValid()); // create subSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(subSurfaceTreeChangedSpy.count(), 1); // let's damage the child surface QSignalSpy childDamagedSpy(childSurface, &SurfaceInterface::damaged); QVERIFY(childDamagedSpy.isValid()); QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 200, 200)); surface->commit(); // state should be applied when the parent surface's state gets applied QVERIFY(!childDamagedSpy.wait(100)); QVERIFY(!childSurface->buffer()); QVERIFY(!childSurface->isMapped()); QVERIFY(!parentSurface->isMapped()); QImage image2(QSize(400, 400), QImage::Format_ARGB32_Premultiplied); image2.fill(Qt::red); parent->attachBuffer(m_shm->createBuffer(image2)); parent->damage(QRect(0, 0, 400, 400)); parent->commit(); QVERIFY(childDamagedSpy.wait()); QCOMPARE(childDamagedSpy.count(), 1); QCOMPARE(subSurfaceTreeChangedSpy.count(), 2); QCOMPARE(childSurface->buffer()->data(), image); QCOMPARE(parentSurface->buffer()->data(), image2); QVERIFY(childSurface->isMapped()); QVERIFY(parentSurface->isMapped()); // sending frame rendered to parent should also send it to child QSignalSpy frameRenderedSpy(surface.data(), &Surface::frameRendered); QVERIFY(frameRenderedSpy.isValid()); parentSurface->frameRendered(100); QVERIFY(frameRenderedSpy.wait()); } void TestSubSurface::testDeSyncMode() { // this test verifies that state gets applied immediately in desync mode using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(childSurface); QScopedPointer parent(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto parentSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(parentSurface); QSignalSpy subSurfaceTreeChangedSpy(parentSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subSurfaceTreeChangedSpy.isValid()); // create subSurface for surface of parent QScopedPointer subSurface(m_subCompositor->createSubSurface(QPointer(surface.data()), QPointer(parent.data()))); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(subSurfaceTreeChangedSpy.count(), 1); // let's damage the child surface QSignalSpy childDamagedSpy(childSurface, &SurfaceInterface::damaged); QVERIFY(childDamagedSpy.isValid()); QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 200, 200)); surface->commit(Surface::CommitFlag::None); // state should be applied when the parent surface's state gets applied or when the subsurface switches to desync QVERIFY(!childDamagedSpy.wait(100)); QVERIFY(!childSurface->isMapped()); QVERIFY(!parentSurface->isMapped()); // setting to desync should apply the state directly subSurface->setMode(SubSurface::Mode::Desynchronized); QVERIFY(childDamagedSpy.wait()); QCOMPARE(subSurfaceTreeChangedSpy.count(), 2); QCOMPARE(childSurface->buffer()->data(), image); QVERIFY(!childSurface->isMapped()); QVERIFY(!parentSurface->isMapped()); // and damaging again, should directly be applied image.fill(Qt::red); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 200, 200)); surface->commit(Surface::CommitFlag::None); QVERIFY(childDamagedSpy.wait()); QCOMPARE(subSurfaceTreeChangedSpy.count(), 3); QCOMPARE(childSurface->buffer()->data(), image); } void TestSubSurface::testMainSurfaceFromTree() { // this test verifies that in a tree of surfaces every surface has the same main surface using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto parentServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(parentServerSurface); QScopedPointer childLevel1Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel1ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel1ServerSurface); QScopedPointer childLevel2Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel2ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel2ServerSurface); QScopedPointer childLevel3Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel3ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel3ServerSurface); QSignalSpy subSurfaceTreeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subSurfaceTreeChangedSpy.isValid()); m_subCompositor->createSubSurface(childLevel1Surface.data(), parentSurface.data()); m_subCompositor->createSubSurface(childLevel2Surface.data(), childLevel1Surface.data()); m_subCompositor->createSubSurface(childLevel3Surface.data(), childLevel2Surface.data()); parentSurface->commit(Surface::CommitFlag::None); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(parentServerSurface->childSubSurfaces().count(), 1); auto child = parentServerSurface->childSubSurfaces().first(); QCOMPARE(child->parentSurface().data(), parentServerSurface); QCOMPARE(child->mainSurface().data(), parentServerSurface); QCOMPARE(child->surface()->childSubSurfaces().count(), 1); auto child2 = child->surface()->childSubSurfaces().first(); QCOMPARE(child2->parentSurface().data(), child->surface().data()); QCOMPARE(child2->mainSurface().data(), parentServerSurface); QCOMPARE(child2->surface()->childSubSurfaces().count(), 1); auto child3 = child2->surface()->childSubSurfaces().first(); QCOMPARE(child3->parentSurface().data(), child2->surface().data()); QCOMPARE(child3->mainSurface().data(), parentServerSurface); QCOMPARE(child3->surface()->childSubSurfaces().count(), 0); } void TestSubSurface::testRemoveSurface() { // this test verifies that removing the surface also removes the sub-surface from the parent using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto parentServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(parentServerSurface); QScopedPointer childSurface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childServerSurface); QSignalSpy subSurfaceTreeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subSurfaceTreeChangedSpy.isValid()); m_subCompositor->createSubSurface(childSurface.data(), parentSurface.data()); parentSurface->commit(Surface::CommitFlag::None); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(parentServerSurface->childSubSurfaces().count(), 1); // destroy surface, takes place immediately childSurface.reset(); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(parentServerSurface->childSubSurfaces().count(), 0); } void TestSubSurface::testMappingOfSurfaceTree() { // this test verifies mapping and unmapping of a sub-surface tree using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer parentSurface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto parentServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(parentServerSurface); QScopedPointer childLevel1Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel1ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel1ServerSurface); QScopedPointer childLevel2Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel2ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel2ServerSurface); QScopedPointer childLevel3Surface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto childLevel3ServerSurface = surfaceCreatedSpy.last().first().value(); QVERIFY(childLevel3ServerSurface); QSignalSpy subSurfaceTreeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(subSurfaceTreeChangedSpy.isValid()); auto subSurfaceLevel1 = m_subCompositor->createSubSurface(childLevel1Surface.data(), parentSurface.data()); auto subSurfaceLevel2 = m_subCompositor->createSubSurface(childLevel2Surface.data(), childLevel1Surface.data()); auto subSurfaceLevel3 = m_subCompositor->createSubSurface(childLevel3Surface.data(), childLevel2Surface.data()); parentSurface->commit(Surface::CommitFlag::None); QVERIFY(subSurfaceTreeChangedSpy.wait()); QCOMPARE(parentServerSurface->childSubSurfaces().count(), 1); auto child = parentServerSurface->childSubSurfaces().first(); QCOMPARE(child->surface()->childSubSurfaces().count(), 1); auto child2 = child->surface()->childSubSurfaces().first(); QCOMPARE(child2->surface()->childSubSurfaces().count(), 1); auto child3 = child2->surface()->childSubSurfaces().first(); QCOMPARE(child3->parentSurface().data(), child2->surface().data()); QCOMPARE(child3->mainSurface().data(), parentServerSurface); QCOMPARE(child3->surface()->childSubSurfaces().count(), 0); // so far no surface is mapped QVERIFY(!parentServerSurface->isMapped()); QVERIFY(!child->surface()->isMapped()); QVERIFY(!child2->surface()->isMapped()); QVERIFY(!child3->surface()->isMapped()); // first set all subsurfaces to desync, to simplify subSurfaceLevel1->setMode(SubSurface::Mode::Desynchronized); subSurfaceLevel2->setMode(SubSurface::Mode::Desynchronized); subSurfaceLevel3->setMode(SubSurface::Mode::Desynchronized); // first map the child, should not map it QSignalSpy child3DamageSpy(child3->surface().data(), &SurfaceInterface::damaged); QVERIFY(child3DamageSpy.isValid()); QImage image(QSize(200, 200), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::black); childLevel3Surface->attachBuffer(m_shm->createBuffer(image)); childLevel3Surface->damage(QRect(0, 0, 200, 200)); childLevel3Surface->commit(Surface::CommitFlag::None); QVERIFY(child3DamageSpy.wait()); QVERIFY(child3->surface()->buffer()); QVERIFY(!child3->surface()->isMapped()); // let's map the top level QSignalSpy parentSpy(parentServerSurface, &SurfaceInterface::damaged); QVERIFY(parentSpy.isValid()); parentSurface->attachBuffer(m_shm->createBuffer(image)); parentSurface->damage(QRect(0, 0, 200, 200)); parentSurface->commit(Surface::CommitFlag::None); QVERIFY(parentSpy.wait()); QVERIFY(parentServerSurface->isMapped()); // children should not yet be mapped QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child->surface()->isMapped()); QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child2->surface()->isMapped()); QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child3->surface()->isMapped()); // next level QSignalSpy child2DamageSpy(child2->surface().data(), &SurfaceInterface::damaged); QVERIFY(child2DamageSpy.isValid()); childLevel2Surface->attachBuffer(m_shm->createBuffer(image)); childLevel2Surface->damage(QRect(0, 0, 200, 200)); childLevel2Surface->commit(Surface::CommitFlag::None); QVERIFY(child2DamageSpy.wait()); QVERIFY(parentServerSurface->isMapped()); // children should not yet be mapped QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child->surface()->isMapped()); QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child2->surface()->isMapped()); QEXPECT_FAIL("", "Workaround for QtWayland bug https://bugreports.qt.io/browse/QTBUG-52192", Continue); QVERIFY(!child3->surface()->isMapped()); // last but not least the first child level, which should map all our subsurfaces QSignalSpy child1DamageSpy(child->surface().data(), &SurfaceInterface::damaged); QVERIFY(child1DamageSpy.isValid()); childLevel1Surface->attachBuffer(m_shm->createBuffer(image)); childLevel1Surface->damage(QRect(0, 0, 200, 200)); childLevel1Surface->commit(Surface::CommitFlag::None); QVERIFY(child1DamageSpy.wait()); // everything is mapped QVERIFY(parentServerSurface->isMapped()); QVERIFY(child->surface()->isMapped()); QVERIFY(child2->surface()->isMapped()); QVERIFY(child3->surface()->isMapped()); // unmapping a parent should unmap the complete tree QSignalSpy unmappedSpy(child->surface().data(), &SurfaceInterface::unmapped); QVERIFY(unmappedSpy.isValid()); childLevel1Surface->attachBuffer(Buffer::Ptr()); childLevel1Surface->damage(QRect(0, 0, 200, 200)); childLevel1Surface->commit(Surface::CommitFlag::None); QVERIFY(unmappedSpy.wait()); QVERIFY(parentServerSurface->isMapped()); QVERIFY(!child->surface()->isMapped()); QVERIFY(!child2->surface()->isMapped()); QVERIFY(!child3->surface()->isMapped()); } void TestSubSurface::testSurfaceAt() { // this test verifies that the correct surface is picked in a sub-surface tree using namespace KWayland::Client; using namespace KWayland::Server; // first create a parent surface and map it QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer parent(m_compositor->createSurface()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); parent->attachBuffer(m_shm->createBuffer(image)); parent->damage(QRect(0, 0, 100, 100)); parent->commit(Surface::CommitFlag::None); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *parentServerSurface = serverSurfaceCreated.last().first().value(); // create two child sub surfaces, those won't be mapped, just added to the parent // this is to simulate the behavior of QtWayland QScopedPointer directChild1(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *directChild1ServerSurface = serverSurfaceCreated.last().first().value(); QVERIFY(directChild1ServerSurface); QScopedPointer directChild2(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *directChild2ServerSurface = serverSurfaceCreated.last().first().value(); QVERIFY(directChild2ServerSurface); // create the sub surfaces for them QScopedPointer directChild1SubSurface(m_subCompositor->createSubSurface(directChild1.data(), parent.data())); directChild1SubSurface->setMode(SubSurface::Mode::Desynchronized); QScopedPointer directChild2SubSurface(m_subCompositor->createSubSurface(directChild2.data(), parent.data())); directChild2SubSurface->setMode(SubSurface::Mode::Desynchronized); // each of the children gets a child QScopedPointer childFor1(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *childFor1ServerSurface = serverSurfaceCreated.last().first().value(); QScopedPointer childFor2(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *childFor2ServerSurface = serverSurfaceCreated.last().first().value(); // create sub surfaces for them QScopedPointer childFor1SubSurface(m_subCompositor->createSubSurface(childFor1.data(), directChild1.data())); childFor1SubSurface->setMode(SubSurface::Mode::Desynchronized); QScopedPointer childFor2SubSurface(m_subCompositor->createSubSurface(childFor2.data(), directChild2.data())); childFor2SubSurface->setMode(SubSurface::Mode::Desynchronized); // both get a quarter of the grand-parent surface childFor2SubSurface->setPosition(QPoint(50, 50)); childFor2->commit(Surface::CommitFlag::None); directChild2->commit(Surface::CommitFlag::None); parent->commit(Surface::CommitFlag::None); // now let's render both grand children QImage partImage(QSize(50, 50), QImage::Format_ARGB32_Premultiplied); partImage.fill(Qt::green); childFor1->attachBuffer(m_shm->createBuffer(partImage)); childFor1->damage(QRect(0, 0, 50, 50)); childFor1->commit(Surface::CommitFlag::None); partImage.fill(Qt::blue); childFor2->attachBuffer(m_shm->createBuffer(partImage)); // child for 2's input region is subdivided into quadrants, with input mask on the top left and bottom right QRegion region; region += QRect(0,0,25,25); region += QRect(25,25,25,25); childFor2->setInputRegion(m_compositor->createRegion(region).get()); childFor2->damage(QRect(0, 0, 50, 50)); childFor2->commit(Surface::CommitFlag::None); QSignalSpy treeChangedSpy(parentServerSurface, &SurfaceInterface::subSurfaceTreeChanged); QVERIFY(treeChangedSpy.isValid()); QVERIFY(treeChangedSpy.wait()); QCOMPARE(directChild1ServerSurface->subSurface()->parentSurface().data(), parentServerSurface); QCOMPARE(directChild2ServerSurface->subSurface()->parentSurface().data(), parentServerSurface); QCOMPARE(childFor1ServerSurface->subSurface()->parentSurface().data(), directChild1ServerSurface); QCOMPARE(childFor2ServerSurface->subSurface()->parentSurface().data(), directChild2ServerSurface); // now let's test a few positions QCOMPARE(parentServerSurface->surfaceAt(QPointF(0, 0)), childFor1ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(49, 49)), childFor1ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(50, 50)), childFor2ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(100, 100)), childFor2ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(100, 50)), childFor2ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(50, 100)), childFor2ServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(25, 75)), parentServerSurface); QCOMPARE(parentServerSurface->surfaceAt(QPointF(75, 25)), parentServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(0, 0)), childFor1ServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(49, 49)), childFor1ServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(50, 50)), childFor2ServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(99, 99)), childFor2ServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(99, 50)), parentServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(50, 99)), parentServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(25, 75)), parentServerSurface); QCOMPARE(parentServerSurface->inputSurfaceAt(QPointF(75, 25)), parentServerSurface); // outside the geometries should be no surface QVERIFY(!parentServerSurface->surfaceAt(QPointF(-1, -1))); QVERIFY(!parentServerSurface->surfaceAt(QPointF(101, 101))); } void TestSubSurface::testDestroyAttachedBuffer() { // this test verifies that destroying of a buffer attached to a sub-surface works using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer parent(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); QScopedPointer child(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverChildSurface = serverSurfaceCreated.last().first().value(); // create sub-surface m_subCompositor->createSubSurface(child.data(), parent.data()); // let's damage this surface, will be in sub-surface pending state QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); child->attachBuffer(m_shm->createBuffer(image)); child->damage(QRect(0, 0, 100, 100)); child->commit(Surface::CommitFlag::None); m_connection->flush(); // Let's try to destroy it QSignalSpy destroySpy(serverChildSurface, &QObject::destroyed); QVERIFY(destroySpy.isValid()); delete m_shm; m_shm = nullptr; child.reset(); QVERIFY(destroySpy.wait()); } void TestSubSurface::testDestroyParentSurface() { // this test verifies that destroying a parent surface does not create problems // see BUG 389231 using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer parent(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverParentSurface = serverSurfaceCreated.last().first().value(); QScopedPointer child(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverChildSurface = serverSurfaceCreated.last().first().value(); QScopedPointer grandChild(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverGrandChildSurface = serverSurfaceCreated.last().first().value(); // create sub-surface in desynchronized mode as Qt uses them auto sub1 = m_subCompositor->createSubSurface(child.data(), parent.data()); sub1->setMode(SubSurface::Mode::Desynchronized); auto sub2 = m_subCompositor->createSubSurface(grandChild.data(), child.data()); sub2->setMode(SubSurface::Mode::Desynchronized); // let's damage this surface // and at the same time delete the parent surface parent.reset(); QSignalSpy parentDestroyedSpy(serverParentSurface, &QObject::destroyed); QVERIFY(parentDestroyedSpy.isValid()); QVERIFY(parentDestroyedSpy.wait()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); grandChild->attachBuffer(m_shm->createBuffer(image)); grandChild->damage(QRect(0, 0, 100, 100)); grandChild->commit(Surface::CommitFlag::None); QSignalSpy damagedSpy(serverGrandChildSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); QVERIFY(damagedSpy.wait()); // Let's try to destroy it QSignalSpy destroySpy(serverChildSurface, &QObject::destroyed); QVERIFY(destroySpy.isValid()); child.reset(); QVERIFY(destroySpy.wait()); } QTEST_GUILESS_MAIN(TestSubSurface) #include "test_wayland_subsurface.moc" diff --git a/autotests/client/test_wayland_surface.cpp b/autotests/client/test_wayland_surface.cpp index 6f410cf..4495195 100644 --- a/autotests/client/test_wayland_surface.cpp +++ b/autotests/client/test_wayland_surface.cpp @@ -1,1217 +1,1203 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include #include #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/idleinhibit.h" #include "../../src/client/output.h" #include "../../src/client/surface.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/idleinhibit_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include using KWayland::Client::Registry; class TestWaylandSurface : public QObject { Q_OBJECT public: explicit TestWaylandSurface(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testStaticAccessor(); void testDamage(); void testFrameCallback(); void testAttachBuffer(); void testMultipleSurfaces(); void testOpaque(); void testInput(); void testScale(); void testDestroy(); void testUnmapOfNotMappedSurface(); void testDamageTracking(); void testSurfaceAt(); void testDestroyAttachedBuffer(); void testDestroyWithPendingCallback(); void testOutput(); void testDisconnect(); void testInhibit(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::IdleInhibitManagerInterface *m_idleInhibitInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shm; KWayland::Client::EventQueue *m_queue; KWayland::Client::IdleInhibitManager *m_idleInhibitManager; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-surface-0"); TestWaylandSurface::TestWaylandSurface(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_thread(nullptr) { } void TestWaylandSurface::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_idleInhibitInterface = m_display->createIdleInhibitManager(IdleInhibitManagerInterfaceVersion::UnstableV1, m_display); QVERIFY(m_idleInhibitInterface); m_idleInhibitInterface->create(); QVERIFY(m_idleInhibitInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); /*connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, m_connection, [this]() { if (m_connection->display()) { wl_display_flush(m_connection->display()); } } );*/ m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); KWayland::Client::Registry registry; registry.setEventQueue(m_queue); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); QSignalSpy allAnnounced(®istry, SIGNAL(interfacesAnnounced())); QVERIFY(allAnnounced.isValid()); QVERIFY(shmSpy.isValid()); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(allAnnounced.wait()); QVERIFY(!compositorSpy.isEmpty()); QVERIFY(!shmSpy.isEmpty()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); QVERIFY(m_compositor->isValid()); m_shm = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); QVERIFY(m_shm->isValid()); m_idleInhibitManager = registry.createIdleInhibitManager(registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).name, registry.interface(Registry::Interface::IdleInhibitManagerUnstableV1).version, this); QVERIFY(m_idleInhibitManager->isValid()); } void TestWaylandSurface::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_idleInhibitManager) { delete m_idleInhibitManager; m_idleInhibitManager = nullptr; } if (m_shm) { delete m_shm; m_shm = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_compositorInterface; m_compositorInterface = nullptr; delete m_idleInhibitInterface; m_idleInhibitInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandSurface::testStaticAccessor() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QVERIFY(!KWayland::Server::SurfaceInterface::get(nullptr)); QVERIFY(!KWayland::Server::SurfaceInterface::get(1, nullptr)); QVERIFY(KWayland::Client::Surface::all().isEmpty()); KWayland::Client::Surface *s1 = m_compositor->createSurface(); QVERIFY(s1->isValid()); QCOMPARE(KWayland::Client::Surface::all().count(), 1); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface1 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->resource()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1); QVERIFY(!s1->size().isValid()); QSignalSpy sizeChangedSpy(s1, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeChangedSpy.isValid()); const QSize testSize(200, 300); s1->setSize(testSize); QCOMPARE(s1->size(), testSize); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(sizeChangedSpy.first().first().toSize(), testSize); // add another surface KWayland::Client::Surface *s2 = m_compositor->createSurface(); QVERIFY(s2->isValid()); QCOMPARE(KWayland::Client::Surface::all().count(), 2); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::all().last(), s2); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); QCOMPARE(KWayland::Client::Surface::get(*s2), s2); serverSurfaceCreated.clear(); QVERIFY(serverSurfaceCreated.wait()); auto serverSurface2 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface2); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->resource()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface1->id(), serverSurface1->client()), serverSurface1); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface2->resource()), serverSurface2); QCOMPARE(KWayland::Server::SurfaceInterface::get(serverSurface2->id(), serverSurface2->client()), serverSurface2); // delete s2 again delete s2; QCOMPARE(KWayland::Client::Surface::all().count(), 1); QCOMPARE(KWayland::Client::Surface::all().first(), s1); QCOMPARE(KWayland::Client::Surface::get(*s1), s1); // and finally delete the last one delete s1; QVERIFY(KWayland::Client::Surface::all().isEmpty()); QVERIFY(!KWayland::Client::Surface::get(nullptr)); QSignalSpy unboundSpy(serverSurface1, &KWayland::Server::Resource::unbound); QVERIFY(unboundSpy.isValid()); QVERIFY(unboundSpy.wait()); QVERIFY(!KWayland::Server::SurfaceInterface::get(nullptr)); QVERIFY(!KWayland::Server::SurfaceInterface::get(1, nullptr)); } void TestWaylandSurface::testDamage() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); s->setScale(2); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->damage(), QRegion()); QVERIFY(serverSurface->parentResource()); QVERIFY(!serverSurface->isMapped()); QSignalSpy committedSpy(serverSurface, SIGNAL(committed())); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); // send damage without a buffer s->damage(QRect(0, 0, 100, 100)); s->commit(KWayland::Client::Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QVERIFY(damageSpy.isEmpty()); QVERIFY(!serverSurface->isMapped()); QCOMPARE(committedSpy.count(), 1); QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b, QPoint(55, 55)); s->damage(QRect(0, 0, 10, 10)); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QCOMPARE(serverSurface->offset(), QPoint(55, 55)); // offset is surface local so scale doesn't change this QCOMPARE(serverSurface->damage(), QRegion(0, 0, 5, 5)); // scale is 2 QCOMPARE(damageSpy.first().first().value(), QRegion(0, 0, 5, 5)); QVERIFY(serverSurface->isMapped()); QCOMPARE(committedSpy.count(), 2); // damage multiple times QRegion testRegion(5, 8, 3, 6); testRegion = testRegion.united(QRect(10, 11, 6, 1)); img = QImage(QSize(40, 35), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(testRegion); damageSpy.clear(); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QCOMPARE(serverSurface->damage(), testRegion); QCOMPARE(damageSpy.first().first().value(), testRegion); QVERIFY(serverSurface->isMapped()); QCOMPARE(committedSpy.count(), 3); // damage buffer const QRegion testRegion2(30, 40, 22, 4); const QRegion cmpRegion2(15, 20, 11, 2); // divided by scale factor img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); s->attachBuffer(b); s->damageBuffer(testRegion2); damageSpy.clear(); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QCOMPARE(serverSurface->damage(), cmpRegion2); QCOMPARE(damageSpy.first().first().value(), cmpRegion2); QVERIFY(serverSurface->isMapped()); // combined regular damage and damaged buffer const QRegion testRegion3 = testRegion.united(cmpRegion2); img = QImage(QSize(80, 70), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(testRegion); s->damageBuffer(testRegion2); damageSpy.clear(); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(damageSpy.wait()); QVERIFY(serverSurface->damage() != testRegion); QVERIFY(serverSurface->damage() != testRegion2); QVERIFY(serverSurface->damage() != cmpRegion2); QCOMPARE(serverSurface->damage(), testRegion3); QCOMPARE(damageSpy.first().first().value(), testRegion3); QVERIFY(serverSurface->isMapped()); } void TestWaylandSurface::testFrameCallback() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); QSignalSpy frameRenderedSpy(s, SIGNAL(frameRendered())); QVERIFY(frameRenderedSpy.isValid()); QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(QRect(0, 0, 10, 10)); s->commit(); QVERIFY(damageSpy.wait()); serverSurface->frameRendered(10); QVERIFY(frameRenderedSpy.isEmpty()); QVERIFY(frameRenderedSpy.wait()); QVERIFY(!frameRenderedSpy.isEmpty()); } void TestWaylandSurface::testAttachBuffer() { // create the surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); // create three images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); QImage red(24, 24, QImage::Format_ARGB32); //Note - deliberately not premultiplied red.fill(QColor(255, 0, 0, 128)); QImage blue(24, 24, QImage::Format_ARGB32_Premultiplied); blue.fill(QColor(0, 0, 255, 128)); QSharedPointer blackBufferPtr = m_shm->createBuffer(black).toStrongRef(); QVERIFY(blackBufferPtr); wl_buffer *blackBuffer = *(blackBufferPtr.data()); QSharedPointer redBuffer = m_shm->createBuffer(red).toStrongRef(); QVERIFY(redBuffer); QSharedPointer blueBuffer = m_shm->createBuffer(blue).toStrongRef(); QVERIFY(blueBuffer); QCOMPARE(blueBuffer->format(), KWayland::Client::Buffer::Format::ARGB32); QCOMPARE(blueBuffer->size(), blue.size()); QVERIFY(!blueBuffer->isReleased()); QVERIFY(!blueBuffer->isUsed()); QCOMPARE(blueBuffer->stride(), blue.bytesPerLine()); s->attachBuffer(redBuffer.data()); s->attachBuffer(blackBuffer); s->damage(QRect(0, 0, 24, 24)); s->commit(KWayland::Client::Surface::CommitFlag::None); QSignalSpy damageSpy(serverSurface, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy.isValid()); QSignalSpy unmappedSpy(serverSurface, SIGNAL(unmapped())); QVERIFY(unmappedSpy.isValid()); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); // now the ServerSurface should have the black image attached as a buffer KWayland::Server::BufferInterface *buffer = serverSurface->buffer(); buffer->ref(); QVERIFY(buffer->shmBuffer()); QCOMPARE(buffer->data(), black); QCOMPARE(buffer->data().format(), QImage::Format_RGB32); // render another frame s->attachBuffer(redBuffer); s->damage(QRect(0, 0, 24, 24)); s->commit(KWayland::Client::Surface::CommitFlag::None); damageSpy.clear(); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); KWayland::Server::BufferInterface *buffer2 = serverSurface->buffer(); buffer2->ref(); QVERIFY(buffer2->shmBuffer()); QCOMPARE(buffer2->data().format(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(buffer2->data().width(), 24); QCOMPARE(buffer2->data().height(), 24); for (int i = 0; i < 24; ++i) { for (int j = 0; j < 24; ++j) { // it's premultiplied in the format QCOMPARE(buffer2->data().pixel(i, j), qRgba(128, 0, 0, 128)); } } buffer2->unref(); QVERIFY(buffer2->isReferenced()); QVERIFY(!redBuffer.data()->isReleased()); // render another frame blueBuffer->setUsed(true); QVERIFY(blueBuffer->isUsed()); s->attachBuffer(blueBuffer.data()); s->damage(QRect(0, 0, 24, 24)); QSignalSpy frameRenderedSpy(s, SIGNAL(frameRendered())); QVERIFY(frameRenderedSpy.isValid()); s->commit(); damageSpy.clear(); QVERIFY(damageSpy.wait()); QVERIFY(unmappedSpy.isEmpty()); QVERIFY(!buffer2->isReferenced()); delete buffer2; // TODO: we should have a signal on when the Buffer gets released QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); if (!redBuffer.data()->isReleased()) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } QVERIFY(redBuffer.data()->isReleased()); KWayland::Server::BufferInterface *buffer3 = serverSurface->buffer(); buffer3->ref(); QVERIFY(buffer3->shmBuffer()); QCOMPARE(buffer3->data().format(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(buffer3->data().width(), 24); QCOMPARE(buffer3->data().height(), 24); for (int i = 0; i < 24; ++i) { for (int j = 0; j < 24; ++j) { // it's premultiplied in the format QCOMPARE(buffer3->data().pixel(i, j), qRgba(0, 0, 128, 128)); } } buffer3->unref(); QVERIFY(buffer3->isReferenced()); serverSurface->frameRendered(1); QVERIFY(frameRenderedSpy.wait()); // commit a different value shouldn't change our buffer QCOMPARE(serverSurface->buffer(), buffer3); QVERIFY(serverSurface->input().isNull()); damageSpy.clear(); s->setInputRegion(m_compositor->createRegion(QRegion(0, 0, 24, 24)).get()); s->commit(KWayland::Client::Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCoreApplication::processEvents(); QCOMPARE(serverSurface->input(), QRegion(0, 0, 24, 24)); QCOMPARE(serverSurface->buffer(), buffer3); QVERIFY(damageSpy.isEmpty()); QVERIFY(unmappedSpy.isEmpty()); QVERIFY(serverSurface->isMapped()); // clear the surface s->attachBuffer(blackBuffer); s->damage(QRect(0, 0, 1, 1)); // TODO: better method s->attachBuffer((wl_buffer*)nullptr); s->damage(QRect(0, 0, 10, 10)); s->commit(KWayland::Client::Surface::CommitFlag::None); QVERIFY(unmappedSpy.wait()); QVERIFY(!unmappedSpy.isEmpty()); QCOMPARE(unmappedSpy.count(), 1); QVERIFY(damageSpy.isEmpty()); QVERIFY(!serverSurface->isMapped()); // TODO: add signal test on release buffer->unref(); } void TestWaylandSurface::testMultipleSurfaces() { using namespace KWayland::Client; using namespace KWayland::Server; Registry registry; QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(shmSpy.wait()); ShmPool pool1; ShmPool pool2; pool1.setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); pool2.setup(registry.bindShm(shmSpy.first().first().value(), shmSpy.first().last().value())); QVERIFY(pool1.isValid()); QVERIFY(pool2.isValid()); // create the surfaces QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s1(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface1 = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface1); //second surface QScopedPointer s2(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface2 = serverSurfaceCreated.last().first().value(); QVERIFY(serverSurface2); QVERIFY(serverSurface1->resource() != serverSurface2->resource()); // create two images QImage black(24, 24, QImage::Format_RGB32); black.fill(Qt::black); QImage red(24, 24, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); auto blackBuffer = pool1.createBuffer(black); auto redBuffer = pool2.createBuffer(red); s1->attachBuffer(blackBuffer); s1->damage(QRect(0, 0, 24, 24)); s1->commit(Surface::CommitFlag::None); QSignalSpy damageSpy1(serverSurface1, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy1.isValid()); QVERIFY(damageSpy1.wait()); // now the ServerSurface should have the black image attached as a buffer BufferInterface *buffer1 = serverSurface1->buffer(); QVERIFY(buffer1); QImage buffer1Data = buffer1->data(); QCOMPARE(buffer1Data, black); // accessing the same buffer is OK QImage buffer1Data2 = buffer1->data(); QCOMPARE(buffer1Data2, buffer1Data); buffer1Data = QImage(); QVERIFY(buffer1Data.isNull()); buffer1Data2 = QImage(); QVERIFY(buffer1Data2.isNull()); // attach a buffer for the other surface s2->attachBuffer(redBuffer); s2->damage(QRect(0, 0, 24, 24)); s2->commit(Surface::CommitFlag::None); QSignalSpy damageSpy2(serverSurface2, SIGNAL(damaged(QRegion))); QVERIFY(damageSpy2.isValid()); QVERIFY(damageSpy2.wait()); BufferInterface *buffer2 = serverSurface2->buffer(); QVERIFY(buffer2); QImage buffer2Data = buffer2->data(); QCOMPARE(buffer2Data, red); // while buffer2 is accessed we cannot access buffer1 buffer1Data = buffer1->data(); QVERIFY(buffer1Data.isNull()); // a deep copy can be kept around QImage deepCopy = buffer2Data.copy(); QCOMPARE(deepCopy, red); buffer2Data = QImage(); QVERIFY(buffer2Data.isNull()); QCOMPARE(deepCopy, red); // now that buffer2Data is destroyed we can access buffer1 again buffer1Data = buffer1->data(); QVERIFY(!buffer1Data.isNull()); QCOMPARE(buffer1Data, black); } void TestWaylandSurface::testOpaque() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy opaqueRegionChangedSpy(serverSurface, SIGNAL(opaqueChanged(QRegion))); QVERIFY(opaqueRegionChangedSpy.isValid()); // by default there should be an empty opaque region QCOMPARE(serverSurface->opaque(), QRegion()); // let's install an opaque region s->setOpaqueRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get()); // the region should only be applied after the surface got committed wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSurface->opaque(), QRegion()); QCOMPARE(opaqueRegionChangedSpy.count(), 0); // so let's commit to get the new region s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 1); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30)); // committing without setting a new region shouldn't change s->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(opaqueRegionChangedSpy.count(), 1); QCOMPARE(serverSurface->opaque(), QRegion(0, 10, 20, 30)); // let's change the opaque region s->setOpaqueRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get()); s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 2); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->opaque(), QRegion(10, 20, 30, 40)); // and let's go back to an empty region s->setOpaqueRegion(); s->commit(Surface::CommitFlag::None); QVERIFY(opaqueRegionChangedSpy.wait()); QCOMPARE(opaqueRegionChangedSpy.count(), 3); QCOMPARE(opaqueRegionChangedSpy.last().first().value(), QRegion()); QCOMPARE(serverSurface->opaque(), QRegion()); } void TestWaylandSurface::testInput() { using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); Surface *s = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QSignalSpy inputRegionChangedSpy(serverSurface, SIGNAL(inputChanged(QRegion))); QVERIFY(inputRegionChangedSpy.isValid()); // by default there should be an empty == infinite input region QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); // let's install an input region s->setInputRegion(m_compositor->createRegion(QRegion(0, 10, 20, 30)).get()); // the region should only be applied after the surface got committed wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); QCOMPARE(inputRegionChangedSpy.count(), 0); // so let's commit to get the new region s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 1); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->inputIsInfinite(), false); // committing without setting a new region shouldn't change s->commit(Surface::CommitFlag::None); wl_display_flush(m_connection->display()); QCoreApplication::processEvents(); QCOMPARE(inputRegionChangedSpy.count(), 1); QCOMPARE(serverSurface->input(), QRegion(0, 10, 20, 30)); QCOMPARE(serverSurface->inputIsInfinite(), false); // let's change the input region s->setInputRegion(m_compositor->createRegion(QRegion(10, 20, 30, 40)).get()); s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 2); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->input(), QRegion(10, 20, 30, 40)); QCOMPARE(serverSurface->inputIsInfinite(), false); // and let's go back to an empty region s->setInputRegion(); s->commit(Surface::CommitFlag::None); QVERIFY(inputRegionChangedSpy.wait()); QCOMPARE(inputRegionChangedSpy.count(), 3); QCOMPARE(inputRegionChangedSpy.last().first().value(), QRegion()); QCOMPARE(serverSurface->input(), QRegion()); QCOMPARE(serverSurface->inputIsInfinite(), true); } void TestWaylandSurface::testScale() { // this test verifies that updating the scale factor is correctly passed to the Wayland server using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QCOMPARE(s->scale(), 1); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->scale(), 1); // let's change the scale factor QSignalSpy scaleChangedSpy(serverSurface, &SurfaceInterface::scaleChanged); //changing the scale implicitly changes the size QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged); QVERIFY(scaleChangedSpy.isValid()); s->setScale(2); QCOMPARE(s->scale(), 2); // needs a commit QVERIFY(!scaleChangedSpy.wait(100)); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChangedSpy.wait()); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.first().first().toInt(), 2); QCOMPARE(serverSurface->scale(), 2); //even though we've changed the scale, if we don't have a buffer we //don't have a size. If we don't have a size it can't have changed QCOMPARE(sizeChangedSpy.count(), 0); QVERIFY(!serverSurface->size().isValid()); // let's try changing to same factor, should not emit changed on server s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(!scaleChangedSpy.wait(100)); // but changing to a different value should still work s->setScale(4); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChangedSpy.wait()); QCOMPARE(scaleChangedSpy.count(), 2); QCOMPARE(scaleChangedSpy.first().first().toInt(), 2); QCOMPARE(scaleChangedSpy.last().first().toInt(), 4); QCOMPARE(serverSurface->scale(), 4); scaleChangedSpy.clear(); //attach a buffer of 100x100, our scale is 4, so this should be a size of 25x25 QImage red(100, 100, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); QSharedPointer redBuffer = m_shm->createBuffer(red).toStrongRef(); QVERIFY(redBuffer); s->attachBuffer(redBuffer.data()); s->damage(QRect(0,0, 25,25)); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(serverSurface->size(), QSize(25,25)); sizeChangedSpy.clear(); scaleChangedSpy.clear(); //set the scale to 1, buffer is still 100x100 so size should change to 100x100 s->setScale(1); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(serverSurface->scale(), 1); QCOMPARE(serverSurface->size(), QSize(100,100)); sizeChangedSpy.clear(); scaleChangedSpy.clear(); //set scale and size in one commit, buffer is 50x50 at scale 2 so size should be 25x25 QImage blue(50, 50, QImage::Format_ARGB32_Premultiplied); red.fill(QColor(255, 0, 0, 128)); QSharedPointer blueBuffer = m_shm->createBuffer(blue).toStrongRef(); QVERIFY(blueBuffer); s->attachBuffer(blueBuffer.data()); s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); QCOMPARE(sizeChangedSpy.count(), 1); QCOMPARE(scaleChangedSpy.count(), 1); QCOMPARE(serverSurface->scale(), 2); QCOMPARE(serverSurface->size(), QSize(25,25)); } void TestWaylandSurface::testDestroy() { using namespace KWayland::Client; Surface *s = m_compositor->createSurface(); connect(m_connection, &ConnectionThread::connectionDied, s, &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_idleInhibitManager, &IdleInhibitManager::destroy); QVERIFY(s->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_compositorInterface; m_compositorInterface = nullptr; delete m_idleInhibitInterface; m_idleInhibitInterface = nullptr; delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the Surface should be destroyed; QVERIFY(!s->isValid()); // calling destroy again should not fail s->destroy(); } void TestWaylandSurface::testUnmapOfNotMappedSurface() { // this test verifies that a surface which doesn't have a buffer attached doesn't trigger the unmapped signal using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); QSignalSpy unmappedSpy(serverSurface, &SurfaceInterface::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy scaleChanged(serverSurface, &SurfaceInterface::scaleChanged); // let's map a null buffer and change scale to trigger a signal we can wait for s->attachBuffer(Buffer::Ptr()); s->setScale(2); s->commit(Surface::CommitFlag::None); QVERIFY(scaleChanged.wait()); QVERIFY(unmappedSpy.isEmpty()); } void TestWaylandSurface::testDamageTracking() { // this tests the damage tracking feature using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // before first commit, the tracked damage should be empty QVERIFY(serverSurface->trackedDamage().isEmpty()); // Now let's damage the surface QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 100, 100)); QCOMPARE(serverSurface->damage(), QRegion(0, 0, 100, 100)); // resetting the tracked damage should empty it serverSurface->resetTrackedDamage(); QVERIFY(serverSurface->trackedDamage().isEmpty()); // but not affect the actual damage QCOMPARE(serverSurface->damage(), QRegion(0, 0, 100, 100)); // let's damage some parts of the surface QPainter p; p.begin(&image); p.fillRect(QRect(0, 0, 10, 10), Qt::blue); p.end(); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 10, 10)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10)); QCOMPARE(serverSurface->damage(), QRegion(0, 0, 10, 10)); // and damage some part completely not bounding to the current damage region p.begin(&image); p.fillRect(QRect(50, 40, 20, 30), Qt::blue); p.end(); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(50, 40, 20, 30)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QCOMPARE(serverSurface->trackedDamage(), QRegion(0, 0, 10, 10).united(QRegion(50, 40, 20, 30))); QCOMPARE(serverSurface->trackedDamage().rectCount(), 2); QCOMPARE(serverSurface->damage(), QRegion(50, 40, 20, 30)); // now let's reset the tracked damage again serverSurface->resetTrackedDamage(); QVERIFY(serverSurface->trackedDamage().isEmpty()); // but not affect the actual damage QCOMPARE(serverSurface->damage(), QRegion(50, 40, 20, 30)); } void TestWaylandSurface::testSurfaceAt() { // this test verifies that surfaceAt(const QPointF&) works as expected for the case of no children using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // a newly created surface should not be mapped and not provide a surface at a position QVERIFY(!serverSurface->isMapped()); QVERIFY(!serverSurface->surfaceAt(QPointF(0, 0))); // let's damage this surface QSignalSpy sizeChangedSpy(serverSurface, &SurfaceInterface::sizeChanged); QVERIFY(sizeChangedSpy.isValid()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(sizeChangedSpy.wait()); // now the surface is mapped and surfaceAt should give the surface QVERIFY(serverSurface->isMapped()); QCOMPARE(serverSurface->surfaceAt(QPointF(0, 0)), serverSurface); QCOMPARE(serverSurface->surfaceAt(QPointF(100, 100)), serverSurface); // outside the geometry it should not give a surface QVERIFY(!serverSurface->surfaceAt(QPointF(101, 101))); QVERIFY(!serverSurface->surfaceAt(QPointF(-1, -1))); } void TestWaylandSurface::testDestroyAttachedBuffer() { // this test verifies that destroying of a buffer attached to a surface works using namespace KWayland::Client; using namespace KWayland::Server; // create surface QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(serverSurfaceCreated.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(serverSurfaceCreated.wait()); SurfaceInterface *serverSurface = serverSurfaceCreated.first().first().value(); // let's damage this surface QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); s->attachBuffer(m_shm->createBuffer(image)); s->damage(QRect(0, 0, 100, 100)); s->commit(Surface::CommitFlag::None); QVERIFY(damagedSpy.wait()); QVERIFY(serverSurface->buffer()); // attach another buffer image.fill(Qt::blue); s->attachBuffer(m_shm->createBuffer(image)); m_connection->flush(); // Let's try to destroy it QSignalSpy destroySpy(serverSurface->buffer(), &BufferInterface::aboutToBeDestroyed); QVERIFY(destroySpy.isValid()); delete m_shm; m_shm = nullptr; QVERIFY(destroySpy.wait()); // TODO: should this emit unmapped? QVERIFY(!serverSurface->buffer()); } void TestWaylandSurface::testDestroyWithPendingCallback() { // this test tries to verify that destroying a surface with a pending callback works correctly // first create surface using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // now render to it QImage img(QSize(10, 10), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); auto b = m_shm->createBuffer(img); s->attachBuffer(b); s->damage(QRect(0, 0, 10, 10)); // add some frame callbacks for (int i = 0; i < 1000; i++) { wl_surface_frame(*s); } s->commit(KWayland::Client::Surface::CommitFlag::FrameCallback); QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged); QVERIFY(damagedSpy.isValid()); QVERIFY(damagedSpy.wait()); // now try to destroy the Surface again QSignalSpy destroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); s.reset(); QVERIFY(destroyedSpy.wait()); } void TestWaylandSurface::testDisconnect() { // this test verifies that the server side correctly tears down the resources when the client disconnects using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // destroy client QSignalSpy clientDisconnectedSpy(serverSurface->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(surfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); if (surfaceDestroyedSpy.isEmpty()) { QVERIFY(surfaceDestroyedSpy.wait()); } QTRY_COMPARE(surfaceDestroyedSpy.count(), 1); s->destroy(); m_shm->destroy(); m_compositor->destroy(); m_queue->destroy(); m_idleInhibitManager->destroy(); } void TestWaylandSurface::testOutput() { // This test verifies that the enter/leave are sent correctly to the Client using namespace KWayland::Client; using namespace KWayland::Server; qRegisterMetaType(); QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QVERIFY(s->outputs().isEmpty()); QSignalSpy enteredSpy(s.data(), &Surface::outputEntered); QVERIFY(enteredSpy.isValid()); QSignalSpy leftSpy(s.data(), &Surface::outputLeft); QVERIFY(leftSpy.isValid()); // wait for the surface on the Server side QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->outputs(), QVector()); // create another registry to get notified about added outputs Registry registry; registry.setEventQueue(m_queue); QSignalSpy allAnnounced(®istry, &Registry::interfacesAnnounced); QVERIFY(allAnnounced.isValid()); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(allAnnounced.wait()); QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); QVERIFY(outputAnnouncedSpy.isValid()); auto serverOutput = m_display->createOutput(m_display); serverOutput->create(); QVERIFY(outputAnnouncedSpy.wait()); QScopedPointer clientOutput(registry.createOutput(outputAnnouncedSpy.first().first().value(), outputAnnouncedSpy.first().last().value())); QVERIFY(clientOutput->isValid()); m_connection->flush(); m_display->dispatchEvents(); // now enter it serverSurface->setOutputs(QVector{serverOutput}); QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(enteredSpy.first().first().value(), clientOutput.data()); QCOMPARE(s->outputs(), QVector{clientOutput.data()}); // adding to same should not trigger serverSurface->setOutputs(QVector{serverOutput}); // leave again serverSurface->setOutputs(QVector()); QCOMPARE(serverSurface->outputs(), QVector()); QVERIFY(leftSpy.wait()); QCOMPARE(enteredSpy.count(), 1); QCOMPARE(leftSpy.count(), 1); QCOMPARE(leftSpy.first().first().value(), clientOutput.data()); QCOMPARE(s->outputs(), QVector()); // leave again should not trigger serverSurface->setOutputs(QVector()); // and enter again, just to verify serverSurface->setOutputs(QVector{serverOutput}); QCOMPARE(serverSurface->outputs(), QVector{serverOutput}); QVERIFY(enteredSpy.wait()); QCOMPARE(enteredSpy.count(), 2); QCOMPARE(leftSpy.count(), 1); //delete output client is on. //client should get an exit and be left on no outputs (which is allowed) serverOutput->deleteLater(); QVERIFY(leftSpy.wait()); QCOMPARE(serverSurface->outputs(), QVector()); } void TestWaylandSurface::testInhibit() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); // wait for the surface on the Server side QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->inhibitsIdle(), false); QSignalSpy inhibitsChangedSpy(serverSurface, &SurfaceInterface::inhibitsIdleChanged); QVERIFY(inhibitsChangedSpy.isValid()); // now create an idle inhibition QScopedPointer inhibitor1(m_idleInhibitManager->createInhibitor(s.data())); QVERIFY(inhibitsChangedSpy.wait()); QCOMPARE(serverSurface->inhibitsIdle(), true); // creating a second idle inhibition should not trigger the signal QScopedPointer inhibitor2(m_idleInhibitManager->createInhibitor(s.data())); QVERIFY(!inhibitsChangedSpy.wait(500)); QCOMPARE(serverSurface->inhibitsIdle(), true); // and also deleting the first inhibitor should not yet change the inhibition inhibitor1.reset(); QVERIFY(!inhibitsChangedSpy.wait(500)); QCOMPARE(serverSurface->inhibitsIdle(), true); // but deleting also the second inhibitor should trigger inhibitor2.reset(); QVERIFY(inhibitsChangedSpy.wait()); QCOMPARE(serverSurface->inhibitsIdle(), false); QCOMPARE(inhibitsChangedSpy.count(), 2); // recreate inhibitor1 should inhibit again inhibitor1.reset(m_idleInhibitManager->createInhibitor(s.data())); QVERIFY(inhibitsChangedSpy.wait()); QCOMPARE(serverSurface->inhibitsIdle(), true); // and destroying should uninhibit inhibitor1.reset(); QVERIFY(inhibitsChangedSpy.wait()); QCOMPARE(serverSurface->inhibitsIdle(), false); QCOMPARE(inhibitsChangedSpy.count(), 4); } QTEST_GUILESS_MAIN(TestWaylandSurface) #include "test_wayland_surface.moc" diff --git a/autotests/client/test_wayland_windowmanagement.cpp b/autotests/client/test_wayland_windowmanagement.cpp index 41803f3..0069e8b 100644 --- a/autotests/client/test_wayland_windowmanagement.cpp +++ b/autotests/client/test_wayland_windowmanagement.cpp @@ -1,656 +1,642 @@ -/******************************************************************** -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmawindowmanagement.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/server/surface_interface.h" #include typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowSignal)(); Q_DECLARE_METATYPE(ServerWindowSignal) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBooleanSignal)(bool); Q_DECLARE_METATYPE(ServerWindowBooleanSignal) typedef void (KWayland::Client::PlasmaWindow::*ClientWindowVoidSetter)(); Q_DECLARE_METATYPE(ClientWindowVoidSetter) class TestWindowManagement : public QObject { Q_OBJECT public: explicit TestWindowManagement(QObject *parent = nullptr); private Q_SLOTS: void init(); void testWindowTitle(); void testMinimizedGeometry(); void testUseAfterUnmap(); void testServerDelete(); void testActiveWindowOnUnmapped(); void testDeleteActiveWindow(); void testCreateAfterUnmap(); void testRequests_data(); void testRequests(); void testRequestsBoolean_data(); void testRequestsBoolean(); void testKeepAbove(); void testKeepBelow(); void testShowingDesktop(); void testRequestShowingDesktop_data(); void testRequestShowingDesktop(); void testParentWindow(); void testGeometry(); void testIcon(); void testPid(); void cleanup(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; KWayland::Server::PlasmaWindowInterface *m_windowInterface; KWayland::Server::SurfaceInterface *m_surfaceInterface = nullptr; KWayland::Client::Surface *m_surface = nullptr; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; KWayland::Client::PlasmaWindowManagement *m_windowManagement; KWayland::Client::PlasmaWindow *m_window; QThread *m_thread; KWayland::Client::Registry *m_registry; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-windowmanagement-0"); TestWindowManagement::TestWindowManagement(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWindowManagement::init() { using namespace KWayland::Server; qRegisterMetaType("ShowingDesktopState"); delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new KWayland::Client::Registry(this); QSignalSpy compositorSpy(m_registry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QSignalSpy windowManagementSpy(m_registry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); QVERIFY(windowManagementSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection->display()); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); m_windowManagementInterface->create(); QVERIFY(m_windowManagementInterface->isValid()); QVERIFY(windowManagementSpy.wait()); m_windowManagement = m_registry->createPlasmaWindowManagement(windowManagementSpy.first().first().value(), windowManagementSpy.first().last().value(), this); QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow*))); QVERIFY(windowSpy.isValid()); m_windowInterface = m_windowManagementInterface->createWindow(this); m_windowInterface->setPid(1337); QVERIFY(windowSpy.wait()); m_window = windowSpy.first().first().value(); QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_surface = m_compositor->createSurface(this); QVERIFY(m_surface); QVERIFY(serverSurfaceCreated.wait()); m_surfaceInterface = serverSurfaceCreated.first().first().value(); QVERIFY(m_surfaceInterface); } void TestWindowManagement::testWindowTitle() { m_windowInterface->setTitle(QStringLiteral("Test Title")); QSignalSpy titleSpy(m_window, SIGNAL(titleChanged())); QVERIFY(titleSpy.isValid()); QVERIFY(titleSpy.wait()); QCOMPARE(m_window->title(), QString::fromUtf8("Test Title")); } void TestWindowManagement::testMinimizedGeometry() { m_window->setMinimizedGeometry(m_surface, QRect(5, 10, 100, 200)); QSignalSpy geometrySpy(m_windowInterface, SIGNAL(minimizedGeometriesChanged())); QVERIFY(geometrySpy.isValid()); QVERIFY(geometrySpy.wait()); QCOMPARE(m_windowInterface->minimizedGeometries().values().first(), QRect(5, 10, 100, 200)); m_window->unsetMinimizedGeometry(m_surface); QVERIFY(geometrySpy.wait()); QVERIFY(m_windowInterface->minimizedGeometries().isEmpty()); } void TestWindowManagement::cleanup() { if (m_surface) { delete m_surface; m_surface = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_windowManagement) { delete m_windowManagement; m_windowManagement = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { if (m_connection) { m_connection->flush(); m_connection->deleteLater(); } m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } m_connection = nullptr; m_display->dispatchEvents(); delete m_windowManagementInterface; m_windowManagementInterface = nullptr; delete m_windowInterface; m_windowInterface = nullptr; delete m_surfaceInterface; m_surfaceInterface = nullptr; delete m_display; m_display = nullptr; } void TestWindowManagement::testUseAfterUnmap() { // this test verifies that when the client uses a window after it's unmapped, things don't break QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); m_windowInterface->unmap(); m_window->requestClose(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testServerDelete() { QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete m_windowInterface; m_windowInterface = nullptr; QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; } void TestWindowManagement::testActiveWindowOnUnmapped() { // This test verifies that unmapping the active window changes the active window. // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // now unmap should change the active window QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); m_windowManagementInterface->unmapWindow(m_windowInterface); QVERIFY(activeWindowChangedSpy.wait()); QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); m_window = nullptr; QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testDeleteActiveWindow() { // This test verifies that deleting the active window on client side changes the active window // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(activeWindowChangedSpy.count(), 1); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // delete the client side window - that's semantically kind of wrong, but shouldn't make our code crash delete m_window; m_window = nullptr; QCOMPARE(activeWindowChangedSpy.count(), 2); QVERIFY(!m_windowManagement->activeWindow()); } void TestWindowManagement::testCreateAfterUnmap() { // this test verifies that we don't get a protocol error on client side when creating an already unmapped window. QCOMPARE(m_windowManagement->children().count(), 1); // create and unmap in one go // client will first handle the create, the unmap will be sent once the server side is bound auto serverWindow = m_windowManagementInterface->createWindow(this); serverWindow->unmap(); QCOMPARE(m_windowManagementInterface->children().count(), 0); QCoreApplication::instance()->processEvents(); QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QTRY_COMPARE(m_windowManagement->children().count(), 2); auto window = dynamic_cast(m_windowManagement->children().last()); QVERIFY(window); // now this is not yet on the server, on the server it will be after next roundtrip // which we can trigger by waiting for destroy of the newly created window. // why destroy? Because we will get the unmap which triggers a destroy QSignalSpy clientDestroyedSpy(window, &QObject::destroyed); QVERIFY(clientDestroyedSpy.isValid()); QVERIFY(clientDestroyedSpy.wait()); QCOMPARE(m_windowManagement->children().count(), 1); // the server side created a helper PlasmaWindowInterface with PlasmaWindowManagementInterface as parent // it emitted unmapped so we can be sure it will be destroyed directly QCOMPARE(m_windowManagementInterface->children().count(), 1); auto helperWindow = qobject_cast(m_windowManagementInterface->children().first()); QVERIFY(helperWindow); QSignalSpy helperDestroyedSpy(helperWindow, &QObject::destroyed); QVERIFY(helperDestroyedSpy.isValid()); QVERIFY(helperDestroyedSpy.wait()); } void TestWindowManagement::testRequests_data() { using namespace KWayland::Server; using namespace KWayland::Client; QTest::addColumn("changedSignal"); QTest::addColumn("requester"); QTest::newRow("close") << &PlasmaWindowInterface::closeRequested << &PlasmaWindow::requestClose; QTest::newRow("move") << &PlasmaWindowInterface::moveRequested << &PlasmaWindow::requestMove; QTest::newRow("resize") << &PlasmaWindowInterface::resizeRequested << &PlasmaWindow::requestResize; } void TestWindowManagement::testRequests() { // this test case verifies all the different requests on a PlasmaWindow QFETCH(ServerWindowSignal, changedSignal); QSignalSpy requestSpy(m_windowInterface, changedSignal); QVERIFY(requestSpy.isValid()); QFETCH(ClientWindowVoidSetter, requester); (m_window->*(requester))(); QVERIFY(requestSpy.wait()); } void TestWindowManagement::testRequestsBoolean_data() { using namespace KWayland::Server; QTest::addColumn("changedSignal"); QTest::addColumn("flag"); QTest::newRow("activate") << &PlasmaWindowInterface::activeRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); QTest::newRow("minimized") << &PlasmaWindowInterface::minimizedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); QTest::newRow("maximized") << &PlasmaWindowInterface::maximizedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); QTest::newRow("fullscreen") << &PlasmaWindowInterface::fullscreenRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); QTest::newRow("keepAbove") << &PlasmaWindowInterface::keepAboveRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); QTest::newRow("keepBelow") << &PlasmaWindowInterface::keepBelowRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); QTest::newRow("demandsAttention") << &PlasmaWindowInterface::demandsAttentionRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); QTest::newRow("closeable") << &PlasmaWindowInterface::closeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); QTest::newRow("minimizable") << &PlasmaWindowInterface::minimizeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); QTest::newRow("maximizable") << &PlasmaWindowInterface::maximizeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); QTest::newRow("fullscreenable") << &PlasmaWindowInterface::fullscreenableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); QTest::newRow("skiptaskbar") << &PlasmaWindowInterface::skipTaskbarRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); QTest::newRow("skipSwitcher") << &PlasmaWindowInterface::skipSwitcherRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); QTest::newRow("shadeable") << &PlasmaWindowInterface::shadeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); QTest::newRow("shaded") << &PlasmaWindowInterface::shadedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); QTest::newRow("movable") << &PlasmaWindowInterface::movableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); QTest::newRow("resizable") << &PlasmaWindowInterface::resizableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); QTest::newRow("virtualDesktopChangeable") << &PlasmaWindowInterface::virtualDesktopChangeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } void TestWindowManagement::testRequestsBoolean() { // this test case verifies all the different requests on a PlasmaWindow QFETCH(ServerWindowBooleanSignal, changedSignal); QSignalSpy requestSpy(m_windowInterface, changedSignal); QVERIFY(requestSpy.isValid()); QFETCH(int, flag); org_kde_plasma_window_set_state(*m_window, flag, flag); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 1); QCOMPARE(requestSpy.first().first().toBool(), true); org_kde_plasma_window_set_state(*m_window, flag, 0); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 2); QCOMPARE(requestSpy.last().first().toBool(), false); } void TestWindowManagement::testShowingDesktop() { using namespace KWayland::Server; // this test verifies setting the showing desktop state QVERIFY(!m_windowManagement->isShowingDesktop()); QSignalSpy showingDesktopChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::showingDesktopChanged); QVERIFY(showingDesktopChangedSpy.isValid()); m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Enabled); QVERIFY(showingDesktopChangedSpy.wait()); QCOMPARE(showingDesktopChangedSpy.count(), 1); QCOMPARE(showingDesktopChangedSpy.first().first().toBool(), true); QVERIFY(m_windowManagement->isShowingDesktop()); // setting to same should not change m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Enabled); QVERIFY(!showingDesktopChangedSpy.wait(100)); QCOMPARE(showingDesktopChangedSpy.count(), 1); // setting to other state should change m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Disabled); QVERIFY(showingDesktopChangedSpy.wait()); QCOMPARE(showingDesktopChangedSpy.count(), 2); QCOMPARE(showingDesktopChangedSpy.first().first().toBool(), true); QCOMPARE(showingDesktopChangedSpy.last().first().toBool(), false); QVERIFY(!m_windowManagement->isShowingDesktop()); } void TestWindowManagement::testRequestShowingDesktop_data() { using namespace KWayland::Server; QTest::addColumn("value"); QTest::addColumn("expectedValue"); QTest::newRow("enable") << true << PlasmaWindowManagementInterface::ShowingDesktopState::Enabled; QTest::newRow("disable") << false << PlasmaWindowManagementInterface::ShowingDesktopState::Disabled; } void TestWindowManagement::testRequestShowingDesktop() { // this test verifies requesting show desktop state using namespace KWayland::Server; QSignalSpy requestSpy(m_windowManagementInterface, &PlasmaWindowManagementInterface::requestChangeShowingDesktop); QVERIFY(requestSpy.isValid()); QFETCH(bool, value); m_windowManagement->setShowingDesktop(value); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 1); QTEST(requestSpy.first().first().value(), "expectedValue"); } void TestWindowManagement::testKeepAbove() { using namespace KWayland::Server; // this test verifies setting the keep above state QVERIFY(!m_window->isKeepAbove()); QSignalSpy keepAboveChangedSpy(m_window, &KWayland::Client::PlasmaWindow::keepAboveChanged); QVERIFY(keepAboveChangedSpy.isValid()); m_windowInterface->setKeepAbove(true); QVERIFY(keepAboveChangedSpy.wait()); QCOMPARE(keepAboveChangedSpy.count(), 1); QVERIFY(m_window->isKeepAbove()); // setting to same should not change m_windowInterface->setKeepAbove(true); QVERIFY(!keepAboveChangedSpy.wait(100)); QCOMPARE(keepAboveChangedSpy.count(), 1); // setting to other state should change m_windowInterface->setKeepAbove(false); QVERIFY(keepAboveChangedSpy.wait()); QCOMPARE(keepAboveChangedSpy.count(), 2); QVERIFY(!m_window->isKeepAbove()); } void TestWindowManagement::testKeepBelow() { using namespace KWayland::Server; // this test verifies setting the keep below state QVERIFY(!m_window->isKeepBelow()); QSignalSpy keepBelowChangedSpy(m_window, &KWayland::Client::PlasmaWindow::keepBelowChanged); QVERIFY(keepBelowChangedSpy.isValid()); m_windowInterface->setKeepBelow(true); QVERIFY(keepBelowChangedSpy.wait()); QCOMPARE(keepBelowChangedSpy.count(), 1); QVERIFY(m_window->isKeepBelow()); // setting to same should not change m_windowInterface->setKeepBelow(true); QVERIFY(!keepBelowChangedSpy.wait(100)); QCOMPARE(keepBelowChangedSpy.count(), 1); // setting to other state should change m_windowInterface->setKeepBelow(false); QVERIFY(keepBelowChangedSpy.wait()); QCOMPARE(keepBelowChangedSpy.count(), 2); QVERIFY(!m_window->isKeepBelow()); } void TestWindowManagement::testParentWindow() { using namespace KWayland::Client; // this test verifies the functionality of ParentWindows QCOMPARE(m_windowManagement->windows().count(), 1); auto parentWindow = m_windowManagement->windows().first(); QVERIFY(parentWindow); QVERIFY(parentWindow->parentWindow().isNull()); // now let's create a second window QSignalSpy windowAddedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(windowAddedSpy.isValid()); auto serverTransient = m_windowManagementInterface->createWindow(this); serverTransient->setParentWindow(m_windowInterface); QVERIFY(windowAddedSpy.wait()); auto transient = windowAddedSpy.first().first().value(); QCOMPARE(transient->parentWindow().data(), parentWindow); // let's unset the parent QSignalSpy parentWindowChangedSpy(transient, &PlasmaWindow::parentWindowChanged); QVERIFY(parentWindowChangedSpy.isValid()); serverTransient->setParentWindow(nullptr); QVERIFY(parentWindowChangedSpy.wait()); QVERIFY(transient->parentWindow().isNull()); // and set it again serverTransient->setParentWindow(m_windowInterface); QVERIFY(parentWindowChangedSpy.wait()); QCOMPARE(transient->parentWindow().data(), parentWindow); // now let's try to unmap the parent m_windowInterface->unmap(); m_window = nullptr; m_windowInterface = nullptr; QVERIFY(parentWindowChangedSpy.wait()); QVERIFY(transient->parentWindow().isNull()); } void TestWindowManagement::testGeometry() { using namespace KWayland::Client; QVERIFY(m_window); QCOMPARE(m_window->geometry(), QRect()); QSignalSpy windowGeometryChangedSpy(m_window, &PlasmaWindow::geometryChanged); QVERIFY(windowGeometryChangedSpy.isValid()); m_windowInterface->setGeometry(QRect(20, -10, 30, 40)); QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(m_window->geometry(), QRect(20, -10, 30, 40)); // setting an empty geometry should not be sent to the client m_windowInterface->setGeometry(QRect()); QVERIFY(!windowGeometryChangedSpy.wait(10)); // setting to the geometry which the client still has should not trigger signal m_windowInterface->setGeometry(QRect(20, -10, 30, 40)); QVERIFY(!windowGeometryChangedSpy.wait(10)); // setting another geometry should work, though m_windowInterface->setGeometry(QRect(0, 0, 35, 45)); QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(windowGeometryChangedSpy.count(), 2); QCOMPARE(m_window->geometry(), QRect(0, 0, 35, 45)); // let's bind a second PlasmaWindowManagement to verify the initial setting QScopedPointer pm(m_registry->createPlasmaWindowManagement(m_registry->interface(Registry::Interface::PlasmaWindowManagement).name, m_registry->interface(Registry::Interface::PlasmaWindowManagement).version)); QVERIFY(!pm.isNull()); QSignalSpy windowAddedSpy(pm.data(), &PlasmaWindowManagement::windowCreated); QVERIFY(windowAddedSpy.isValid()); QVERIFY(windowAddedSpy.wait()); auto window = pm->windows().first(); QCOMPARE(window->geometry(), QRect(0, 0, 35, 45)); } void TestWindowManagement::testIcon() { using namespace KWayland::Client; // initially, the server should send us an icon QSignalSpy iconChangedSpy(m_window, &PlasmaWindow::iconChanged); QVERIFY(iconChangedSpy.isValid()); QVERIFY(iconChangedSpy.wait()); QCOMPARE(iconChangedSpy.count(), 1); if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("wayland")); // first goes from themed name to empty m_windowInterface->setIcon(QIcon()); QVERIFY(iconChangedSpy.wait()); QCOMPARE(iconChangedSpy.count(), 2); if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("wayland")); // create an icon with a pixmap QPixmap p(32, 32); p.fill(Qt::red); m_windowInterface->setIcon(p); QVERIFY(iconChangedSpy.wait()); QCOMPARE(iconChangedSpy.count(), 3); QCOMPARE(m_window->icon().pixmap(32, 32), p); // let's set a themed icon m_windowInterface->setIcon(QIcon::fromTheme(QStringLiteral("xorg"))); QVERIFY(iconChangedSpy.wait()); QCOMPARE(iconChangedSpy.count(), 4); if (!QIcon::hasThemeIcon(QStringLiteral("xorg"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("xorg")); } void TestWindowManagement::testPid() { using namespace KWayland::Client; QVERIFY(m_window); QVERIFY(m_window->pid() == 1337); //test server not setting a PID for whatever reason QScopedPointer newWindowInterface(m_windowManagementInterface->createWindow(this)); QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow*))); QVERIFY(windowSpy.wait()); QScopedPointer newWindow( windowSpy.first().first().value()); QVERIFY(newWindow); QVERIFY(newWindow->pid() == 0); } QTEST_MAIN(TestWindowManagement) #include "test_wayland_windowmanagement.moc" diff --git a/autotests/client/test_xdg_decoration.cpp b/autotests/client/test_xdg_decoration.cpp index 4d7f913..6f0b72a 100644 --- a/autotests/client/test_xdg_decoration.cpp +++ b/autotests/client/test_xdg_decoration.cpp @@ -1,250 +1,236 @@ -/******************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/xdgshell.h" #include "../../src/client/xdgdecoration.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/xdgshell_interface.h" #include "../../src/server/xdgdecoration_interface.h" class TestXdgDecoration : public QObject { Q_OBJECT public: explicit TestXdgDecoration(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testDecoration_data(); void testDecoration(); private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositorInterface = nullptr; KWayland::Server::XdgShellInterface *m_xdgShellInterface = nullptr; KWayland::Server::XdgDecorationManagerInterface *m_xdgDecorationManagerInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::XdgShell *m_xdgShell = nullptr; KWayland::Client::XdgDecorationManager *m_xdgDecorationManager = nullptr; QThread *m_thread = nullptr; KWayland::Client::Registry *m_registry = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-server-side-decoration-0"); TestXdgDecoration::TestXdgDecoration(QObject *parent) : QObject(parent) { } void TestXdgDecoration::init() { using namespace KWayland::Server; using namespace KWayland::Client; qRegisterMetaType(); qRegisterMetaType(); delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); QSignalSpy compositorSpy(m_registry, &Registry::compositorAnnounced); QSignalSpy xdgShellSpy(m_registry, &Registry::xdgShellStableAnnounced); QSignalSpy xdgDecorationManagerSpy(m_registry, &Registry::xdgDecorationAnnounced); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_xdgShellInterface = m_display->createXdgShell(XdgShellInterfaceVersion::Stable, m_display); m_xdgShellInterface->create(); QVERIFY(m_xdgShellInterface->isValid()); QVERIFY(xdgShellSpy.wait()); m_xdgShell = m_registry->createXdgShell(xdgShellSpy.first().first().value(), xdgShellSpy.first().last().value(), this); m_xdgDecorationManagerInterface = m_display->createXdgDecorationManager(m_xdgShellInterface, m_display); m_xdgDecorationManagerInterface->create(); QVERIFY(m_xdgDecorationManagerInterface->isValid()); QVERIFY(xdgDecorationManagerSpy.wait()); m_xdgDecorationManager = m_registry->createXdgDecorationManager(xdgDecorationManagerSpy.first().first().value(), xdgDecorationManagerSpy.first().last().value(), this); } void TestXdgDecoration::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_xdgShell) { delete m_xdgShell; m_xdgShell = nullptr; } if (m_xdgDecorationManager) { delete m_xdgDecorationManager; m_xdgDecorationManager = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestXdgDecoration::testDecoration_data() { using namespace KWayland::Client; using namespace KWayland::Server; QTest::addColumn("configuredMode"); QTest::addColumn("configuredModeExp"); QTest::addColumn("setMode"); QTest::addColumn("setModeExp"); const auto serverClient = XdgDecorationInterface::Mode::ClientSide; const auto serverServer = XdgDecorationInterface::Mode::ServerSide; const auto clientClient = XdgDecoration::Mode::ClientSide; const auto clientServer = XdgDecoration::Mode::ServerSide; QTest::newRow("client->client") << serverClient << clientClient << clientClient << serverClient; QTest::newRow("client->server") << serverClient << clientClient << clientServer << serverServer; QTest::newRow("server->client") << serverServer << clientServer << clientClient << serverClient; QTest::newRow("server->server") << serverServer << clientServer << clientServer << serverServer; } void TestXdgDecoration::testDecoration() { using namespace KWayland::Client; using namespace KWayland::Server; QFETCH(KWayland::Server::XdgDecorationInterface::Mode, configuredMode); QFETCH(KWayland::Client::XdgDecoration::Mode, configuredModeExp); QFETCH(KWayland::Client::XdgDecoration::Mode, setMode); QFETCH(KWayland::Server::XdgDecorationInterface::Mode, setModeExp); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QSignalSpy shellSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy decorationCreatedSpy(m_xdgDecorationManagerInterface, &XdgDecorationManagerInterface::xdgDecorationInterfaceCreated); // create shell surface and deco object QScopedPointer surface(m_compositor->createSurface()); QScopedPointer shellSurface(m_xdgShell->createSurface(surface.data())); QScopedPointer decoration(m_xdgDecorationManager->getToplevelDecoration(shellSurface.data())); //and receive all these on the "server" QVERIFY(surfaceCreatedSpy.count() || surfaceCreatedSpy.wait()); QVERIFY(shellSurfaceCreatedSpy.count() || shellSurfaceCreatedSpy.wait()); QVERIFY(decorationCreatedSpy.count() || decorationCreatedSpy.wait()); auto shellSurfaceIface = shellSurfaceCreatedSpy.first().first().value(); auto decorationIface = decorationCreatedSpy.first().first().value(); QVERIFY(decorationIface); QVERIFY(shellSurfaceIface); QCOMPARE(decorationIface->surface(), shellSurfaceIface); QCOMPARE(decorationIface->requestedMode(), XdgDecorationInterface::Mode::Undefined); QSignalSpy clientConfiguredSpy(decoration.data(), &XdgDecoration::modeChanged); QSignalSpy modeRequestedSpy(decorationIface, &XdgDecorationInterface::modeRequested); //server configuring a client decorationIface->configure(configuredMode); quint32 serial = shellSurfaceIface->configure({}); QVERIFY(clientConfiguredSpy.wait()); QCOMPARE(clientConfiguredSpy.first().first().value(), configuredModeExp); shellSurface->ackConfigure(serial); //client requesting another mode decoration->setMode(setMode); QVERIFY(modeRequestedSpy.wait()); QCOMPARE(modeRequestedSpy.first().first().value(), setModeExp); QCOMPARE(decorationIface->requestedMode(), setModeExp); modeRequestedSpy.clear(); decoration->unsetMode(); QVERIFY(modeRequestedSpy.wait()); QCOMPARE(modeRequestedSpy.first().first().value(), XdgDecorationInterface::Mode::Undefined); } QTEST_GUILESS_MAIN(TestXdgDecoration) #include "test_xdg_decoration.moc" diff --git a/autotests/client/test_xdg_foreign.cpp b/autotests/client/test_xdg_foreign.cpp index 21d196c..106b020 100644 --- a/autotests/client/test_xdg_foreign.cpp +++ b/autotests/client/test_xdg_foreign.cpp @@ -1,399 +1,385 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/xdgforeign.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/surface_interface.h" #include "../../src/server/xdgforeign_interface.h" using namespace KWayland::Client; class TestForeign : public QObject { Q_OBJECT public: explicit TestForeign(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testExport(); void testDeleteImported(); void testDeleteChildSurface(); void testDeleteParentSurface(); void testDeleteExported(); void testExportTwoTimes(); void testImportTwoTimes(); private: void doExport(); KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::XdgForeignInterface *m_foreignInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; KWayland::Client::XdgExporter *m_exporter; KWayland::Client::XdgImporter *m_importer; QPointer m_exportedSurface; QPointer m_exportedSurfaceInterface; QPointer m_exported; QPointer m_imported; QPointer m_childSurface; QPointer m_childSurfaceInterface; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-xdg-foreign-0"); TestForeign::TestForeign(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_exporter(nullptr) , m_importer(nullptr) , m_thread(nullptr) { } void TestForeign::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); qRegisterMetaType("KWayland::Server::SurfaceInterface"); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy exporterSpy(®istry, &Registry::exporterUnstableV2Announced); QVERIFY(exporterSpy.isValid()); QSignalSpy importerSpy(®istry, &Registry::importerUnstableV2Announced); QVERIFY(importerSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_foreignInterface = m_display->createXdgForeignInterface(m_display); m_foreignInterface->create(); QVERIFY(m_foreignInterface->isValid()); - + QVERIFY(exporterSpy.wait()); //Both importer and exporter should have been triggered by now QCOMPARE(exporterSpy.count(), 1); QCOMPARE(importerSpy.count(), 1); m_exporter = registry.createXdgExporter(exporterSpy.first().first().value(), exporterSpy.first().last().value(), this); m_importer = registry.createXdgImporter(importerSpy.first().first().value(), importerSpy.first().last().value(), this); } void TestForeign::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_exportedSurfaceInterface) CLEANUP(m_childSurfaceInterface) CLEANUP(m_compositor) CLEANUP(m_exporter) CLEANUP(m_importer) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_foreignInterface) //internally there are some deleteLaters on exported interfaces //we want them processed before we delete the connection if (m_display) { QSignalSpy destroyedSpy(m_display, &QObject::destroyed); m_display->deleteLater(); m_display = nullptr; destroyedSpy.wait(); } #undef CLEANUP } void TestForeign::doExport() { QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_exportedSurface = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); m_exportedSurfaceInterface = serverSurfaceCreated.first().first().value(); //Export a window m_exported = m_exporter->exportTopLevel(m_exportedSurface, m_connection); QVERIFY(m_exported->handle().isEmpty()); QSignalSpy doneSpy(m_exported.data(), &XdgExported::done); QVERIFY(doneSpy.wait()); QVERIFY(!m_exported->handle().isEmpty()); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import the just exported window m_imported = m_importer->importTopLevel(m_exported->handle(), m_connection); QVERIFY(m_imported->isValid()); QSignalSpy childSurfaceInterfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_childSurface = m_compositor->createSurface(); QVERIFY(childSurfaceInterfaceCreated.wait()); m_childSurfaceInterface = childSurfaceInterfaceCreated.first().first().value(); m_childSurface->commit(Surface::CommitFlag::None); m_imported->setParentOf(m_childSurface); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); } void TestForeign::testExport() { doExport(); } void TestForeign::testDeleteImported() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); - + QVERIFY(transientSpy.isValid()); m_imported->deleteLater(); m_imported = nullptr; QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); } void TestForeign::testDeleteChildSurface() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); - + QVERIFY(transientSpy.isValid()); m_childSurface->deleteLater(); QVERIFY(transientSpy.wait()); //when the client surface dies, the server one will eventually die too QSignalSpy surfaceDestroyedSpy(m_childSurfaceInterface, SIGNAL(destroyed())); QVERIFY(surfaceDestroyedSpy.wait()); QVERIFY(!transientSpy.first().at(0).value()); - QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); + QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); } void TestForeign::testDeleteParentSurface() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); - + QVERIFY(transientSpy.isValid()); m_exportedSurface->deleteLater(); QSignalSpy exportedSurfaceDestroyedSpy(m_exportedSurfaceInterface.data(), &QObject::destroyed); QVERIFY(exportedSurfaceDestroyedSpy.isValid()); exportedSurfaceDestroyedSpy.wait(); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); } void TestForeign::testDeleteExported() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QSignalSpy destroyedSpy(m_imported.data(), &KWayland::Client::XdgImported::importedDestroyed); - + QVERIFY(transientSpy.isValid()); m_exported->deleteLater(); m_exported = nullptr; QVERIFY(transientSpy.wait()); QVERIFY(destroyedSpy.wait()); QCOMPARE(transientSpy.first().first().value(), m_childSurfaceInterface.data()); QVERIFY(!transientSpy.first().at(1).value()); QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); QVERIFY(!m_imported->isValid()); } void TestForeign::testExportTwoTimes() { doExport(); //Export second window KWayland::Client::XdgExported *exported2 = m_exporter->exportTopLevel(m_exportedSurface, m_connection); QVERIFY(exported2->handle().isEmpty()); QSignalSpy doneSpy(exported2, &XdgExported::done); QVERIFY(doneSpy.wait()); QVERIFY(!exported2->handle().isEmpty()); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import the just exported window KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(exported2->handle(), m_connection); QVERIFY(imported2->isValid()); //create a second child surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); imported2->setParentOf(childSurface2); QVERIFY(transientSpy.wait()); - + QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api //check the old relationship is still here QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); //check the new relationship QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); } void TestForeign::testImportTwoTimes() { doExport(); QSignalSpy transientSpy(m_foreignInterface, &KWayland::Server::XdgForeignInterface::transientChanged); QVERIFY(transientSpy.isValid()); //Import another time the exported window KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(m_exported->handle(), m_connection); QVERIFY(imported2->isValid()); //create a second child surface QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); QVERIFY(serverSurfaceCreated.wait()); KWayland::Server::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value(); imported2->setParentOf(childSurface2); QVERIFY(transientSpy.wait()); - + QCOMPARE(transientSpy.first().first().value(), childSurface2Interface); QCOMPARE(transientSpy.first().at(1).value(), m_exportedSurfaceInterface.data()); //transientFor api //check the old relationship is still here QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); //check the new relationship QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); } QTEST_GUILESS_MAIN(TestForeign) #include "test_xdg_foreign.moc" diff --git a/autotests/client/test_xdg_output.cpp b/autotests/client/test_xdg_output.cpp index ea57377..19913ba 100644 --- a/autotests/client/test_xdg_output.cpp +++ b/autotests/client/test_xdg_output.cpp @@ -1,174 +1,160 @@ -/******************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/dpms.h" #include "../../src/client/output.h" #include "../../src/client/xdgoutput.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/dpms_interface.h" #include "../../src/server/output_interface.h" #include "../../src/server/xdgoutput_interface.h" // Wayland class TestXdgOutput : public QObject { Q_OBJECT public: explicit TestXdgOutput(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testChanges(); private: KWayland::Server::Display *m_display; KWayland::Server::OutputInterface *m_serverOutput; KWayland::Server::XdgOutputManagerInterface *m_serverXdgOutputManager; KWayland::Server::XdgOutputInterface *m_serverXdgOutput; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-xdg-output-0"); TestXdgOutput::TestXdgOutput(QObject *parent) : QObject(parent) , m_display(nullptr) , m_serverOutput(nullptr) , m_connection(nullptr) , m_thread(nullptr) { } void TestXdgOutput::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_serverOutput = m_display->createOutput(this); m_serverOutput->addMode(QSize(1920, 1080), OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred)); m_serverOutput->setCurrentMode(QSize(1920, 1080)); m_serverOutput->create(); m_serverXdgOutputManager = m_display->createXdgOutputManager(this); m_serverXdgOutputManager->create(); m_serverXdgOutput = m_serverXdgOutputManager->createXdgOutput(m_serverOutput, this); m_serverXdgOutput->setLogicalSize(QSize(1280, 720)); //a 1.5 scale factor m_serverXdgOutput->setLogicalPosition(QPoint(11,12)); //not a sensible value for one monitor, but works for this test m_serverXdgOutput->done(); // setup connection m_connection = new KWayland::Client::ConnectionThread; 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()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); } void TestXdgOutput::cleanup() { if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_serverOutput; m_serverOutput = nullptr; delete m_display; m_display = nullptr; } void TestXdgOutput::testChanges() { // verify the server modes using namespace KWayland::Server; using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); QSignalSpy xdgOutputAnnounced(®istry, SIGNAL(xdgOutputAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(announced.wait()); if (xdgOutputAnnounced.count() != 1) { QVERIFY(xdgOutputAnnounced.wait()); } KWayland::Client::Output output; QSignalSpy outputChanged(&output, SIGNAL(changed())); output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value())); QVERIFY(outputChanged.wait()); QScopedPointer xdgOutputManager(registry.createXdgOutputManager(xdgOutputAnnounced.first().first().value(), xdgOutputAnnounced.first().last().value(), this)); QScopedPointer xdgOutput(xdgOutputManager->getXdgOutput(&output, this)); QSignalSpy xdgOutputChanged(xdgOutput.data(), SIGNAL(changed())); //check details are sent on client bind QVERIFY(xdgOutputChanged.wait()); xdgOutputChanged.clear(); QCOMPARE(xdgOutput->logicalPosition(), QPoint(11,12)); QCOMPARE(xdgOutput->logicalSize(), QSize(1280,720)); //dynamic updates m_serverXdgOutput->setLogicalPosition(QPoint(1000, 2000)); m_serverXdgOutput->setLogicalSize(QSize(100,200)); m_serverXdgOutput->done(); QVERIFY(xdgOutputChanged.wait()); QCOMPARE(xdgOutputChanged.count(), 1); QCOMPARE(xdgOutput->logicalPosition(), QPoint(1000, 2000)); QCOMPARE(xdgOutput->logicalSize(), QSize(100,200)); } QTEST_GUILESS_MAIN(TestXdgOutput) #include "test_xdg_output.moc" diff --git a/autotests/client/test_xdg_shell.cpp b/autotests/client/test_xdg_shell.cpp index a3b93b5..980d8ca 100644 --- a/autotests/client/test_xdg_shell.cpp +++ b/autotests/client/test_xdg_shell.cpp @@ -1,573 +1,559 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "test_xdg_shell.h" XdgShellTest::XdgShellTest(XdgShellInterfaceVersion version): m_version(version) {} static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0"); void XdgShellTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_o1Interface = m_display->createOutput(m_display); m_o1Interface->addMode(QSize(1024, 768)); m_o1Interface->create(); m_o2Interface = m_display->createOutput(m_display); m_o2Interface->addMode(QSize(1024, 768)); m_o2Interface->create(); m_seatInterface = m_display->createSeat(m_display); m_seatInterface->setHasKeyboard(true); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); m_seatInterface->create(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); m_xdgShellInterface = m_display->createXdgShell(m_version, m_display); QCOMPARE(m_xdgShellInterface->interfaceVersion(), m_version); m_xdgShellInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced); QVERIFY(interfaceAnnouncedSpy.isValid()); QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); QVERIFY(outputAnnouncedSpy.isValid()); auto shellAnnouncedSignal = m_version == XdgShellInterfaceVersion::UnstableV5 ? &Registry::xdgShellUnstableV5Announced : m_version == XdgShellInterfaceVersion::UnstableV6 ? &Registry::xdgShellUnstableV6Announced : &Registry::xdgShellStableAnnounced; QSignalSpy xdgShellAnnouncedSpy(®istry, shellAnnouncedSignal); QVERIFY(xdgShellAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); QCOMPARE(outputAnnouncedSpy.count(), 2); m_output1 = registry.createOutput(outputAnnouncedSpy.first().at(0).value(), outputAnnouncedSpy.first().at(1).value(), this); m_output2 = registry.createOutput(outputAnnouncedSpy.last().at(0).value(), outputAnnouncedSpy.last().at(1).value(), this); m_shmPool = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version, this); QVERIFY(m_shmPool); QVERIFY(m_shmPool->isValid()); m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this); QVERIFY(m_compositor); QVERIFY(m_compositor->isValid()); m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); QVERIFY(m_seat); QVERIFY(m_seat->isValid()); QCOMPARE(xdgShellAnnouncedSpy.count(), 1); Registry::Interface iface; switch (m_version) { case XdgShellInterfaceVersion::UnstableV5: iface = Registry::Interface::XdgShellUnstableV5; break; case XdgShellInterfaceVersion::UnstableV6: iface = Registry::Interface::XdgShellUnstableV6; break; case XdgShellInterfaceVersion::Stable: iface = Registry::Interface::XdgShellStable; break; } m_xdgShell = registry.createXdgShell(registry.interface(iface).name, registry.interface(iface).version, this); QVERIFY(m_xdgShell); QVERIFY(m_xdgShell->isValid()); } void XdgShellTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_xdgShell) CLEANUP(m_compositor) CLEANUP(m_shmPool) CLEANUP(m_output1) CLEANUP(m_output2) CLEANUP(m_seat) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_xdgShellInterface) CLEANUP(m_o1Interface); CLEANUP(m_o2Interface); CLEANUP(m_seatInterface); CLEANUP(m_display) #undef CLEANUP } void XdgShellTest::testCreateSurface() { // this test verifies that we can create a surface // first created the signal spies for the server QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); // create surface QScopedPointer surface(m_compositor->createSurface()); QVERIFY(!surface.isNull()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // create shell surface QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(!xdgSurface.isNull()); QVERIFY(xdgSurfaceCreatedSpy.wait()); // verify base things auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QCOMPARE(serverXdgSurface->isConfigurePending(), false); QCOMPARE(serverXdgSurface->title(), QString()); QCOMPARE(serverXdgSurface->windowClass(), QByteArray()); QCOMPARE(serverXdgSurface->isTransient(), false); QCOMPARE(serverXdgSurface->transientFor(), QPointer()); QCOMPARE(serverXdgSurface->surface(), serverSurface); // now let's destroy it QSignalSpy destroyedSpy(serverXdgSurface, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); xdgSurface.reset(); QVERIFY(destroyedSpy.wait()); } void XdgShellTest::testTitle() { // this test verifies that we can change the title of a shell surface // first create surface SURFACE // should not have a title yet QCOMPARE(serverXdgSurface->title(), QString()); // lets' change the title QSignalSpy titleChangedSpy(serverXdgSurface, &XdgShellSurfaceInterface::titleChanged); QVERIFY(titleChangedSpy.isValid()); xdgSurface->setTitle(QStringLiteral("foo")); QVERIFY(titleChangedSpy.wait()); QCOMPARE(titleChangedSpy.count(), 1); QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("foo")); QCOMPARE(serverXdgSurface->title(), QStringLiteral("foo")); } void XdgShellTest::testWindowClass() { // this test verifies that we can change the window class/app id of a shell surface // first create surface SURFACE // should not have a window class yet QCOMPARE(serverXdgSurface->windowClass(), QByteArray()); // let's change the window class QSignalSpy windowClassChanged(serverXdgSurface, &XdgShellSurfaceInterface::windowClassChanged); QVERIFY(windowClassChanged.isValid()); xdgSurface->setAppId(QByteArrayLiteral("org.kde.xdgsurfacetest")); QVERIFY(windowClassChanged.wait()); QCOMPARE(windowClassChanged.count(), 1); QCOMPARE(windowClassChanged.first().first().toByteArray(), QByteArrayLiteral("org.kde.xdgsurfacetest")); QCOMPARE(serverXdgSurface->windowClass(), QByteArrayLiteral("org.kde.xdgsurfacetest")); } void XdgShellTest::testMaximize() { // this test verifies that the maximize/unmaximize calls work SURFACE QSignalSpy maximizeRequestedSpy(serverXdgSurface, &XdgShellSurfaceInterface::maximizedChanged); QVERIFY(maximizeRequestedSpy.isValid()); xdgSurface->setMaximized(true); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.last().first().toBool(), true); xdgSurface->setMaximized(false); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 2); QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false); } void XdgShellTest::testMinimize() { // this test verifies that the minimize request is delivered SURFACE QSignalSpy minimizeRequestedSpy(serverXdgSurface, &XdgShellSurfaceInterface::minimizeRequested); QVERIFY(minimizeRequestedSpy.isValid()); xdgSurface->requestMinimize(); QVERIFY(minimizeRequestedSpy.wait()); QCOMPARE(minimizeRequestedSpy.count(), 1); } void XdgShellTest::testFullscreen() { qRegisterMetaType(); // this test verifies going to/from fullscreen QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QSignalSpy fullscreenSpy(serverXdgSurface, &XdgShellSurfaceInterface::fullscreenChanged); QVERIFY(fullscreenSpy.isValid()); // without an output xdgSurface->setFullscreen(true, nullptr); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 1); QCOMPARE(fullscreenSpy.last().at(0).toBool(), true); QVERIFY(!fullscreenSpy.last().at(1).value()); // unset xdgSurface->setFullscreen(false); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 2); QCOMPARE(fullscreenSpy.last().at(0).toBool(), false); QVERIFY(!fullscreenSpy.last().at(1).value()); // with outputs xdgSurface->setFullscreen(true, m_output1); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 3); QCOMPARE(fullscreenSpy.last().at(0).toBool(), true); QCOMPARE(fullscreenSpy.last().at(1).value(), m_o1Interface); // now other output xdgSurface->setFullscreen(true, m_output2); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 4); QCOMPARE(fullscreenSpy.last().at(0).toBool(), true); QCOMPARE(fullscreenSpy.last().at(1).value(), m_o2Interface); } void XdgShellTest::testShowWindowMenu() { qRegisterMetaType(); // this test verifies that the show window menu request works SURFACE QSignalSpy windowMenuSpy(serverXdgSurface, &XdgShellSurfaceInterface::windowMenuRequested); QVERIFY(windowMenuSpy.isValid()); // TODO: the serial needs to be a proper one xdgSurface->requestShowWindowMenu(m_seat, 20, QPoint(30, 40)); QVERIFY(windowMenuSpy.wait()); QCOMPARE(windowMenuSpy.count(), 1); QCOMPARE(windowMenuSpy.first().at(0).value(), m_seatInterface); QCOMPARE(windowMenuSpy.first().at(1).value(), 20u); QCOMPARE(windowMenuSpy.first().at(2).toPoint(), QPoint(30, 40)); } void XdgShellTest::testMove() { qRegisterMetaType(); // this test verifies that the move request works SURFACE QSignalSpy moveSpy(serverXdgSurface, &XdgShellSurfaceInterface::moveRequested); QVERIFY(moveSpy.isValid()); // TODO: the serial needs to be a proper one xdgSurface->requestMove(m_seat, 50); QVERIFY(moveSpy.wait()); QCOMPARE(moveSpy.count(), 1); QCOMPARE(moveSpy.first().at(0).value(), m_seatInterface); QCOMPARE(moveSpy.first().at(1).value(), 50u); } void XdgShellTest::testResize_data() { QTest::addColumn("edges"); QTest::newRow("none") << Qt::Edges(); QTest::newRow("top") << Qt::Edges(Qt::TopEdge); QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge); QTest::newRow("left") << Qt::Edges(Qt::LeftEdge); QTest::newRow("top left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge); QTest::newRow("bottom left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge); QTest::newRow("right") << Qt::Edges(Qt::RightEdge); QTest::newRow("top right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge); QTest::newRow("bottom right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge); } void XdgShellTest::testResize() { qRegisterMetaType(); // this test verifies that the resize request works SURFACE QSignalSpy resizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::resizeRequested); QVERIFY(resizeSpy.isValid()); // TODO: the serial needs to be a proper one QFETCH(Qt::Edges, edges); xdgSurface->requestResize(m_seat, 60, edges); QVERIFY(resizeSpy.wait()); QCOMPARE(resizeSpy.count(), 1); QCOMPARE(resizeSpy.first().at(0).value(), m_seatInterface); QCOMPARE(resizeSpy.first().at(1).value(), 60u); QCOMPARE(resizeSpy.first().at(2).value(), edges); } void XdgShellTest::testTransient() { // this test verifies that setting the transient for works SURFACE QScopedPointer surface2(m_compositor->createSurface()); QScopedPointer xdgSurface2(m_xdgShell->createSurface(surface2.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface2 = xdgSurfaceCreatedSpy.last().first().value(); QVERIFY(serverXdgSurface2); QVERIFY(!serverXdgSurface->isTransient()); QVERIFY(!serverXdgSurface2->isTransient()); // now make xdsgSurface2 a transient for xdgSurface QSignalSpy transientForSpy(serverXdgSurface2, &XdgShellSurfaceInterface::transientForChanged); QVERIFY(transientForSpy.isValid()); xdgSurface2->setTransientFor(xdgSurface.data()); QVERIFY(transientForSpy.wait()); QCOMPARE(transientForSpy.count(), 1); QVERIFY(serverXdgSurface2->isTransient()); QCOMPARE(serverXdgSurface2->transientFor().data(), serverXdgSurface); QVERIFY(!serverXdgSurface->isTransient()); // unset the transient for xdgSurface2->setTransientFor(nullptr); QVERIFY(transientForSpy.wait()); QCOMPARE(transientForSpy.count(), 2); QVERIFY(!serverXdgSurface2->isTransient()); QVERIFY(serverXdgSurface2->transientFor().isNull()); QVERIFY(!serverXdgSurface->isTransient()); } void XdgShellTest::testPing() { // this test verifies that a ping request is sent to the client SURFACE QSignalSpy pingSpy(m_xdgShellInterface, &XdgShellInterface::pongReceived); QVERIFY(pingSpy.isValid()); quint32 serial = m_xdgShellInterface->ping(serverXdgSurface); QVERIFY(pingSpy.wait()); QCOMPARE(pingSpy.count(), 1); QCOMPARE(pingSpy.takeFirst().at(0).value(), serial); // test of a ping failure // disconnecting the connection thread to the queue will break the connection and pings will do a timeout disconnect(m_connection, &ConnectionThread::eventsRead, m_queue, &EventQueue::dispatch); m_xdgShellInterface->ping(serverXdgSurface); QSignalSpy pingDelayedSpy(m_xdgShellInterface, &XdgShellInterface::pingDelayed); QVERIFY(pingDelayedSpy.wait()); QSignalSpy pingTimeoutSpy(m_xdgShellInterface, &XdgShellInterface::pingTimeout); QVERIFY(pingTimeoutSpy.wait()); } void XdgShellTest::testClose() { // this test verifies that a close request is sent to the client SURFACE QSignalSpy closeSpy(xdgSurface.data(), &XdgShellSurface::closeRequested); QVERIFY(closeSpy.isValid()); serverXdgSurface->close(); QVERIFY(closeSpy.wait()); QCOMPARE(closeSpy.count(), 1); QSignalSpy destroyedSpy(serverXdgSurface, &XdgShellSurfaceInterface::destroyed); QVERIFY(destroyedSpy.isValid()); xdgSurface.reset(); QVERIFY(destroyedSpy.wait()); } void XdgShellTest::testConfigureStates_data() { QTest::addColumn("serverStates"); QTest::addColumn("clientStates"); const auto sa = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Activated); const auto sm = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Maximized); const auto sf = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Fullscreen); const auto sr = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Resizing); const auto ca = XdgShellSurface::States(XdgShellSurface::State::Activated); const auto cm = XdgShellSurface::States(XdgShellSurface::State::Maximized); const auto cf = XdgShellSurface::States(XdgShellSurface::State::Fullscreen); const auto cr = XdgShellSurface::States(XdgShellSurface::State::Resizing); QTest::newRow("none") << XdgShellSurfaceInterface::States() << XdgShellSurface::States(); QTest::newRow("Active") << sa << ca; QTest::newRow("Maximize") << sm << cm; QTest::newRow("Fullscreen") << sf << cf; QTest::newRow("Resizing") << sr << cr; QTest::newRow("Active/Maximize") << (sa | sm) << (ca | cm); QTest::newRow("Active/Fullscreen") << (sa | sf) << (ca | cf); QTest::newRow("Active/Resizing") << (sa | sr) << (ca | cr); QTest::newRow("Maximize/Fullscreen") << (sm | sf) << (cm | cf); QTest::newRow("Maximize/Resizing") << (sm | sr) << (cm | cr); QTest::newRow("Fullscreen/Resizing") << (sf | sr) << (cf | cr); QTest::newRow("Active/Maximize/Fullscreen") << (sa | sm | sf) << (ca | cm | cf); QTest::newRow("Active/Maximize/Resizing") << (sa | sm | sr) << (ca | cm | cr); QTest::newRow("Maximize/Fullscreen|Resizing") << (sm | sf | sr) << (cm | cf | cr); QTest::newRow("Active/Maximize/Fullscreen/Resizing") << (sa | sm | sf | sr) << (ca | cm | cf | cr); } void XdgShellTest::testConfigureStates() { qRegisterMetaType(); // this test verifies that configure states works SURFACE QSignalSpy configureSpy(xdgSurface.data(), &XdgShellSurface::configureRequested); QVERIFY(configureSpy.isValid()); QFETCH(XdgShellSurfaceInterface::States, serverStates); serverXdgSurface->configure(serverStates); QVERIFY(configureSpy.wait()); QCOMPARE(configureSpy.count(), 1); QCOMPARE(configureSpy.first().at(0).toSize(), QSize(0, 0)); QTEST(configureSpy.first().at(1).value(), "clientStates"); QCOMPARE(configureSpy.first().at(2).value(), m_display->serial()); QSignalSpy ackSpy(serverXdgSurface, &XdgShellSurfaceInterface::configureAcknowledged); QVERIFY(ackSpy.isValid()); xdgSurface->ackConfigure(configureSpy.first().at(2).value()); QVERIFY(ackSpy.wait()); QCOMPARE(ackSpy.count(), 1); QCOMPARE(ackSpy.first().first().value(), configureSpy.first().at(2).value()); } void XdgShellTest::testConfigureMultipleAcks() { qRegisterMetaType(); // this test verifies that with multiple configure requests the last acknowledged one acknowledges all SURFACE QSignalSpy configureSpy(xdgSurface.data(), &XdgShellSurface::configureRequested); QVERIFY(configureSpy.isValid()); QSignalSpy sizeChangedSpy(xdgSurface.data(), &XdgShellSurface::sizeChanged); QVERIFY(sizeChangedSpy.isValid()); QSignalSpy ackSpy(serverXdgSurface, &XdgShellSurfaceInterface::configureAcknowledged); QVERIFY(ackSpy.isValid()); serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(10, 20)); const quint32 serial1 = m_display->serial(); serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(20, 30)); const quint32 serial2 = m_display->serial(); QVERIFY(serial1 != serial2); serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(30, 40)); const quint32 serial3 = m_display->serial(); QVERIFY(serial1 != serial3); QVERIFY(serial2 != serial3); QVERIFY(configureSpy.wait()); QCOMPARE(configureSpy.count(), 3); QCOMPARE(configureSpy.at(0).at(0).toSize(), QSize(10, 20)); QCOMPARE(configureSpy.at(0).at(1).value(), XdgShellSurface::States()); QCOMPARE(configureSpy.at(0).at(2).value(), serial1); QCOMPARE(configureSpy.at(1).at(0).toSize(), QSize(20, 30)); QCOMPARE(configureSpy.at(1).at(1).value(), XdgShellSurface::States()); QCOMPARE(configureSpy.at(1).at(2).value(), serial2); QCOMPARE(configureSpy.at(2).at(0).toSize(), QSize(30, 40)); QCOMPARE(configureSpy.at(2).at(1).value(), XdgShellSurface::States()); QCOMPARE(configureSpy.at(2).at(2).value(), serial3); QCOMPARE(sizeChangedSpy.count(), 3); QCOMPARE(sizeChangedSpy.at(0).at(0).toSize(), QSize(10, 20)); QCOMPARE(sizeChangedSpy.at(1).at(0).toSize(), QSize(20, 30)); QCOMPARE(sizeChangedSpy.at(2).at(0).toSize(), QSize(30, 40)); QCOMPARE(xdgSurface->size(), QSize(30, 40)); xdgSurface->ackConfigure(serial3); QVERIFY(ackSpy.wait()); QCOMPARE(ackSpy.count(), 3); QCOMPARE(ackSpy.at(0).first().value(), serial1); QCOMPARE(ackSpy.at(1).first().value(), serial2); QCOMPARE(ackSpy.at(2).first().value(), serial3); // configure once more with a null size serverXdgSurface->configure(XdgShellSurfaceInterface::States()); // should not change size QVERIFY(configureSpy.wait()); QCOMPARE(configureSpy.count(), 4); QCOMPARE(sizeChangedSpy.count(), 3); QCOMPARE(xdgSurface->size(), QSize(30, 40)); } diff --git a/autotests/client/test_xdg_shell.h b/autotests/client/test_xdg_shell.h index 48fe01d..1f7e757 100644 --- a/autotests/client/test_xdg_shell.h +++ b/autotests/client/test_xdg_shell.h @@ -1,106 +1,92 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef TEST_XDG_SHELL_H #define TEST_XDG_SHELL_H // Qt #include // client #include "../../src/client/xdgshell.h" #include "../../src/client/connection_thread.h" #include "../../src/client/compositor.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/output.h" #include "../../src/client/seat.h" #include "../../src/client/shm_pool.h" #include "../../src/client/surface.h" // server #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/output_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/surface_interface.h" #include "../../src/server/xdgshell_interface.h" using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) class XdgShellTest : public QObject { Q_OBJECT protected: XdgShellTest(XdgShellInterfaceVersion version); private Q_SLOTS: void init(); void cleanup(); void testCreateSurface(); void testTitle(); void testWindowClass(); void testMaximize(); void testMinimize(); void testFullscreen(); void testShowWindowMenu(); void testMove(); void testResize_data(); void testResize(); void testTransient(); void testPing(); void testClose(); void testConfigureStates_data(); void testConfigureStates(); void testConfigureMultipleAcks(); protected: XdgShellInterface *m_xdgShellInterface = nullptr; Compositor *m_compositor = nullptr; XdgShell *m_xdgShell = nullptr; Display *m_display = nullptr; CompositorInterface *m_compositorInterface = nullptr; OutputInterface *m_o1Interface = nullptr; OutputInterface *m_o2Interface = nullptr; SeatInterface *m_seatInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; ShmPool *m_shmPool = nullptr; Output *m_output1 = nullptr; Output *m_output2 = nullptr; Seat *m_seat = nullptr; private: XdgShellInterfaceVersion m_version; }; #define SURFACE \ QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); \ QVERIFY(xdgSurfaceCreatedSpy.isValid()); \ QScopedPointer surface(m_compositor->createSurface()); \ QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); \ QCOMPARE(xdgSurface->size(), QSize()); \ QVERIFY(xdgSurfaceCreatedSpy.wait()); \ auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); \ QVERIFY(serverXdgSurface); #endif diff --git a/autotests/client/test_xdg_shell_stable.cpp b/autotests/client/test_xdg_shell_stable.cpp index 8f2fc7d..d978f4b 100644 --- a/autotests/client/test_xdg_shell_stable.cpp +++ b/autotests/client/test_xdg_shell_stable.cpp @@ -1,259 +1,245 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "test_xdg_shell.h" #include class XdgShellTestStable : public XdgShellTest { Q_OBJECT public: XdgShellTestStable() : XdgShellTest(KWayland::Server::XdgShellInterfaceVersion::Stable) {} private Q_SLOTS: void testMaxSize(); void testMinSize(); void testPopup_data(); void testPopup(); void testMultipleRoles1(); void testMultipleRoles2(); void testWindowGeometry(); }; void XdgShellTestStable::testMaxSize() { qRegisterMetaType(); // this test verifies changing the window maxSize QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QSignalSpy maxSizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::maxSizeChanged); QVERIFY(maxSizeSpy.isValid()); xdgSurface->setMaxSize(QSize(100, 100)); surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 1); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(100,100)); QCOMPARE(serverXdgSurface->maximumSize(), QSize(100, 100)); xdgSurface->setMaxSize(QSize(200, 200)); surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 2); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(200,200)); QCOMPARE(serverXdgSurface->maximumSize(), QSize(200, 200)); } void XdgShellTestStable::testPopup_data() { QTest::addColumn("positioner"); XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); QTest::newRow("default") << positioner; XdgPositioner positioner2(QSize(20,20), QRect(101,102,51,52)); QTest::newRow("sizeAndAnchorRect") << positioner2; positioner.setAnchorEdge(Qt::TopEdge | Qt::RightEdge); QTest::newRow("anchorEdge") << positioner; positioner.setGravity(Qt::BottomEdge); QTest::newRow("gravity") << positioner; positioner.setGravity(Qt::TopEdge | Qt::RightEdge); QTest::newRow("gravity2") << positioner; positioner.setConstraints(XdgPositioner::Constraint::SlideX | XdgPositioner::Constraint::FlipY); QTest::newRow("constraints") << positioner; positioner.setConstraints(XdgPositioner::Constraint::SlideX | XdgPositioner::Constraint::SlideY | XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::FlipY | XdgPositioner::Constraint::ResizeX | XdgPositioner::Constraint::ResizeY); QTest::newRow("constraints2") << positioner; positioner.setAnchorOffset(QPoint(4,5)); QTest::newRow("offset") << positioner; } void XdgShellTestStable::testPopup() { QSignalSpy xdgTopLevelCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer xdgParentSurface(m_xdgShell->createSurface(parentSurface.data())); QVERIFY(xdgTopLevelCreatedSpy.wait()); auto serverXdgTopLevel = xdgTopLevelCreatedSpy.first().first().value(); QFETCH(XdgPositioner, positioner); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createPopup(surface.data(), xdgParentSurface.data(), positioner)); QVERIFY(xdgPopupCreatedSpy.wait()); auto serverXdgPopup = xdgPopupCreatedSpy.first().first().value(); QVERIFY(serverXdgPopup); QCOMPARE(serverXdgPopup->initialSize(), positioner.initialSize()); QCOMPARE(serverXdgPopup->anchorRect(), positioner.anchorRect()); QCOMPARE(serverXdgPopup->anchorEdge(), positioner.anchorEdge()); QCOMPARE(serverXdgPopup->gravity(), positioner.gravity()); QCOMPARE(serverXdgPopup->anchorOffset(), positioner.anchorOffset()); //we have different enums for client server, but they share the same values QCOMPARE((int)serverXdgPopup->constraintAdjustments(), (int)positioner.constraints()); QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgTopLevel->surface()); } void XdgShellTestStable::testMinSize() { qRegisterMetaType(); // this test verifies changing the window minSize QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QSignalSpy minSizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::minSizeChanged); QVERIFY(minSizeSpy.isValid()); xdgSurface->setMinSize(QSize(200, 200)); surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 1); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(200,200)); QCOMPARE(serverXdgSurface->minimumSize(), QSize(200, 200)); xdgSurface->setMinSize(QSize(100, 100)); surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 2); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(100,100)); QCOMPARE(serverXdgSurface->minimumSize(), QSize(100, 100)); } //top level then toplevel void XdgShellTestStable::testMultipleRoles1() { //setting multiple roles on an xdg surface should fail QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QVERIFY(xdgPopupCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); //This is testing we work when a client does something stupid //we can't use KWayland API here because by design that stops you from doing anything stupid qDebug() << (xdg_wm_base*)*m_xdgShell; auto xdgSurface = xdg_wm_base_get_xdg_surface(*m_xdgShell, *surface.data()); //create a top level auto xdgTopLevel1 = xdg_surface_get_toplevel(xdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); //now try to create another top level for the same xdg surface. It should fail auto xdgTopLevel2 = xdg_surface_get_toplevel(xdgSurface); QVERIFY(!xdgSurfaceCreatedSpy.wait(10)); xdg_toplevel_destroy(xdgTopLevel1); xdg_toplevel_destroy(xdgTopLevel2); xdg_surface_destroy(xdgSurface); } //toplevel then popup void XdgShellTestStable::testMultipleRoles2() { QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QVERIFY(xdgPopupCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parentSurface(m_compositor->createSurface()); auto parentXdgSurface = xdg_wm_base_get_xdg_surface(*m_xdgShell, *parentSurface.data()); auto xdgTopLevelParent = xdg_surface_get_toplevel(parentXdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto xdgSurface = xdg_wm_base_get_xdg_surface(*m_xdgShell, *surface.data()); //create a top level auto xdgTopLevel1 = xdg_surface_get_toplevel(xdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); //now try to create a popup on the same xdg surface. It should fail auto positioner = xdg_wm_base_create_positioner(*m_xdgShell); xdg_positioner_set_anchor_rect(positioner,10, 10, 10, 10); xdg_positioner_set_size(positioner,10, 100); auto xdgPopup2 = xdg_surface_get_popup(xdgSurface, parentXdgSurface, positioner); QVERIFY(!xdgPopupCreatedSpy.wait(10)); xdg_positioner_destroy(positioner); xdg_toplevel_destroy(xdgTopLevel1); xdg_toplevel_destroy(xdgTopLevelParent); xdg_popup_destroy(xdgPopup2); xdg_surface_destroy(xdgSurface); } void XdgShellTestStable::testWindowGeometry() { SURFACE QSignalSpy windowGeometryChangedSpy(serverXdgSurface, &XdgShellSurfaceInterface::windowGeometryChanged); xdgSurface->setWindowGeometry(QRect(50, 50, 400, 400)); surface->commit(Surface::CommitFlag::None); QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(serverXdgSurface->windowGeometry(), QRect(50, 50, 400, 400)); //add a popup to this surface XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QScopedPointer popupSurface(m_compositor->createSurface()); QScopedPointer xdgPopupSurface(m_xdgShell->createPopup(popupSurface.data(), xdgSurface.data(), positioner)); QVERIFY(xdgPopupCreatedSpy.wait()); auto serverXdgPopup = xdgPopupCreatedSpy.first().first().value(); QVERIFY(serverXdgPopup); QSignalSpy popupWindowGeometryChangedSpy(serverXdgPopup, &XdgShellPopupInterface::windowGeometryChanged); xdgPopupSurface->setWindowGeometry(QRect(60, 60, 300, 300)); popupSurface->commit(Surface::CommitFlag::None); QVERIFY(popupWindowGeometryChangedSpy.wait()); QCOMPARE(serverXdgPopup->windowGeometry(), QRect(60, 60, 300, 300)); } QTEST_GUILESS_MAIN(XdgShellTestStable) #include "test_xdg_shell_stable.moc" diff --git a/autotests/client/test_xdg_shell_v5.cpp b/autotests/client/test_xdg_shell_v5.cpp index adb08b0..3aaafc8 100644 --- a/autotests/client/test_xdg_shell_v5.cpp +++ b/autotests/client/test_xdg_shell_v5.cpp @@ -1,93 +1,79 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "test_xdg_shell.h" class XdgShellTestV5 : public XdgShellTest { Q_OBJECT public: XdgShellTestV5() : XdgShellTest(KWayland::Server::XdgShellInterfaceVersion::UnstableV5) {} private Q_SLOTS: void testPopup(); }; void XdgShellTestV5::testPopup() { // this test verifies that the creation of popups works correctly SURFACE QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QSignalSpy xdgPopupSpy(m_xdgShellInterface, &XdgShellInterface::popupCreated); //check as well as the compat signal, the new signal is also fired QSignalSpy xdgPopupSpyNew(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QVERIFY(xdgPopupSpy.isValid()); QScopedPointer popupSurface(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); // TODO: proper serial QScopedPointer xdgPopup(m_xdgShell->createPopup(popupSurface.data(), surface.data(), m_seat, 120, QPoint(10, 20))); QVERIFY(xdgPopupSpy.wait()); QCOMPARE(xdgPopupSpy.count(), 1); QCOMPARE(xdgPopupSpyNew.count(), 1); QCOMPARE(xdgPopupSpy.first().at(1).value(), m_seatInterface); QCOMPARE(xdgPopupSpy.first().at(2).value(), 120u); auto serverXdgPopup = xdgPopupSpy.first().first().value(); QVERIFY(serverXdgPopup); QCOMPARE(serverXdgPopup->surface(), surfaceCreatedSpy.first().first().value()); QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgSurface->surface()); QCOMPARE(serverXdgPopup->transientOffset(), QPoint(10, 20)); // now also a popup for the popup QScopedPointer popup2Surface(m_compositor->createSurface()); QScopedPointer xdgPopup2(m_xdgShell->createPopup(popup2Surface.data(), popupSurface.data(), m_seat, 121, QPoint(5, 7))); QVERIFY(xdgPopupSpy.wait()); QCOMPARE(xdgPopupSpy.count(), 2); QCOMPARE(xdgPopupSpy.last().at(1).value(), m_seatInterface); QCOMPARE(xdgPopupSpy.last().at(2).value(), 121u); auto serverXdgPopup2 = xdgPopupSpy.last().first().value(); QVERIFY(serverXdgPopup2); QCOMPARE(serverXdgPopup2->surface(), surfaceCreatedSpy.last().first().value()); QCOMPARE(serverXdgPopup2->transientFor().data(), serverXdgPopup->surface()); QCOMPARE(serverXdgPopup2->transientOffset(), QPoint(5, 7)); QSignalSpy popup2DoneSpy(xdgPopup2.data(), &XdgShellPopup::popupDone); QVERIFY(popup2DoneSpy.isValid()); serverXdgPopup2->popupDone(); QVERIFY(popup2DoneSpy.wait()); // TODO: test that this sends also the done to all parents } QTEST_GUILESS_MAIN(XdgShellTestV5) #include "test_xdg_shell_v5.moc" diff --git a/autotests/client/test_xdg_shell_v6.cpp b/autotests/client/test_xdg_shell_v6.cpp index da45b02..37f22b4 100644 --- a/autotests/client/test_xdg_shell_v6.cpp +++ b/autotests/client/test_xdg_shell_v6.cpp @@ -1,230 +1,216 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "test_xdg_shell.h" #include class XdgShellTestV6 : public XdgShellTest { Q_OBJECT public: XdgShellTestV6() : XdgShellTest(KWayland::Server::XdgShellInterfaceVersion::UnstableV6) {} private Q_SLOTS: void testMaxSize(); void testMinSize(); void testPopup_data(); void testPopup(); void testMultipleRoles1(); void testMultipleRoles2(); }; void XdgShellTestV6::testMaxSize() { qRegisterMetaType(); // this test verifies changing the window maxSize QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QSignalSpy maxSizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::maxSizeChanged); QVERIFY(maxSizeSpy.isValid()); xdgSurface->setMaxSize(QSize(100, 100)); surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 1); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(100,100)); QCOMPARE(serverXdgSurface->maximumSize(), QSize(100, 100)); xdgSurface->setMaxSize(QSize(200, 200)); surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 2); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(200,200)); QCOMPARE(serverXdgSurface->maximumSize(), QSize(200, 200)); } void XdgShellTestV6::testPopup_data() { QTest::addColumn("positioner"); XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); QTest::newRow("default") << positioner; XdgPositioner positioner2(QSize(20,20), QRect(101,102,51,52)); QTest::newRow("sizeAndAnchorRect") << positioner2; positioner.setAnchorEdge(Qt::TopEdge | Qt::RightEdge); QTest::newRow("anchorEdge") << positioner; positioner.setGravity(Qt::BottomEdge); QTest::newRow("gravity") << positioner; positioner.setGravity(Qt::TopEdge | Qt::RightEdge); QTest::newRow("gravity2") << positioner; positioner.setConstraints(XdgPositioner::Constraint::SlideX | XdgPositioner::Constraint::FlipY); QTest::newRow("constraints") << positioner; positioner.setConstraints(XdgPositioner::Constraint::SlideX | XdgPositioner::Constraint::SlideY | XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::FlipY | XdgPositioner::Constraint::ResizeX | XdgPositioner::Constraint::ResizeY); QTest::newRow("constraints2") << positioner; positioner.setAnchorOffset(QPoint(4,5)); QTest::newRow("offset") << positioner; } void XdgShellTestV6::testPopup() { QSignalSpy xdgTopLevelCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QScopedPointer parentSurface(m_compositor->createSurface()); QScopedPointer xdgParentSurface(m_xdgShell->createSurface(parentSurface.data())); QVERIFY(xdgTopLevelCreatedSpy.wait()); auto serverXdgTopLevel = xdgTopLevelCreatedSpy.first().first().value(); QFETCH(XdgPositioner, positioner); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createPopup(surface.data(), xdgParentSurface.data(), positioner)); QVERIFY(xdgPopupCreatedSpy.wait()); auto serverXdgPopup = xdgPopupCreatedSpy.first().first().value(); QVERIFY(serverXdgPopup); QCOMPARE(serverXdgPopup->initialSize(), positioner.initialSize()); QCOMPARE(serverXdgPopup->anchorRect(), positioner.anchorRect()); QCOMPARE(serverXdgPopup->anchorEdge(), positioner.anchorEdge()); QCOMPARE(serverXdgPopup->gravity(), positioner.gravity()); QCOMPARE(serverXdgPopup->anchorOffset(), positioner.anchorOffset()); //we have different enums for client server, but they share the same values QCOMPARE((int)serverXdgPopup->constraintAdjustments(), (int)positioner.constraints()); QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgTopLevel->surface()); } void XdgShellTestV6::testMinSize() { qRegisterMetaType(); // this test verifies changing the window minSize QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); QVERIFY(serverXdgSurface); QSignalSpy minSizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::minSizeChanged); QVERIFY(minSizeSpy.isValid()); xdgSurface->setMinSize(QSize(200, 200)); surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 1); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(200,200)); QCOMPARE(serverXdgSurface->minimumSize(), QSize(200, 200)); xdgSurface->setMinSize(QSize(100, 100)); surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 2); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(100,100)); QCOMPARE(serverXdgSurface->minimumSize(), QSize(100, 100)); } //top level then toplevel void XdgShellTestV6::testMultipleRoles1() { //setting multiple roles on an xdg surface should fail QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QVERIFY(xdgPopupCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); //This is testing we work when a client does something stupid //we can't use KWayland API here because by design that stops you from doing anything stupid auto xdgSurface = zxdg_shell_v6_get_xdg_surface(*m_xdgShell, *surface.data()); //create a top level auto xdgTopLevel1 = zxdg_surface_v6_get_toplevel(xdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); //now try to create another top level for the same xdg surface. It should fail auto xdgTopLevel2 = zxdg_surface_v6_get_toplevel(xdgSurface); QVERIFY(!xdgSurfaceCreatedSpy.wait(10)); zxdg_toplevel_v6_destroy(xdgTopLevel1); zxdg_toplevel_v6_destroy(xdgTopLevel2); zxdg_surface_v6_destroy(xdgSurface); } //toplevel then popup void XdgShellTestV6::testMultipleRoles2() { QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); QVERIFY(xdgSurfaceCreatedSpy.isValid()); QVERIFY(xdgPopupCreatedSpy.isValid()); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer parentSurface(m_compositor->createSurface()); auto parentXdgSurface = zxdg_shell_v6_get_xdg_surface(*m_xdgShell, *parentSurface.data()); auto xdgTopLevelParent = zxdg_surface_v6_get_toplevel(parentXdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); auto xdgSurface = zxdg_shell_v6_get_xdg_surface(*m_xdgShell, *surface.data()); //create a top level auto xdgTopLevel1 = zxdg_surface_v6_get_toplevel(xdgSurface); QVERIFY(xdgSurfaceCreatedSpy.wait()); //now try to create a popup on the same xdg surface. It should fail auto positioner = zxdg_shell_v6_create_positioner(*m_xdgShell); zxdg_positioner_v6_set_anchor_rect(positioner,10, 10, 10, 10); zxdg_positioner_v6_set_size(positioner,10, 100); auto xdgPopup2 = zxdg_surface_v6_get_popup(xdgSurface, parentXdgSurface, positioner); QVERIFY(!xdgPopupCreatedSpy.wait(10)); zxdg_positioner_v6_destroy(positioner); zxdg_toplevel_v6_destroy(xdgTopLevel1); zxdg_toplevel_v6_destroy(xdgTopLevelParent); zxdg_popup_v6_destroy(xdgPopup2); zxdg_surface_v6_destroy(xdgSurface); } QTEST_GUILESS_MAIN(XdgShellTestV6) #include "test_xdg_shell_v6.moc" diff --git a/autotests/server/surfaceextension_helper.cpp b/autotests/server/surfaceextension_helper.cpp index 30e4454..04ab3ee 100644 --- a/autotests/server/surfaceextension_helper.cpp +++ b/autotests/server/surfaceextension_helper.cpp @@ -1,61 +1,47 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include #include #include class Window : public QRasterWindow { Q_OBJECT public: explicit Window(); virtual ~Window(); protected: void paintEvent(QPaintEvent *event) override; }; Window::Window() : QRasterWindow() { setGeometry(QRect(0, 0, 200, 200)); } Window::~Window() = default; void Window::paintEvent(QPaintEvent *event) { Q_UNUSED(event) QPainter p(this); p.fillRect(QRect(QPoint(0, 0), size()), Qt::black); } int main(int argc, char **argv) { qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); QGuiApplication app(argc, argv); QScopedPointer w(new Window); w->show(); return app.exec(); } #include "surfaceextension_helper.moc" diff --git a/autotests/server/test_display.cpp b/autotests/server/test_display.cpp index b866d76..b59a05a 100644 --- a/autotests/server/test_display.cpp +++ b/autotests/server/test_display.cpp @@ -1,244 +1,230 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // WaylandServer #include "../../src/server/display.h" #include "../../src/server/clientconnection.h" #include "../../src/server/outputmanagement_interface.h" #include "../../src/server/output_interface.h" // Wayland #include // system #include #include #include using namespace KWayland::Server; class TestWaylandServerDisplay : public QObject { Q_OBJECT private Q_SLOTS: void testSocketName(); void testStartStop(); void testAddRemoveOutput(); void testClientConnection(); void testConnectNoSocket(); void testOutputManagement(); void testAutoSocketName(); }; void TestWaylandServerDisplay::testSocketName() { Display display; QSignalSpy changedSpy(&display, SIGNAL(socketNameChanged(QString))); QVERIFY(changedSpy.isValid()); QCOMPARE(display.socketName(), QStringLiteral("wayland-0")); const QString testSName = QStringLiteral("fooBar"); display.setSocketName(testSName); QCOMPARE(display.socketName(), testSName); QCOMPARE(changedSpy.count(), 1); QCOMPARE(changedSpy.first().first().toString(), testSName); // changing to same name again should not emit signal display.setSocketName(testSName); QCOMPARE(changedSpy.count(), 1); } void TestWaylandServerDisplay::testStartStop() { const QString testSocketName = QStringLiteral("kwin-wayland-server-display-test-0"); QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR")); QVERIFY(runtimeDir.exists()); QVERIFY(!runtimeDir.exists(testSocketName)); Display display; QSignalSpy runningSpy(&display, SIGNAL(runningChanged(bool))); QVERIFY(runningSpy.isValid()); display.setSocketName(testSocketName); QVERIFY(!display.isRunning()); display.start(); // QVERIFY(runningSpy.wait()); QCOMPARE(runningSpy.count(), 1); QVERIFY(runningSpy.first().first().toBool()); QVERIFY(display.isRunning()); QVERIFY(runtimeDir.exists(testSocketName)); display.terminate(); QVERIFY(!display.isRunning()); QCOMPARE(runningSpy.count(), 2); QVERIFY(runningSpy.first().first().toBool()); QVERIFY(!runningSpy.last().first().toBool()); QVERIFY(!runtimeDir.exists(testSocketName)); } void TestWaylandServerDisplay::testAddRemoveOutput() { Display display; display.setSocketName(QStringLiteral("kwin-wayland-server-display-test-output-0")); display.start(); OutputInterface *output = display.createOutput(); QCOMPARE(display.outputs().size(), 1); QCOMPARE(display.outputs().first(), output); // create a second output OutputInterface *output2 = display.createOutput(); QCOMPARE(display.outputs().size(), 2); QCOMPARE(display.outputs().first(), output); QCOMPARE(display.outputs().last(), output2); // remove the first output display.removeOutput(output); QCOMPARE(display.outputs().size(), 1); QCOMPARE(display.outputs().first(), output2); // and delete the second delete output2; QVERIFY(display.outputs().isEmpty()); } void TestWaylandServerDisplay::testClientConnection() { Display display; display.setSocketName(QStringLiteral("kwin-wayland-server-display-test-client-connection")); display.start(); QSignalSpy connectedSpy(&display, SIGNAL(clientConnected(KWayland::Server::ClientConnection*))); QVERIFY(connectedSpy.isValid()); QSignalSpy disconnectedSpy(&display, SIGNAL(clientDisconnected(KWayland::Server::ClientConnection*))); QVERIFY(disconnectedSpy.isValid()); int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto client = wl_client_create(display, sv[0]); QVERIFY(client); QVERIFY(connectedSpy.isEmpty()); QVERIFY(display.connections().isEmpty()); ClientConnection *connection = display.getConnection(client); QVERIFY(connection); QCOMPARE(connection->client(), client); if (getuid() == 0) { QEXPECT_FAIL("", "Please don't run test as root", Continue); } QVERIFY(connection->userId() != 0); if (getgid() == 0) { QEXPECT_FAIL("", "Please don't run test as root", Continue); } QVERIFY(connection->groupId() != 0); QVERIFY(connection->processId() != 0); QCOMPARE(connection->display(), &display); QCOMPARE(connection->executablePath(), QCoreApplication::applicationFilePath()); QCOMPARE((wl_client*)*connection, client); const ClientConnection &constRef = *connection; QCOMPARE((wl_client*)constRef, client); QCOMPARE(connectedSpy.count(), 1); QCOMPARE(connectedSpy.first().first().value(), connection); QCOMPARE(display.connections().count(), 1); QCOMPARE(display.connections().first(), connection); QCOMPARE(connection, display.getConnection(client)); QCOMPARE(connectedSpy.count(), 1); // create a second client int sv2[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv2) >= 0); auto client2 = display.createClient(sv2[0]); QVERIFY(client2); ClientConnection *connection2 = display.getConnection(client2->client()); QVERIFY(connection2); QCOMPARE(connection2, client2); QCOMPARE(connectedSpy.count(), 2); QCOMPARE(connectedSpy.first().first().value(), connection); QCOMPARE(connectedSpy.last().first().value(), connection2); QCOMPARE(connectedSpy.last().first().value(), client2); QCOMPARE(display.connections().count(), 2); QCOMPARE(display.connections().first(), connection); QCOMPARE(display.connections().last(), connection2); QCOMPARE(display.connections().last(), client2); // and destroy QVERIFY(disconnectedSpy.isEmpty()); wl_client_destroy(client); QCOMPARE(disconnectedSpy.count(), 1); QSignalSpy clientDestroyedSpy(client2, &QObject::destroyed); QVERIFY(clientDestroyedSpy.isValid()); client2->destroy(); QVERIFY(clientDestroyedSpy.wait()); QCOMPARE(disconnectedSpy.count(), 2); close(sv[0]); close(sv[1]); close(sv2[0]); close(sv2[1]); QVERIFY(display.connections().isEmpty()); } void TestWaylandServerDisplay::testConnectNoSocket() { Display display; display.start(Display::StartMode::ConnectClientsOnly); QVERIFY(display.isRunning()); // let's try connecting a client int sv[2]; QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0); auto client = display.createClient(sv[0]); QVERIFY(client); wl_client_destroy(client->client()); close(sv[0]); close(sv[1]); } void TestWaylandServerDisplay::testOutputManagement() { Display display; display.setSocketName("kwayland-test-0"); display.start(); auto kwin = display.createOutputManagement(this); kwin->create(); QVERIFY(kwin->isValid()); } void TestWaylandServerDisplay::testAutoSocketName() { QTemporaryDir runtimeDir; QVERIFY(runtimeDir.isValid()); QVERIFY(qputenv("XDG_RUNTIME_DIR", runtimeDir.path().toUtf8())); Display display0; display0.setAutomaticSocketNaming(true); QSignalSpy socketNameChangedSpy0(&display0, SIGNAL(socketNameChanged(QString))); display0.start(); QVERIFY(display0.isRunning()); QCOMPARE(socketNameChangedSpy0.count(), 0); QCOMPARE(display0.socketName(), QStringLiteral("wayland-0")); Display display1; display1.setAutomaticSocketNaming(true); QSignalSpy socketNameChangedSpy1(&display1, SIGNAL(socketNameChanged(QString))); display1.start(); QVERIFY(display1.isRunning()); QCOMPARE(socketNameChangedSpy1.count(), 1); QCOMPARE(display1.socketName(), QStringLiteral("wayland-1")); } QTEST_GUILESS_MAIN(TestWaylandServerDisplay) #include "test_display.moc" diff --git a/autotests/server/test_no_xdg_runtime_dir.cpp b/autotests/server/test_no_xdg_runtime_dir.cpp index 7e8ce61..2ff3a58 100644 --- a/autotests/server/test_no_xdg_runtime_dir.cpp +++ b/autotests/server/test_no_xdg_runtime_dir.cpp @@ -1,58 +1,44 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // WaylandServer #include "../../src/server/display.h" using namespace KWayland::Server; class NoXdgRuntimeDirTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testCreate(); }; void NoXdgRuntimeDirTest::initTestCase() { qunsetenv("XDG_RUNTIME_DIR"); } void NoXdgRuntimeDirTest::testCreate() { // this test verifies that not having an XDG_RUNTIME_DIR is handled gracefully // the server cannot start, but should not crash const QString testSocketName = QStringLiteral("kwayland-test-no-xdg-runtime-dir-0"); Display display; QSignalSpy runningSpy(&display, &Display::runningChanged); QVERIFY(runningSpy.isValid()); display.setSocketName(testSocketName); QVERIFY(!display.isRunning()); display.start(); QVERIFY(!display.isRunning()); // call into dispatchEvents should not crash display.dispatchEvents(); } QTEST_GUILESS_MAIN(NoXdgRuntimeDirTest) #include "test_no_xdg_runtime_dir.moc" diff --git a/autotests/server/test_qt_surface_extension.cpp b/autotests/server/test_qt_surface_extension.cpp index a36d775..b682c83 100644 --- a/autotests/server/test_qt_surface_extension.cpp +++ b/autotests/server/test_qt_surface_extension.cpp @@ -1,104 +1,90 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include #include // WaylandServer #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/output_interface.h" #include "../../src/server/qtsurfaceextension_interface.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" using namespace KWayland::Server; class TestQtSurfaceExtension : public QObject { Q_OBJECT private Q_SLOTS: void testCloseWindow(); }; static const QString s_socketName = QStringLiteral("kwin-wayland-server-qt-surface-extension-0"); void TestQtSurfaceExtension::testCloseWindow() { // this test verifies that we can close windows through the Qt surface extension interface // for this we start a dummy server, launch a QtWayland powered application, wait for the // window it opens, close it and verify that the process terminates Display display; display.setSocketName(s_socketName); display.start(); display.createShm(); SeatInterface *seat = display.createSeat(); seat->setHasKeyboard(true); seat->setHasPointer(true); seat->setHasTouch(true); seat->create(); CompositorInterface *compositor = display.createCompositor(); compositor->create(); ShellInterface *shell = display.createShell(); shell->create(); OutputInterface *output = display.createOutput(); output->setManufacturer(QStringLiteral("org.kde")); output->setModel(QStringLiteral("QtSurfaceExtensionTestCase")); output->addMode(QSize(1280, 1024), OutputInterface::ModeFlag::Preferred | OutputInterface::ModeFlag::Current); output->setPhysicalSize(QSize(1280, 1024) / 3.8); output->create(); // surface extension QtSurfaceExtensionInterface *surfaceExtension = display.createQtSurfaceExtension(); surfaceExtension->create(); // create a signalspy for surfaceCreated QSignalSpy surfaceExtensionSpy(surfaceExtension, &QtSurfaceExtensionInterface::surfaceCreated); QVERIFY(surfaceExtensionSpy.isValid()); // now start our application QString binary = QFINDTESTDATA("surfaceExtensionHelper"); QVERIFY(!binary.isEmpty()); QProcess process; QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert(QStringLiteral("WAYLAND_DISPLAY"), s_socketName); process.setProcessEnvironment(env); process.start(binary, QStringList()); QVERIFY(surfaceExtensionSpy.wait()); QCOMPARE(surfaceExtensionSpy.count(), 1); auto *extension = surfaceExtensionSpy.first().first().value(); QVERIFY(extension); QSignalSpy surfaceExtensionDestroyedSpy(extension, &QObject::destroyed); QVERIFY(surfaceExtensionSpy.isValid()); qRegisterMetaType(); QSignalSpy processStateChangedSpy(&process, &QProcess::stateChanged); QVERIFY(processStateChangedSpy.isValid()); extension->close(); extension->client()->flush(); QVERIFY(processStateChangedSpy.wait()); QCOMPARE(process.exitStatus(), QProcess::NormalExit); if (surfaceExtensionDestroyedSpy.count() == 0) { QVERIFY(surfaceExtensionDestroyedSpy.wait()); } QCOMPARE(surfaceExtensionSpy.count(), 1); } QTEST_GUILESS_MAIN(TestQtSurfaceExtension) #include "test_qt_surface_extension.moc" diff --git a/autotests/server/test_seat.cpp b/autotests/server/test_seat.cpp index 987f737..89d7aeb 100644 --- a/autotests/server/test_seat.cpp +++ b/autotests/server/test_seat.cpp @@ -1,234 +1,220 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ // Qt #include // WaylandServer #include "../../src/server/display.h" #include "../../src/server/pointer_interface.h" #include "../../src/server/seat_interface.h" using namespace KWayland::Server; class TestWaylandServerSeat : public QObject { Q_OBJECT private Q_SLOTS: void testCapabilities(); void testName(); void testPointerButton(); void testPointerPos(); void testDestroyThroughTerminate(); void testRepeatInfo(); void testMultiple(); }; static const QString s_socketName = QStringLiteral("kwin-wayland-server-seat-test-0"); void TestWaylandServerSeat::testCapabilities() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); QVERIFY(!seat->hasKeyboard()); QVERIFY(!seat->hasPointer()); QVERIFY(!seat->hasTouch()); QSignalSpy keyboardSpy(seat, SIGNAL(hasKeyboardChanged(bool))); QVERIFY(keyboardSpy.isValid()); seat->setHasKeyboard(true); QCOMPARE(keyboardSpy.count(), 1); QVERIFY(keyboardSpy.last().first().toBool()); QVERIFY(seat->hasKeyboard()); seat->setHasKeyboard(false); QCOMPARE(keyboardSpy.count(), 2); QVERIFY(!keyboardSpy.last().first().toBool()); QVERIFY(!seat->hasKeyboard()); seat->setHasKeyboard(false); QCOMPARE(keyboardSpy.count(), 2); QSignalSpy pointerSpy(seat, SIGNAL(hasPointerChanged(bool))); QVERIFY(pointerSpy.isValid()); seat->setHasPointer(true); QCOMPARE(pointerSpy.count(), 1); QVERIFY(pointerSpy.last().first().toBool()); QVERIFY(seat->hasPointer()); seat->setHasPointer(false); QCOMPARE(pointerSpy.count(), 2); QVERIFY(!pointerSpy.last().first().toBool()); QVERIFY(!seat->hasPointer()); seat->setHasPointer(false); QCOMPARE(pointerSpy.count(), 2); QSignalSpy touchSpy(seat, SIGNAL(hasTouchChanged(bool))); QVERIFY(touchSpy.isValid()); seat->setHasTouch(true); QCOMPARE(touchSpy.count(), 1); QVERIFY(touchSpy.last().first().toBool()); QVERIFY(seat->hasTouch()); seat->setHasTouch(false); QCOMPARE(touchSpy.count(), 2); QVERIFY(!touchSpy.last().first().toBool()); QVERIFY(!seat->hasTouch()); seat->setHasTouch(false); QCOMPARE(touchSpy.count(), 2); } void TestWaylandServerSeat::testName() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); QCOMPARE(seat->name(), QString()); QSignalSpy nameSpy(seat, SIGNAL(nameChanged(QString))); QVERIFY(nameSpy.isValid()); const QString name = QStringLiteral("foobar"); seat->setName(name); QCOMPARE(seat->name(), name); QCOMPARE(nameSpy.count(), 1); QCOMPARE(nameSpy.first().first().toString(), name); seat->setName(name); QCOMPARE(nameSpy.count(), 1); } void TestWaylandServerSeat::testPointerButton() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); PointerInterface *pointer = seat->focusedPointer(); QVERIFY(!pointer); // no button pressed yet, should be released and no serial QVERIFY(!seat->isPointerButtonPressed(0)); QVERIFY(!seat->isPointerButtonPressed(1)); QCOMPARE(seat->pointerButtonSerial(0), quint32(0)); QCOMPARE(seat->pointerButtonSerial(1), quint32(0)); // mark the button as pressed seat->pointerButtonPressed(0); QVERIFY(seat->isPointerButtonPressed(0)); QCOMPARE(seat->pointerButtonSerial(0), display.serial()); // other button should still be unpressed QVERIFY(!seat->isPointerButtonPressed(1)); QCOMPARE(seat->pointerButtonSerial(1), quint32(0)); // release it again seat->pointerButtonReleased(0); QVERIFY(!seat->isPointerButtonPressed(0)); QCOMPARE(seat->pointerButtonSerial(0), display.serial()); } void TestWaylandServerSeat::testPointerPos() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); QSignalSpy seatPosSpy(seat, SIGNAL(pointerPosChanged(QPointF))); QVERIFY(seatPosSpy.isValid()); PointerInterface *pointer = seat->focusedPointer(); QVERIFY(!pointer); QCOMPARE(seat->pointerPos(), QPointF()); seat->setPointerPos(QPointF(10, 15)); QCOMPARE(seat->pointerPos(), QPointF(10, 15)); QCOMPARE(seatPosSpy.count(), 1); QCOMPARE(seatPosSpy.first().first().toPointF(), QPointF(10, 15)); seat->setPointerPos(QPointF(10, 15)); QCOMPARE(seatPosSpy.count(), 1); seat->setPointerPos(QPointF(5, 7)); QCOMPARE(seat->pointerPos(), QPointF(5, 7)); QCOMPARE(seatPosSpy.count(), 2); QCOMPARE(seatPosSpy.first().first().toPointF(), QPointF(10, 15)); QCOMPARE(seatPosSpy.last().first().toPointF(), QPointF(5, 7)); } void TestWaylandServerSeat::testDestroyThroughTerminate() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); QSignalSpy destroyedSpy(seat, SIGNAL(destroyed(QObject*))); QVERIFY(destroyedSpy.isValid()); display.terminate(); QVERIFY(!destroyedSpy.isEmpty()); } void TestWaylandServerSeat::testRepeatInfo() { Display display; display.setSocketName(s_socketName); display.start(); SeatInterface *seat = display.createSeat(); QCOMPARE(seat->keyRepeatRate(), 0); QCOMPARE(seat->keyRepeatDelay(), 0); seat->setKeyRepeatInfo(25, 660); QCOMPARE(seat->keyRepeatRate(), 25); QCOMPARE(seat->keyRepeatDelay(), 660); // setting negative values should result in 0 seat->setKeyRepeatInfo(-25, -660); QCOMPARE(seat->keyRepeatRate(), 0); QCOMPARE(seat->keyRepeatDelay(), 0); } void TestWaylandServerSeat::testMultiple() { Display display; display.setSocketName(s_socketName); display.start(); QVERIFY(display.seats().isEmpty()); SeatInterface *seat1 = display.createSeat(); QCOMPARE(display.seats().count(), 1); QCOMPARE(display.seats().at(0), seat1); SeatInterface *seat2 = display.createSeat(); QCOMPARE(display.seats().count(), 2); QCOMPARE(display.seats().at(0), seat1); QCOMPARE(display.seats().at(1), seat2); SeatInterface *seat3 = display.createSeat(); QCOMPARE(display.seats().count(), 3); QCOMPARE(display.seats().at(0), seat1); QCOMPARE(display.seats().at(1), seat2); QCOMPARE(display.seats().at(2), seat3); delete seat3; QCOMPARE(display.seats().count(), 2); QCOMPARE(display.seats().at(0), seat1); QCOMPARE(display.seats().at(1), seat2); delete seat2; QCOMPARE(display.seats().count(), 1); QCOMPARE(display.seats().at(0), seat1); delete seat1; QCOMPARE(display.seats().count(), 0); } QTEST_GUILESS_MAIN(TestWaylandServerSeat) #include "test_seat.moc" diff --git a/cmake/Modules/FindWaylandProtocols.cmake b/cmake/Modules/FindWaylandProtocols.cmake index 5803b33..656d6aa 100644 --- a/cmake/Modules/FindWaylandProtocols.cmake +++ b/cmake/Modules/FindWaylandProtocols.cmake @@ -1,60 +1,39 @@ #.rst: # FindWaylandProtocols # ------- # # Try to find wayland-protocols on a Unix system. # # This will define the following variables: # # ``WaylandProtocols_FOUND`` # True if (the requested version of) wayland-protocols is available # ``WaylandProtocols_VERSION`` # The version of wayland-protocols # ``WaylandProtocols_DATADIR`` # The wayland protocols data directory #============================================================================= -# Copyright 2019 Vlad Zahorodnii +# SPDX-FileCopyrightText: 2019 Vlad Zahorodnii # -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# SPDX-License-Identifier: BSD-3-Clause #============================================================================= find_package(PkgConfig) pkg_check_modules(PKG_wayland_protocols QUIET wayland-protocols) set(WaylandProtocols_VERSION ${PKG_wayland_protocols_VERSION}) pkg_get_variable(WaylandProtocols_DATADIR wayland-protocols pkgdatadir) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WaylandProtocols FOUND_VAR WaylandProtocols_FOUND REQUIRED_VARS WaylandProtocols_DATADIR VERSION_VAR WaylandProtocols_VERSION ) include(FeatureSummary) set_package_properties(WaylandProtocols PROPERTIES DESCRIPTION "Specifications of extended Wayland protocols" URL "https://wayland.freedesktop.org/" ) diff --git a/src/client/appmenu.cpp b/src/client/appmenu.cpp index cb38382..fb0f5b9 100644 --- a/src/client/appmenu.cpp +++ b/src/client/appmenu.cpp @@ -1,184 +1,170 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "appmenu.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class AppMenuManager::Private { public: Private() = default; void setup(org_kde_kwin_appmenu_manager *arg); WaylandPointer appmenumanager; EventQueue *queue = nullptr; }; AppMenuManager::AppMenuManager(QObject *parent) : QObject(parent) , d(new Private) { } void AppMenuManager::Private::setup(org_kde_kwin_appmenu_manager *arg) { Q_ASSERT(arg); Q_ASSERT(!appmenumanager); appmenumanager.setup(arg); } AppMenuManager::~AppMenuManager() { release(); } void AppMenuManager::setup(org_kde_kwin_appmenu_manager *appmenumanager) { d->setup(appmenumanager); } void AppMenuManager::release() { d->appmenumanager.release(); } void AppMenuManager::destroy() { d->appmenumanager.destroy(); } AppMenuManager::operator org_kde_kwin_appmenu_manager*() { return d->appmenumanager; } AppMenuManager::operator org_kde_kwin_appmenu_manager*() const { return d->appmenumanager; } bool AppMenuManager::isValid() const { return d->appmenumanager.isValid(); } void AppMenuManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *AppMenuManager::eventQueue() { return d->queue; } AppMenu *AppMenuManager::create(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new AppMenu(parent); auto w = org_kde_kwin_appmenu_manager_create(d->appmenumanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class AppMenu::Private { public: Private(AppMenu *q); void setup(org_kde_kwin_appmenu *arg); WaylandPointer appmenu; private: AppMenu *q; }; AppMenu::Private::Private(AppMenu *q) : q(q) { } AppMenu::AppMenu(QObject *parent) : QObject(parent) , d(new Private(this)) { } void AppMenu::Private::setup(org_kde_kwin_appmenu *arg) { Q_ASSERT(arg); Q_ASSERT(!appmenu); appmenu.setup(arg); } AppMenu::~AppMenu() { release(); } void AppMenu::setup(org_kde_kwin_appmenu *appmenu) { d->setup(appmenu); } void AppMenu::release() { d->appmenu.release(); } void AppMenu::destroy() { d->appmenu.destroy(); } AppMenu::operator org_kde_kwin_appmenu*() { return d->appmenu; } AppMenu::operator org_kde_kwin_appmenu*() const { return d->appmenu; } bool AppMenu::isValid() const { return d->appmenu.isValid(); } void AppMenu::setAddress(const QString &serviceName, const QString &objectPath) { Q_ASSERT(isValid()); org_kde_kwin_appmenu_set_address(d->appmenu, serviceName.toLatin1(), objectPath.toLatin1()); } } } diff --git a/src/client/appmenu.h b/src/client/appmenu.h index fe78325..338bd26 100644 --- a/src/client/appmenu.h +++ b/src/client/appmenu.h @@ -1,202 +1,188 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_APPMENU_H #define KWAYLAND_CLIENT_APPMENU_H #include #include struct org_kde_kwin_appmenu_manager; struct org_kde_kwin_appmenu; namespace KWayland { namespace Client { class EventQueue; class Surface; class AppMenu; /** * @short Wrapper for the org_kde_kwin_appmenu_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_appmenu_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the AppMenuManager interface: * @code * AppMenuManager *c = registry->createAppMenuManager(name, version); * @endcode * * This creates the AppMenuManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * AppMenuManager *c = new AppMenuManager; * c->setup(registry->bindAppMenuManager(name, version)); * @endcode * * The AppMenuManager can be used as a drop-in replacement for any org_kde_kwin_appmenu_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.42 **/ class KWAYLANDCLIENT_EXPORT AppMenuManager : public QObject { Q_OBJECT public: /** * Creates a new AppMenuManager. * Note: after constructing the AppMenuManager it is not yet valid and one needs * to call setup. In order to get a ready to use AppMenuManager prefer using * Registry::createAppMenuManager. **/ explicit AppMenuManager(QObject *parent = nullptr); virtual ~AppMenuManager(); /** * Setup this AppMenuManager to manage the @p appmenumanager. * When using Registry::createAppMenuManager there is no need to call this * method. **/ void setup(org_kde_kwin_appmenu_manager *appmenumanager); /** * @returns @c true if managing a org_kde_kwin_appmenu_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_appmenu_manager interface. * After the interface has been released the AppMenuManager instance is no * longer valid and can be setup with another org_kde_kwin_appmenu_manager interface. **/ void release(); /** * Destroys the data held by this AppMenuManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_appmenu_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, appmenumanager, &AppMenuManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this AppMenuManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this AppMenuManager. **/ EventQueue *eventQueue(); AppMenu *create(Surface *surface, QObject *parent = nullptr); operator org_kde_kwin_appmenu_manager*(); operator org_kde_kwin_appmenu_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the AppMenuManager got created by * Registry::createAppMenuManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * * @since 5.42 **/ class KWAYLANDCLIENT_EXPORT AppMenu : public QObject { Q_OBJECT public: virtual ~AppMenu(); /** * Setup this Appmenu to manage the @p appmenu. * When using AppMenuManager::createAppmenu there is no need to call this * method. **/ void setup(org_kde_kwin_appmenu *appmenu); /** * @returns @c true if managing a org_kde_kwin_appmenu. **/ bool isValid() const; /** * Releases the org_kde_kwin_appmenu interface. * After the interface has been released the Appmenu instance is no * longer valid and can be setup with another org_kde_kwin_appmenu interface. **/ void release(); /** * Destroys the data held by this Appmenu. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_appmenu interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, appmenu, &Appmenu::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the appmenu address. The DBus object should be registered before making this call * Strings should be valid DBus formatted names, in latin1. */ void setAddress(const QString & serviceName, const QString & objectPath); operator org_kde_kwin_appmenu*(); operator org_kde_kwin_appmenu*() const; private: friend class AppMenuManager; explicit AppMenu(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/blur.cpp b/src/client/blur.cpp index 21a6b8d..56e807b 100644 --- a/src/client/blur.cpp +++ b/src/client/blur.cpp @@ -1,176 +1,162 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "blur.h" #include "event_queue.h" #include "region.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN BlurManager::Private { public: Private() = default; WaylandPointer manager; EventQueue *queue = nullptr; }; BlurManager::BlurManager(QObject *parent) : QObject(parent) , d(new Private) { } BlurManager::~BlurManager() { release(); } void BlurManager::release() { d->manager.release(); } void BlurManager::destroy() { d->manager.destroy(); } bool BlurManager::isValid() const { return d->manager.isValid(); } void BlurManager::setup(org_kde_kwin_blur_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager); d->manager.setup(manager); } void BlurManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *BlurManager::eventQueue() { return d->queue; } Blur *BlurManager::createBlur(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); Blur *s = new Blur(parent); auto w = org_kde_kwin_blur_manager_create(d->manager, *surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } void BlurManager::removeBlur(Surface *surface) { Q_ASSERT(isValid()); org_kde_kwin_blur_manager_unset(d->manager, *surface); } BlurManager::operator org_kde_kwin_blur_manager*() { return d->manager; } BlurManager::operator org_kde_kwin_blur_manager*() const { return d->manager; } class Blur::Private { public: WaylandPointer blur; }; Blur::Blur(QObject *parent) : QObject(parent) , d(new Private) { } Blur::~Blur() { release(); } void Blur::release() { d->blur.release(); } void Blur::setup(org_kde_kwin_blur *blur) { Q_ASSERT(blur); Q_ASSERT(!d->blur); d->blur.setup(blur); } void Blur::destroy() { d->blur.destroy(); } bool Blur::isValid() const { return d->blur.isValid(); } void Blur::commit() { Q_ASSERT(isValid()); org_kde_kwin_blur_commit(d->blur); } void Blur::setRegion(Region *region) { org_kde_kwin_blur_set_region(d->blur, *region); } Blur::operator org_kde_kwin_blur*() { return d->blur; } Blur::operator org_kde_kwin_blur*() const { return d->blur; } } } diff --git a/src/client/blur.h b/src/client/blur.h index c76debc..bcc5033 100644 --- a/src/client/blur.h +++ b/src/client/blur.h @@ -1,211 +1,197 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_BLUR_H #define KWAYLAND_BLUR_H #include "buffer.h" #include #include #include #include struct wl_buffer; struct wl_region; struct org_kde_kwin_blur; struct org_kde_kwin_blur_manager; class QMarginsF; class QWindow; namespace KWayland { namespace Client { class EventQueue; class Blur; class Surface; class Region; /** * TODO */ class KWAYLANDCLIENT_EXPORT BlurManager : public QObject { Q_OBJECT public: /** * Creates a new BlurManager. * Note: after constructing the BlurManager it is not yet valid and one needs * to call setup. In order to get a ready to use BlurManager prefer using * Registry::createBlurManager. **/ explicit BlurManager(QObject *parent = nullptr); virtual ~BlurManager(); /** * @returns @c true if managing a org_kde_kwin_blur_manager. **/ bool isValid() const; /** * Setup this BlurManager to manage the @p compositor. * When using Registry::createBlurManager there is no need to call this * method. **/ void setup(org_kde_kwin_blur_manager *compositor); /** * Releases the org_kde_kwin_blur_manager interface. * After the interface has been released the BlurManager instance is no * longer valid and can be setup with another org_kde_kwin_blur_manager interface. **/ void release(); /** * Destroys the data held by this BlurManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_blur_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, compositor, &BlurManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Blur. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Blur. **/ EventQueue *eventQueue(); /** * Creates and setup a new Blur with @p parent. * @param parent The parent to pass to the Blur. * @returns The new created Blur **/ Blur *createBlur(Surface *surface, QObject *parent = nullptr); void removeBlur(Surface *surface); operator org_kde_kwin_blur_manager*(); operator org_kde_kwin_blur_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the BlurManager got created by * Registry::createBlurManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_blur interface. * * This class is a convenient wrapper for the org_kde_kwin_blur interface. * To create a Blur call BlurManager::createBlur. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see BlurManager **/ class KWAYLANDCLIENT_EXPORT Blur : public QObject { Q_OBJECT public: virtual ~Blur(); /** * Setup this Blur to manage the @p blur. * When using BlurManager::createBlur there is no need to call this * method. **/ void setup(org_kde_kwin_blur *blur); /** * Releases the org_kde_kwin_blur interface. * After the interface has been released the Blur instance is no * longer valid and can be setup with another org_kde_kwin_blur interface. **/ void release(); /** * Destroys the data held by this Blur. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_blur interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Blur gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_blur. **/ bool isValid() const; void commit(); /** * Sets the area of the window that will have a blurred * background. - * The region will have to be created with + * The region will have to be created with * Compositor::createRegion(QRegion) */ void setRegion(Region *region); operator org_kde_kwin_blur*(); operator org_kde_kwin_blur*() const; private: friend class BlurManager; explicit Blur(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/buffer.cpp b/src/client/buffer.cpp index 6fe67a6..338265f 100644 --- a/src/client/buffer.cpp +++ b/src/client/buffer.cpp @@ -1,143 +1,129 @@ -/******************************************************************** -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2013 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "buffer.h" #include "buffer_p.h" #include "shm_pool.h" // system #include // wayland #include namespace KWayland { namespace Client { #ifndef K_DOXYGEN const struct wl_buffer_listener Buffer::Private::s_listener = { Buffer::Private::releasedCallback }; #endif Buffer::Private::Private(Buffer *q, ShmPool *parent, wl_buffer *nativeBuffer, const QSize &size, int32_t stride, size_t offset, Format format) : shm(parent) , nativeBuffer(nativeBuffer) , released(false) , size(size) , stride(stride) , offset(offset) , used(false) , format(format) , q(q) { wl_buffer_add_listener(nativeBuffer, &s_listener, this); } Buffer::Private::~Private() { nativeBuffer.release(); } void Buffer::Private::destroy() { nativeBuffer.destroy(); } void Buffer::Private::releasedCallback(void *data, wl_buffer *buffer) { auto b = reinterpret_cast(data); Q_ASSERT(b->nativeBuffer == buffer); b->q->setReleased(true); } Buffer::Buffer(ShmPool *parent, wl_buffer *buffer, const QSize &size, int32_t stride, size_t offset, Format format) : d(new Private(this, parent, buffer, size, stride, offset, format)) { } Buffer::~Buffer() = default; void Buffer::copy(const void *src) { memcpy(address(), src, d->size.height()*d->stride); } uchar *Buffer::address() { return reinterpret_cast(d->shm->poolAddress()) + d->offset; } wl_buffer *Buffer::buffer() const { return d->nativeBuffer; } Buffer::operator wl_buffer*() { return d->nativeBuffer; } Buffer::operator wl_buffer*() const { return d->nativeBuffer; } bool Buffer::isReleased() const { return d->released; } void Buffer::setReleased(bool released) { d->released = released; } QSize Buffer::size() const { return d->size; } int32_t Buffer::stride() const { return d->stride; } bool Buffer::isUsed() const { return d->used; } void Buffer::setUsed(bool used) { d->used = used; } Buffer::Format Buffer::format() const { return d->format; } quint32 Buffer::getId(wl_buffer *b) { return wl_proxy_get_id(reinterpret_cast(b)); } } } diff --git a/src/client/buffer.h b/src/client/buffer.h index 5c9fc9f..61a8297 100644 --- a/src/client/buffer.h +++ b/src/client/buffer.h @@ -1,129 +1,115 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_BUFFER_H #define WAYLAND_BUFFER_H #include #include #include #include struct wl_buffer; namespace KWayland { namespace Client { class ShmPool; /** * @short Wrapper class for wl_buffer interface. * * The Buffer is provided by ShmPool and is owned by ShmPool. * * @see ShmPool **/ class KWAYLANDCLIENT_EXPORT Buffer { public: /** * All image formats supported by the implementation. **/ enum class Format { ARGB32, ///< 32-bit ARGB format, can be used for QImage::Format_ARGB32 and QImage::Format_ARGB32_Premultiplied RGB32 ///< 32-bit RGB format, can be used for QImage::Format_RGB32 }; ~Buffer(); /** * Copies the data from @p src into the Buffer. **/ void copy(const void *src); /** * Sets the Buffer as @p released. * This is automatically invoked when the Wayland server sends the release event. * @param released Whether the Buffer got released by the Wayland server. * @see isReleased **/ void setReleased(bool released); /** * Sets whether the Buffer is used. * If the Buffer may not be reused when it gets released, the user of a Buffer should * mark the Buffer as used. This is needed for example when the memory is shared with * a QImage. As soon as the Buffer can be reused again one should call this method with * @c false again. * * By default a Buffer is not used. * * @param used Whether the Bufer should be marked as used. * @see isUsed **/ void setUsed(bool used); wl_buffer *buffer() const; /** * @returns The size of this Buffer. **/ QSize size() const; /** * @returns The stride (bytes per line) of this Buffer. **/ int32_t stride() const; /** * @returns @c true if the Wayland server doesn't need the Buffer anymore. **/ bool isReleased() const; /** * @returns @c true if the Buffer's user is still needing the Buffer. **/ bool isUsed() const; /** * @returns the memory address of this Buffer. **/ uchar *address(); /** * @returns The image format used by this Buffer. **/ Format format() const; operator wl_buffer*(); operator wl_buffer*() const; typedef QWeakPointer Ptr; /** * Helper method to get the id for a provided native buffer. * @since 5.3 **/ static quint32 getId(wl_buffer *b); private: friend class ShmPool; explicit Buffer(ShmPool *parent, wl_buffer *buffer, const QSize &size, int32_t stride, size_t offset, Format format); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/buffer_p.h b/src/client/buffer_p.h index 8f6ae78..cfb843d 100644 --- a/src/client/buffer_p.h +++ b/src/client/buffer_p.h @@ -1,56 +1,42 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_BUFFER_P_H #define WAYLAND_BUFFER_P_H #include "buffer.h" #include "wayland_pointer_p.h" // wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Buffer::Private { public: Private(Buffer *q, ShmPool *parent, wl_buffer *nativeBuffer, const QSize &size, int32_t stride, size_t offset, Format format); ~Private(); void destroy(); ShmPool *shm; WaylandPointer nativeBuffer; bool released; QSize size; int32_t stride; size_t offset; bool used; Format format; private: Buffer *q; static const struct wl_buffer_listener s_listener; static void releasedCallback(void *data, wl_buffer *wl_buffer); }; } } #endif diff --git a/src/client/compositor.cpp b/src/client/compositor.cpp index aa70f49..b178e84 100644 --- a/src/client/compositor.cpp +++ b/src/client/compositor.cpp @@ -1,147 +1,133 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "compositor.h" #include "event_queue.h" #include "region.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Compositor::Private { public: Private() = default; WaylandPointer compositor; EventQueue *queue = nullptr; }; Compositor::Compositor(QObject *parent) : QObject(parent) , d(new Private) { } Compositor::~Compositor() { release(); } Compositor *Compositor::fromApplication(QObject *parent) { QPlatformNativeInterface *native = qApp->platformNativeInterface(); if (!native) { return nullptr; } wl_compositor *compositor = reinterpret_cast(native->nativeResourceForIntegration(QByteArrayLiteral("compositor"))); if (!compositor) { return nullptr; } Compositor *c = new Compositor(parent); c->d->compositor.setup(compositor, true); return c; } void Compositor::setup(wl_compositor *compositor) { Q_ASSERT(compositor); Q_ASSERT(!d->compositor); d->compositor.setup(compositor); } void Compositor::release() { d->compositor.release(); } void Compositor::destroy() { d->compositor.destroy(); } void Compositor::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *Compositor::eventQueue() { return d->queue; } Surface *Compositor::createSurface(QObject *parent) { Q_ASSERT(isValid()); Surface *s = new Surface(parent); auto w = wl_compositor_create_surface(d->compositor); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } Region *Compositor::createRegion(QObject *parent) { return createRegion(QRegion(), parent); } Region *Compositor::createRegion(const QRegion ®ion, QObject *parent) { Q_ASSERT(isValid()); Region *r = new Region(region, parent); auto w = wl_compositor_create_region(d->compositor); if (d->queue) { d->queue->addProxy(w); } r->setup(w); return r; } std::unique_ptr< Region > Compositor::createRegion(const QRegion ®ion) { return std::unique_ptr(createRegion(region, nullptr)); } Compositor::operator wl_compositor*() { return d->compositor; } Compositor::operator wl_compositor*() const { return d->compositor; } bool Compositor::isValid() const { return d->compositor.isValid(); } } } diff --git a/src/client/compositor.h b/src/client/compositor.h index 9e5aa93..efc4b5d 100644 --- a/src/client/compositor.h +++ b/src/client/compositor.h @@ -1,195 +1,181 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_COMPOSITOR_H #define WAYLAND_COMPOSITOR_H #include #include #include struct wl_compositor; namespace KWayland { namespace Client { class EventQueue; class Region; class Surface; /** * @short Wrapper for the wl_compositor interface. * * This class provides a convenient wrapper for the wl_compositor interface. * It's main purpose is to create a Surface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Compositor interface: * @code * Compositor *c = registry->createCompositor(name, version); * @endcode * * This creates the Compositor and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Compositor *c = new Compositor; * c->setup(registry->bindCompositor(name, version)); * @endcode * * The Compositor can be used as a drop-in replacement for any wl_compositor * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT Compositor : public QObject { Q_OBJECT public: /** * Creates a new Compositor. * Note: after constructing the Compositor it is not yet valid and one needs * to call setup. In order to get a ready to use Compositor prefer using * Registry::createCompositor. **/ explicit Compositor(QObject *parent = nullptr); virtual ~Compositor(); /** * Creates a Compositor for the used QGuiApplication. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr. * * The returned Compositor will be fully setup, which means it manages a wl_compositor. * When the created Compositor gets destroyed the managed wl_compositor won't be disconnected * as that's managed by Qt. * @since 5.4 **/ static Compositor *fromApplication(QObject *parent = nullptr); /** * @returns @c true if managing a wl_compositor. **/ bool isValid() const; /** * Setup this Compositor to manage the @p compositor. * When using Registry::createCompositor there is no need to call this * method. **/ void setup(wl_compositor *compositor); /** * Releases the wl_compositor interface. * After the interface has been released the Compositor instance is no * longer valid and can be setup with another wl_compositor interface. **/ void release(); /** * Destroys the data held by this Compositor. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_compositor interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, compositor, &Compositor::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); /** * Creates and setup a new Surface with @p parent. * @param parent The parent to pass to the Surface. * @returns The new created Surface **/ Surface *createSurface(QObject *parent = nullptr); /** * Creates and setup a new Region with @p parent. * @param parent The parent to pass to the Region. * @returns The new created Region **/ Region *createRegion(QObject *parent = nullptr); /** * Creates and setup a new Region with @p parent. * * The @p region is directly added to the created Region. * @param parent The parent to pass to the Region. * @param region The region to install on the newly created Region * @returns The new created Region **/ Region *createRegion(const QRegion ®ion, QObject *parent); /** * Creates and setup a new Region with @p region installed. * * This overloaded convenience method is intended to be used in the * case that the Region is only needed to setup e.g. input region on * a Surface and is afterwards no longer needed. Setting the input * region has copy semantics and the Region can be destroyed afterwards. * This allows to simplify setting the input region to: * * @code * Surface *s = compositor->createSurface(); * s->setInputRegion(compositor->createRegion(QRegion(0, 0, 10, 10)).get()); * @endcode * * @param region The region to install on the newly created Region * @returns The new created Region **/ std::unique_ptr createRegion(const QRegion ®ion); operator wl_compositor*(); operator wl_compositor*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createCompositor * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/connection_thread.cpp b/src/client/connection_thread.cpp index 4b067e9..28453f2 100644 --- a/src/client/connection_thread.cpp +++ b/src/client/connection_thread.cpp @@ -1,309 +1,295 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "connection_thread.h" #include "logging.h" // Qt #include #include #include #include #include #include #include #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN ConnectionThread::Private { public: Private(ConnectionThread *q); ~Private(); void doInitConnection(); void setupSocketNotifier(); void setupSocketFileWatcher(); wl_display *display = nullptr; int fd = -1; QString socketName; QDir runtimeDir; QScopedPointer socketNotifier; QScopedPointer socketWatcher; bool serverDied = false; bool foreign = false; QMetaObject::Connection eventDispatcherConnection; int error = 0; static QVector connections; static QMutex mutex; private: ConnectionThread *q; }; QVector ConnectionThread::Private::connections = QVector{}; QMutex ConnectionThread::Private::mutex{QMutex::Recursive}; ConnectionThread::Private::Private(ConnectionThread *q) : socketName(QString::fromUtf8(qgetenv("WAYLAND_DISPLAY"))) , runtimeDir(QString::fromUtf8(qgetenv("XDG_RUNTIME_DIR"))) , q(q) { if (socketName.isEmpty()) { socketName = QStringLiteral("wayland-0"); } { QMutexLocker lock(&mutex); connections << q; } } ConnectionThread::Private::~Private() { { QMutexLocker lock(&mutex); connections.removeOne(q); } if (display && !foreign) { wl_display_flush(display); wl_display_disconnect(display); } } void ConnectionThread::Private::doInitConnection() { if (fd != -1) { display = wl_display_connect_to_fd(fd); } else { display = wl_display_connect(socketName.toUtf8().constData()); } if (!display) { qCWarning(KWAYLAND_CLIENT) << "Failed connecting to Wayland display"; emit q->failed(); return; } if (fd != -1) { qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server over file descriptor:" << fd; } else { qCDebug(KWAYLAND_CLIENT) << "Connected to Wayland server at:" << socketName; } // setup socket notifier setupSocketNotifier(); setupSocketFileWatcher(); emit q->connected(); } void ConnectionThread::Private::setupSocketNotifier() { const int fd = wl_display_get_fd(display); socketNotifier.reset(new QSocketNotifier(fd, QSocketNotifier::Read)); QObject::connect(socketNotifier.data(), &QSocketNotifier::activated, q, [this]() { if (!display) { return; } if (wl_display_dispatch(display) == -1) { error = wl_display_get_error(display); if (error != 0) { if (display) { free(display); display = nullptr; } emit q->errorOccurred(); return; } } emit q->eventsRead(); } ); } void ConnectionThread::Private::setupSocketFileWatcher() { if (!runtimeDir.exists() || fd != -1) { return; } socketWatcher.reset(new QFileSystemWatcher); socketWatcher->addPath(runtimeDir.absoluteFilePath(socketName)); QObject::connect(socketWatcher.data(), &QFileSystemWatcher::fileChanged, q, [this] (const QString &file) { if (QFile::exists(file) || serverDied) { return; } qCWarning(KWAYLAND_CLIENT) << "Connection to server went away"; serverDied = true; if (display) { free(display); display = nullptr; } socketNotifier.reset(); // need a new filesystem watcher socketWatcher.reset(new QFileSystemWatcher); socketWatcher->addPath(runtimeDir.absolutePath()); QObject::connect(socketWatcher.data(), &QFileSystemWatcher::directoryChanged, q, [this]() { if (!serverDied) { return; } if (runtimeDir.exists(socketName)) { qCDebug(KWAYLAND_CLIENT) << "Socket reappeared"; socketWatcher.reset(); serverDied = false; error = 0; q->initConnection(); } } ); emit q->connectionDied(); } ); } ConnectionThread::ConnectionThread(QObject *parent) : QObject(parent) , d(new Private(this)) { d->eventDispatcherConnection = connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, [this] { if (d->display) { wl_display_flush(d->display); } }, Qt::DirectConnection); } ConnectionThread::ConnectionThread(wl_display *display, QObject *parent) : QObject(parent) , d(new Private(this)) { d->display = display; d->foreign = true; } ConnectionThread::~ConnectionThread() { disconnect(d->eventDispatcherConnection); } ConnectionThread *ConnectionThread::fromApplication(QObject *parent) { QPlatformNativeInterface *native = qApp->platformNativeInterface(); if (!native) { return nullptr; } wl_display *display = reinterpret_cast(native->nativeResourceForIntegration(QByteArrayLiteral("wl_display"))); if (!display) { return nullptr; } ConnectionThread *ct = new ConnectionThread(display, parent); connect(native, &QObject::destroyed, ct, &ConnectionThread::connectionDied); return ct; } void ConnectionThread::initConnection() { QMetaObject::invokeMethod(this, "doInitConnection", Qt::QueuedConnection); } void ConnectionThread::doInitConnection() { d->doInitConnection(); } void ConnectionThread::setSocketName(const QString &socketName) { if (d->display) { // already initialized return; } d->socketName = socketName; } void ConnectionThread::setSocketFd(int fd) { if (d->display) { // already initialized return; } d->fd = fd; } wl_display *ConnectionThread::display() { return d->display; } QString ConnectionThread::socketName() const { return d->socketName; } void ConnectionThread::flush() { if (!d->display) { return; } wl_display_flush(d->display); } void ConnectionThread::roundtrip() { if (!d->display) { return; } if (d->foreign) { // try to perform roundtrip through the QPA plugin if it's supported if (QPlatformNativeInterface *native = qApp->platformNativeInterface()) { // in case the platform provides a dedicated roundtrip function use that install of wl_display_roundtrip QFunctionPointer roundtripFunction = native->platformFunction(QByteArrayLiteral("roundtrip")); if (roundtripFunction) { roundtripFunction(); return; } } } wl_display_roundtrip(d->display); } bool ConnectionThread::hasError() const { return d->error != 0; } int ConnectionThread::errorCode() const { return d->error; } QVector ConnectionThread::connections() { return Private::connections; } } } diff --git a/src/client/connection_thread.h b/src/client/connection_thread.h index fe65494..a4c4be3 100644 --- a/src/client/connection_thread.h +++ b/src/client/connection_thread.h @@ -1,276 +1,262 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_CONNECTION_THREAD_H #define WAYLAND_CONNECTION_THREAD_H #include #include #include struct wl_display; namespace KWayland { /** * @short KWayland Client. * * This namespace groups all classes related to the Client module. * * The main entry point into the KWayland::Client API is the ConnectionThread class. * It allows to create a Wayland client connection either in a native way or wrap a * connection created by the QtWayland QPA plugin. * * KWayland::Client provides one the one hand a low-level API to interact with the * Wayland API, on the other hand an easy to use convenience API. Each class directly * relates to a low-level Wayland type and allows direct casting into the type. * * On the convenience side KWayland::Client allows easy creation of objects, signals * emitted for Wayland events and easy conversion from Qt to Wayland types. * * Once one has a ConnectionThread created, it's possible to setup a Registry to * get a listing of all registered globals. For each global the Registry provides a convenience * method to create the resource. * * @see ConnectionThread * @see Registry **/ namespace Client { /** * @short Creates and manages the connection to a Wayland server. * * The purpose of this class is to create the connection to a Wayland server * and to manage it. As the name suggests it's intended to move instances of * this class into a dedicated thread. This also means that this class doesn't * inherit QThread. In order to use it in a threaded way one needs to create a * QThread and move the object there: * * @code * ConnectionThread *connection = new ConnectionThread; * QThread *thread = new QThread; * connection->moveToThread(thread); * thread->start(); * @endcode * * To finalize the initialization of the connection one needs to call @link ::initConnection @endlink. * This starts an asynchronous connection initialization. In case the initialization * succeeds the signal @link ::connected @endlink will be emitted, otherwise @link ::failed @endlink will * be emitted: * * @code * connect(connection, &ConnectionThread::connected, [connection] { * qDebug() << "Successfully connected to Wayland server at socket:" << connection->socketName(); * }); * connect(connection, &ConnectionThread::failed, [connection] { * qDebug() << "Failed to connect to Wayland server at socket:" << connection->socketName(); * }); * connection->initConnection(); * @endcode * * This class is also responsible for dispatching events. Whenever new data is available on * the Wayland socket, it will be dispatched and the signal @link ::eventsRead @endlink is emitted. * This allows further event queues in other threads to also dispatch their events. * * Furthermore this class flushes the Wayland connection whenever the QAbstractEventDispatcher * is about to block. * * To disconnect the connection to the Wayland server one should delete the instance of this * class and quit the dedicated thread: * * @code * connection->deleteLater(); * thread->quit(); * thread->wait(); * @endcode * * In addition the ConnectionThread provides integration with QtWayland QPA plugin. For that * it provides a static factory method: * * @code * auto connection = ConnectionThread::fromApplication(); * @endcode * * The semantics of the ConnectionThread are slightly changed if it's integrated with QtWayland. * The ConnectionThread does not hold the connection, does not emit connected or released signals * (one can safely assume that the connection is valid when integrating with the Qt application), * does not dispatch events. Given that the use case of the ConnectionThread is rather limited to * a convenient API around wl_display to allow easily setup an own Registry in a QtWayland powered * application. Also moving the ConnectionThread to a different thread is not necessarily recommended * in that case as QtWayland holds it's connection in an own thread anyway. * **/ class KWAYLANDCLIENT_EXPORT ConnectionThread : public QObject { Q_OBJECT public: explicit ConnectionThread(QObject *parent = nullptr); virtual ~ConnectionThread(); /** * Creates a ConnectionThread for the used QGuiApplication. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr. * * The returned ConnectionThread will be fully setup, which means it manages a wl_display. * There is no need to initConnection and the connected or failed signals won't be emitted. * When the created ConnectionThread gets destroyed the managed wl_display won't be disconnected * as that's managed by Qt. * * The returned ConnectionThread is not able to detect (protocol) error. The signal * {@link errorOccurred} won't be emitted, {@link hasError} will return @c false, even if the * actual connection held by QtWayland is on error. The behavior of QtWayland is to exit the * application on error. * * @since 5.4 **/ static ConnectionThread *fromApplication(QObject *parent = nullptr); /** * The display this ConnectionThread is connected to. * As long as there is no connection this method returns @c null. * @see initConnection **/ wl_display *display(); /** * @returns the name of the socket it connects to. **/ QString socketName() const; /** * Sets the @p socketName to connect to. * Only applies if called before calling initConnection. * The default socket name is derived from environment variable WAYLAND_DISPLAY * and if not set is hard coded to "wayland-0". * * The socket name will be ignored if a file descriptor has been set through @link setSocketFd @endlink. * * @see setSocketFd **/ void setSocketName(const QString &socketName); /** * Sets the socket @p fd to connect to. * Only applies if called before calling initConnection. * If this method is invoked, the connection will be created on the file descriptor * and not on the socket name passed through @link setSocketName @endlink or through the * default environment variable WAYLAND_DISPLAY. * @see setSocketName **/ void setSocketFd(int fd); /** * Trigger a blocking roundtrip to the Wayland server. Ensures that all events are processed * before returning to the event loop. * * @since 5.4 **/ void roundtrip(); /** * @returns whether the Wayland connection experienced an error * @see errorCode * @see errorOccurred * @since 5.23 **/ bool hasError() const; /** * @returns the error code of the last occurred error or @c 0 if the connection doesn't have an error * @see hasError * @see errorOccurred * @since 5.23 **/ int errorCode() const; /** * @returns all connections created in this application * @since 5.37 **/ static QVector connections(); public Q_SLOTS: /** * Initializes the connection in an asynchronous way. * In case the connection gets established the signal @link ::connected @endlink will be * emitted, on failure the signal @link ::failed @endlink will be emitted. * * @see connected * @see failed **/ void initConnection(); /** * Explicitly flush the Wayland display. * @since 5.3 **/ void flush(); Q_SIGNALS: /** * Emitted once a connection to a Wayland server is established. * Normally emitted after invoking initConnection(), but might also be * emitted after re-connecting to another server. **/ void connected(); /** * Emitted if connecting to a Wayland server failed. **/ void failed(); /** * Emitted whenever new events are ready to be read. **/ void eventsRead(); /** * Emitted if the Wayland server connection dies. * If the socket reappears, it is tried to reconnect. **/ void connectionDied(); /** * The Wayland connection experienced a fatal error. * The ConnectionThread is no longer valid, no requests may be sent. * This has the same effects as {@link connectionDied}. * * @see hasError * @see errorCode * @since 5.23 **/ void errorOccurred(); protected: /* * Creates a connection thread from an existing wl_display object * @see ConnectionThread::fromApplication */ explicit ConnectionThread(wl_display *display, QObject *parent); private Q_SLOTS: /** * @internal **/ void doInitConnection(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/contrast.cpp b/src/client/contrast.cpp index cfaf875..df1031e 100644 --- a/src/client/contrast.cpp +++ b/src/client/contrast.cpp @@ -1,191 +1,177 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "contrast.h" #include "event_queue.h" #include "region.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN ContrastManager::Private { public: Private() = default; WaylandPointer manager; EventQueue *queue = nullptr; }; ContrastManager::ContrastManager(QObject *parent) : QObject(parent) , d(new Private) { } ContrastManager::~ContrastManager() { release(); } void ContrastManager::release() { d->manager.release(); } void ContrastManager::destroy() { d->manager.destroy(); } bool ContrastManager::isValid() const { return d->manager.isValid(); } void ContrastManager::setup(org_kde_kwin_contrast_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager); d->manager.setup(manager); } void ContrastManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ContrastManager::eventQueue() { return d->queue; } Contrast *ContrastManager::createContrast(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); Contrast *s = new Contrast(parent); auto w = org_kde_kwin_contrast_manager_create(d->manager, *surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } void ContrastManager::removeContrast(Surface *surface) { Q_ASSERT(isValid()); org_kde_kwin_contrast_manager_unset(d->manager, *surface); } ContrastManager::operator org_kde_kwin_contrast_manager*() { return d->manager; } ContrastManager::operator org_kde_kwin_contrast_manager*() const { return d->manager; } class Contrast::Private { public: WaylandPointer contrast; }; Contrast::Contrast(QObject *parent) : QObject(parent) , d(new Private) { } Contrast::~Contrast() { release(); } void Contrast::release() { d->contrast.release(); } void Contrast::setup(org_kde_kwin_contrast *contrast) { Q_ASSERT(contrast); Q_ASSERT(!d->contrast); d->contrast.setup(contrast); } void Contrast::destroy() { d->contrast.destroy(); } bool Contrast::isValid() const { return d->contrast.isValid(); } void Contrast::commit() { Q_ASSERT(isValid()); org_kde_kwin_contrast_commit(d->contrast); } void Contrast::setRegion(Region *region) { org_kde_kwin_contrast_set_region(d->contrast, *region); } void Contrast::setContrast(qreal contrast) { org_kde_kwin_contrast_set_contrast(d->contrast, wl_fixed_from_double(contrast)); } void Contrast::setIntensity(qreal intensity) { org_kde_kwin_contrast_set_intensity(d->contrast, wl_fixed_from_double(intensity)); } void Contrast::setSaturation(qreal saturation) { org_kde_kwin_contrast_set_saturation(d->contrast, wl_fixed_from_double(saturation)); } Contrast::operator org_kde_kwin_contrast*() { return d->contrast; } Contrast::operator org_kde_kwin_contrast*() const { return d->contrast; } } } diff --git a/src/client/contrast.h b/src/client/contrast.h index 8965798..bae2b77 100644 --- a/src/client/contrast.h +++ b/src/client/contrast.h @@ -1,207 +1,193 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CONTRAST_H #define KWAYLAND_CONTRAST_H #include #include #include #include struct org_kde_kwin_contrast; struct org_kde_kwin_contrast_manager; namespace KWayland { namespace Client { class EventQueue; class Contrast; class Surface; class Region; /** * TODO */ class KWAYLANDCLIENT_EXPORT ContrastManager : public QObject { Q_OBJECT public: /** * Creates a new ContrastManager. * Note: after constructing the ContrastManager it is not yet valid and one needs * to call setup. In order to get a ready to use ContrastManager prefer using * Registry::createContrastManager. **/ explicit ContrastManager(QObject *parent = nullptr); virtual ~ContrastManager(); /** * @returns @c true if managing a org_kde_kwin_contrast_manager. **/ bool isValid() const; /** * Setup this ContrastManager to manage the @p contrastManager. * When using Registry::createContrastManager there is no need to call this * method. **/ void setup(org_kde_kwin_contrast_manager *contrastManager); /** * Releases the org_kde_kwin_contrast_manager interface. * After the interface has been released the ContrastManager instance is no * longer valid and can be setup with another org_kde_kwin_contrast_manager interface. **/ void release(); /** * Destroys the data held by this ContrastManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_contrast_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, contrastManager, &ContrastManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Contrast. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Contrast. **/ EventQueue *eventQueue(); /** * Creates and setup a new Contrast with @p parent. * @param parent The parent to pass to the Contrast. * @returns The new created Contrast **/ Contrast *createContrast(Surface *surface, QObject *parent = nullptr); void removeContrast(Surface *surface); operator org_kde_kwin_contrast_manager*(); operator org_kde_kwin_contrast_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the ContrastManager got created by * Registry::createContrastManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_contrast interface. * * This class is a convenient wrapper for the org_kde_kwin_contrast interface. * To create a Contrast call ContrastManager::createContrast. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see ContrastManager **/ class KWAYLANDCLIENT_EXPORT Contrast : public QObject { Q_OBJECT public: virtual ~Contrast(); /** * Setup this Contrast to manage the @p contrast. * When using ContrastManager::createContrast there is no need to call this * method. **/ void setup(org_kde_kwin_contrast *contrast); /** * Releases the org_kde_kwin_contrast interface. * After the interface has been released the Contrast instance is no * longer valid and can be setup with another org_kde_kwin_contrast interface. **/ void release(); /** * Destroys the data held by this Contrast. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_contrast interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Contrast gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_contrast. **/ bool isValid() const; void commit(); /** * Sets the area of the window that will have a contrasted * background. * The region will have to be created with * Compositor::createRegion(QRegion) */ void setRegion(Region *region); void setContrast(qreal contrast); void setIntensity(qreal intensity); void setSaturation(qreal saturation); operator org_kde_kwin_contrast*(); operator org_kde_kwin_contrast*() const; private: friend class ContrastManager; explicit Contrast(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/datadevice.cpp b/src/client/datadevice.cpp index 34bc625..dac5ee8 100644 --- a/src/client/datadevice.cpp +++ b/src/client/datadevice.cpp @@ -1,263 +1,249 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datadevice.h" #include "dataoffer.h" #include "datasource.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataDevice::Private { public: explicit Private(DataDevice *q); void setup(wl_data_device *d); WaylandPointer device; QScopedPointer selectionOffer; struct Drag { QPointer offer; QPointer surface; }; Drag drag; private: void dataOffer(wl_data_offer *id); void selection(wl_data_offer *id); void dragEnter(quint32 serial, const QPointer &surface, const QPointF &relativeToSurface, wl_data_offer *dataOffer); void dragLeft(); static void dataOfferCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id); static void enterCallback(void *data, wl_data_device *dataDevice, uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id); static void leaveCallback(void *data, wl_data_device *dataDevice); static void motionCallback(void *data, wl_data_device *dataDevice, uint32_t time, wl_fixed_t x, wl_fixed_t y); static void dropCallback(void *data, wl_data_device *dataDevice); static void selectionCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id); static const struct wl_data_device_listener s_listener; DataDevice *q; DataOffer *lastOffer = nullptr; }; const wl_data_device_listener DataDevice::Private::s_listener = { dataOfferCallback, enterCallback, leaveCallback, motionCallback, dropCallback, selectionCallback }; void DataDevice::Private::dataOfferCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dataOffer(id); } void DataDevice::Private::dataOffer(wl_data_offer *id) { Q_ASSERT(!lastOffer); lastOffer = new DataOffer(q, id); Q_ASSERT(lastOffer->isValid()); } void DataDevice::Private::enterCallback(void *data, wl_data_device *dataDevice, uint32_t serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dragEnter(serial, QPointer(Surface::get(surface)), QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)), id); } void DataDevice::Private::dragEnter(quint32 serial, const QPointer &surface, const QPointF &relativeToSurface, wl_data_offer *dataOffer) { drag.surface = surface; Q_ASSERT(*lastOffer == dataOffer); drag.offer = lastOffer; lastOffer = nullptr; emit q->dragEntered(serial, relativeToSurface); } void DataDevice::Private::leaveCallback(void *data, wl_data_device *dataDevice) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->dragLeft(); } void DataDevice::Private::dragLeft() { if (drag.offer) { delete drag.offer; } drag = Drag(); emit q->dragLeft(); } void DataDevice::Private::motionCallback(void *data, wl_data_device *dataDevice, uint32_t time, wl_fixed_t x, wl_fixed_t y) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); emit d->q->dragMotion(QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)), time); } void DataDevice::Private::dropCallback(void *data, wl_data_device *dataDevice) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); emit d->q->dropped(); } void DataDevice::Private::selectionCallback(void *data, wl_data_device *dataDevice, wl_data_offer *id) { auto d = reinterpret_cast(data); Q_ASSERT(d->device == dataDevice); d->selection(id); } void DataDevice::Private::selection(wl_data_offer *id) { if (!id) { selectionOffer.reset(); emit q->selectionCleared(); return; } Q_ASSERT(*lastOffer == id); selectionOffer.reset(lastOffer); lastOffer = nullptr; emit q->selectionOffered(selectionOffer.data()); } DataDevice::Private::Private(DataDevice *q) : q(q) { } void DataDevice::Private::setup(wl_data_device *d) { Q_ASSERT(d); Q_ASSERT(!device.isValid()); device.setup(d); wl_data_device_add_listener(device, &s_listener, this); } DataDevice::DataDevice(QObject *parent) : QObject(parent) , d(new Private(this)) { } DataDevice::~DataDevice() { if (d->drag.offer) { delete d->drag.offer; } release(); } void DataDevice::destroy() { d->device.destroy(); } void DataDevice::release() { d->device.release(); } bool DataDevice::isValid() const { return d->device.isValid(); } void DataDevice::setup(wl_data_device *dataDevice) { d->setup(dataDevice); } void DataDevice::startDragInternally(quint32 serial, Surface *origin, Surface *icon) { startDrag(serial, nullptr, origin, icon); } namespace { static wl_data_source *dataSource(const DataSource *source) { if (!source) { return nullptr; } return *source; } } void DataDevice::startDrag(quint32 serial, DataSource *source, Surface *origin, Surface *icon) { wl_data_device_start_drag(d->device, dataSource(source), *origin, icon ? (wl_surface*)*icon : nullptr, serial); } void DataDevice::setSelection(quint32 serial, DataSource *source) { wl_data_device_set_selection(d->device, dataSource(source), serial); } void DataDevice::clearSelection(quint32 serial) { setSelection(serial); } DataOffer *DataDevice::offeredSelection() const { return d->selectionOffer.data(); } QPointer DataDevice::dragSurface() const { return d->drag.surface; } DataOffer *DataDevice::dragOffer() const { return d->drag.offer; } DataDevice::operator wl_data_device*() { return d->device; } DataDevice::operator wl_data_device*() const { return d->device; } } } diff --git a/src/client/datadevice.h b/src/client/datadevice.h index 21b7dde..6ec5f12 100644 --- a/src/client/datadevice.h +++ b/src/client/datadevice.h @@ -1,158 +1,144 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_DATADEVICE_H #define WAYLAND_DATADEVICE_H #include "dataoffer.h" #include #include struct wl_data_device; namespace KWayland { namespace Client { class DataSource; class Surface; /** * @short DataDevice allows clients to share data by copy-and-paste and drag-and-drop. * * This class is a convenient wrapper for the wl_data_device interface. * To create a DataDevice call DataDeviceManager::getDataDevice. * * @see DataDeviceManager **/ class KWAYLANDCLIENT_EXPORT DataDevice : public QObject { Q_OBJECT public: explicit DataDevice(QObject *parent = nullptr); virtual ~DataDevice(); /** * Setup this DataDevice to manage the @p dataDevice. * When using DataDeviceManager::createDataDevice there is no need to call this * method. **/ void setup(wl_data_device *dataDevice); /** * Releases the wl_data_device interface. * After the interface has been released the DataDevice instance is no * longer valid and can be setup with another wl_data_device interface. **/ void release(); /** * Destroys the data held by this DataDevice. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_device interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataDevice gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_device. **/ bool isValid() const; void startDrag(quint32 serial, DataSource *source, Surface *origin, Surface *icon = nullptr); void startDragInternally(quint32 serial, Surface *origin, Surface *icon = nullptr); void setSelection(quint32 serial, DataSource *source = nullptr); void clearSelection(quint32 serial); DataOffer *offeredSelection() const; /** * @returns the currently focused surface during drag'n'drop on this DataDevice. * @since 5.22 **/ QPointer dragSurface() const; /** * @returns the DataOffer during a drag'n'drop operation. * @since 5.22 **/ DataOffer *dragOffer() const; operator wl_data_device*(); operator wl_data_device*() const; Q_SIGNALS: void selectionOffered(KWayland::Client::DataOffer*); void selectionCleared(); /** * Notification that a drag'n'drop operation entered a Surface on this DataDevice. * * @param serial The serial for this enter * @param relativeToSurface Coordinates relative to the upper-left corner of the Surface. * @see dragSurface * @see dragOffer * @see dragLeft * @see dragMotion * @since 5.22 **/ void dragEntered(quint32 serial, const QPointF &relativeToSurface); /** * Notification that the drag'n'drop operation left the Surface on this DataDevice. * * The leave notification is sent before the enter notification for the new focus. * @see dragEnter * @since 5.22 **/ void dragLeft(); /** * Notification of drag motion events on the current drag surface. * * @param relativeToSurface Coordinates relative to the upper-left corner of the entered Surface. * @param time timestamp with millisecond granularity * @see dragEntered * @since 5.22 **/ void dragMotion(const QPointF &relativeToSurface, quint32 time); /** * Emitted when the implicit grab is removed and the drag'n'drop operation ended on this * DataDevice. * * The client can now start a data transfer on the DataOffer. * @see dragEntered * @see dragOffer * @since 5.22 **/ void dropped(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/datadevicemanager.cpp b/src/client/datadevicemanager.cpp index 7ce6d66..b1429e3 100644 --- a/src/client/datadevicemanager.cpp +++ b/src/client/datadevicemanager.cpp @@ -1,120 +1,106 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datadevicemanager.h" #include "datadevice.h" #include "datasource.h" #include "event_queue.h" #include "seat.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataDeviceManager::Private { public: WaylandPointer manager; EventQueue *queue = nullptr; }; DataDeviceManager::DataDeviceManager(QObject *parent) : QObject(parent) , d(new Private) { } DataDeviceManager::~DataDeviceManager() { release(); } void DataDeviceManager::release() { d->manager.release(); } void DataDeviceManager::destroy() { d->manager.destroy(); } bool DataDeviceManager::isValid() const { return d->manager.isValid(); } void DataDeviceManager::setup(wl_data_device_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager.isValid()); d->manager.setup(manager); } EventQueue *DataDeviceManager::eventQueue() { return d->queue; } void DataDeviceManager::setEventQueue(EventQueue *queue) { d->queue = queue; } DataSource *DataDeviceManager::createDataSource(QObject *parent) { Q_ASSERT(isValid()); DataSource *s = new DataSource(parent); auto w = wl_data_device_manager_create_data_source(d->manager); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } DataDevice *DataDeviceManager::getDataDevice(Seat *seat, QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(seat); DataDevice *device = new DataDevice(parent); auto w = wl_data_device_manager_get_data_device(d->manager, *seat); if (d->queue) { d->queue->addProxy(w); } device->setup(w); return device; } DataDeviceManager::operator wl_data_device_manager*() const { return d->manager; } DataDeviceManager::operator wl_data_device_manager*() { return d->manager; } } } diff --git a/src/client/datadevicemanager.h b/src/client/datadevicemanager.h index 1834bc2..7c910fe 100644 --- a/src/client/datadevicemanager.h +++ b/src/client/datadevicemanager.h @@ -1,156 +1,142 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_DATA_DEVICE_MANAGER_H #define WAYLAND_DATA_DEVICE_MANAGER_H #include #include struct wl_data_device_manager; namespace KWayland { namespace Client { class EventQueue; class DataDevice; class DataSource; class Seat; /** * @short Wrapper for the wl_data_device_manager interface. * * This class provides a convenient wrapper for the wl_data_device_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the DataDeviceManager interface: * @code * DataDeviceManager *m = registry->createDataDeviceManager(name, version); * @endcode * * This creates the DataDeviceManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * DataDeviceManager *m = new DataDeviceManager; * m->setup(registry->bindDataDeviceManager(name, version)); * @endcode * * The DataDeviceManager can be used as a drop-in replacement for any wl_data_device_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT DataDeviceManager : public QObject { Q_OBJECT public: /** * Drag and Drop actions supported by DataSource and DataOffer. * @since 5.42 **/ enum class DnDAction { None = 0, Copy = 1 << 0, Move = 1 << 1, Ask = 1 << 2 }; Q_DECLARE_FLAGS(DnDActions, DnDAction) /** * Creates a new Compositor. * Note: after constructing the Compositor it is not yet valid and one needs * to call setup. In order to get a ready to use Compositor prefer using * Registry::createCompositor. **/ explicit DataDeviceManager(QObject *parent = nullptr); virtual ~DataDeviceManager(); /** * @returns @c true if managing a wl_data_device_manager. **/ bool isValid() const; /** * Setup this DataDeviceManager to manage the @p manager. * When using Registry::createDataDeviceManager there is no need to call this * method. **/ void setup(wl_data_device_manager *manager); /** * Releases the wl_data_device_manager interface. * After the interface has been released the DataDeviceManager instance is no * longer valid and can be setup with another wl_data_device_manager interface. **/ void release(); /** * Destroys the data held by this DataDeviceManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_device_manager interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataDeviceManager gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a DataSource. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a DataSource. **/ EventQueue *eventQueue(); DataSource *createDataSource(QObject *parent = nullptr); DataDevice *getDataDevice(Seat *seat, QObject *parent = nullptr); operator wl_data_device_manager*(); operator wl_data_device_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createDataDeviceManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::DataDeviceManager::DnDActions) #endif diff --git a/src/client/dataoffer.cpp b/src/client/dataoffer.cpp index b814c20..17e3c00 100644 --- a/src/client/dataoffer.cpp +++ b/src/client/dataoffer.cpp @@ -1,242 +1,228 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "dataoffer.h" #include "datadevice.h" #include "wayland_pointer_p.h" // Qt #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataOffer::Private { public: Private(wl_data_offer *offer, DataOffer *q); WaylandPointer dataOffer; QList mimeTypes; DataDeviceManager::DnDActions sourceActions = DataDeviceManager::DnDAction::None; DataDeviceManager::DnDAction selectedAction = DataDeviceManager::DnDAction::None; private: void offer(const QString &mimeType); void setAction(DataDeviceManager::DnDAction action); static void offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType); static void sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions); static void actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action); DataOffer *q; static const struct wl_data_offer_listener s_listener; }; #ifndef K_DOXYGEN const struct wl_data_offer_listener DataOffer::Private::s_listener = { offerCallback, sourceActionsCallback, actionCallback }; #endif DataOffer::Private::Private(wl_data_offer *offer, DataOffer *q) : q(q) { dataOffer.setup(offer); wl_data_offer_add_listener(offer, &s_listener, this); } void DataOffer::Private::offerCallback(void *data, wl_data_offer *dataOffer, const char *mimeType) { auto d = reinterpret_cast(data); Q_ASSERT(d->dataOffer == dataOffer); d->offer(QString::fromUtf8(mimeType)); } void DataOffer::Private::offer(const QString &mimeType) { QMimeDatabase db; const auto &m = db.mimeTypeForName(mimeType); if (m.isValid()) { mimeTypes << m; emit q->mimeTypeOffered(m.name()); } } void DataOffer::Private::sourceActionsCallback(void *data, wl_data_offer *wl_data_offer, uint32_t source_actions) { Q_UNUSED(wl_data_offer) DataDeviceManager::DnDActions actions; if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { actions |= DataDeviceManager::DnDAction::Copy; } if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { actions |= DataDeviceManager::DnDAction::Move; } if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { actions |= DataDeviceManager::DnDAction::Ask; } auto d = reinterpret_cast(data); if (d->sourceActions != actions) { d->sourceActions = actions; emit d->q->sourceDragAndDropActionsChanged(); } } void DataOffer::Private::actionCallback(void *data, wl_data_offer *wl_data_offer, uint32_t dnd_action) { Q_UNUSED(wl_data_offer) auto d = reinterpret_cast(data); switch(dnd_action) { case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: d->setAction(DataDeviceManager::DnDAction::Copy); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: d->setAction(DataDeviceManager::DnDAction::Move); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: d->setAction(DataDeviceManager::DnDAction::Ask); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: d->setAction(DataDeviceManager::DnDAction::None); break; default: Q_UNREACHABLE(); } } void DataOffer::Private::setAction(DataDeviceManager::DnDAction action) { if (action == selectedAction) { return; } selectedAction = action; emit q->selectedDragAndDropActionChanged(); } DataOffer::DataOffer(DataDevice *parent, wl_data_offer *dataOffer) : QObject(parent) , d(new Private(dataOffer, this)) { } DataOffer::~DataOffer() { release(); } void DataOffer::release() { d->dataOffer.release(); } void DataOffer::destroy() { d->dataOffer.destroy(); } bool DataOffer::isValid() const { return d->dataOffer.isValid(); } QList< QMimeType > DataOffer::offeredMimeTypes() const { return d->mimeTypes; } void DataOffer::receive(const QMimeType &mimeType, qint32 fd) { receive(mimeType.name(), fd); } void DataOffer::receive(const QString &mimeType, qint32 fd) { Q_ASSERT(isValid()); wl_data_offer_receive(d->dataOffer, mimeType.toUtf8().constData(), fd); } DataOffer::operator wl_data_offer*() { return d->dataOffer; } DataOffer::operator wl_data_offer*() const { return d->dataOffer; } void DataOffer::dragAndDropFinished() { Q_ASSERT(isValid()); if (wl_proxy_get_version(d->dataOffer) < WL_DATA_OFFER_FINISH_SINCE_VERSION) { return; } wl_data_offer_finish(d->dataOffer); } DataDeviceManager::DnDActions DataOffer::sourceDragAndDropActions() const { return d->sourceActions; } void DataOffer::setDragAndDropActions(DataDeviceManager::DnDActions supported, DataDeviceManager::DnDAction preferred) { if (wl_proxy_get_version(d->dataOffer) < WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { return; } auto toWayland = [] (DataDeviceManager::DnDAction action) { switch (action) { case DataDeviceManager::DnDAction::Copy: return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; case DataDeviceManager::DnDAction::Move: return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; case DataDeviceManager::DnDAction::Ask: return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; case DataDeviceManager::DnDAction::None: return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; default: Q_UNREACHABLE(); } }; uint32_t wlSupported = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; if (supported.testFlag(DataDeviceManager::DnDAction::Copy)) { wlSupported |= toWayland(DataDeviceManager::DnDAction::Copy); } if (supported.testFlag(DataDeviceManager::DnDAction::Move)) { wlSupported |= toWayland(DataDeviceManager::DnDAction::Move); } if (supported.testFlag(DataDeviceManager::DnDAction::Ask)) { wlSupported |= toWayland(DataDeviceManager::DnDAction::Ask); } wl_data_offer_set_actions(d->dataOffer, wlSupported, toWayland(preferred)); } DataDeviceManager::DnDAction DataOffer::selectedDragAndDropAction() const { return d->selectedAction; } } } diff --git a/src/client/dataoffer.h b/src/client/dataoffer.h index cf954d9..b39f66f 100644 --- a/src/client/dataoffer.h +++ b/src/client/dataoffer.h @@ -1,145 +1,131 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_DATAOFFER_H #define WAYLAND_DATAOFFER_H #include #include #include "datadevicemanager.h" struct wl_data_offer; class QMimeType; namespace KWayland { namespace Client { class DataDevice; /** * @short Wrapper for the wl_data_offer interface. * * This class is a convenient wrapper for the wl_data_offer interface. * The DataOffer gets created by DataDevice. * * @see DataOfferManager **/ class KWAYLANDCLIENT_EXPORT DataOffer : public QObject { Q_OBJECT public: virtual ~DataOffer(); /** * Releases the wl_data_offer interface. * After the interface has been released the DataOffer instance is no * longer valid and can be setup with another wl_data_offer interface. **/ void release(); /** * Destroys the data held by this DataOffer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_offer interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataOffer gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_offer. **/ bool isValid() const; QList offeredMimeTypes() const; void receive(const QMimeType &mimeType, qint32 fd); void receive(const QString &mimeType, qint32 fd); /** * Notifies the compositor that the drag destination successfully * finished the drag-and-drop operation. * * After this operation it is only allowed to release the DataOffer. * * @since 5.42 **/ void dragAndDropFinished(); /** * The actions offered by the DataSource. * @since 5.42 * @see sourceDragAndDropActionsChanged **/ DataDeviceManager::DnDActions sourceDragAndDropActions() const; /** * Sets the @p supported and @p preferred Drag and Drop actions. * @since 5.42 **/ void setDragAndDropActions(DataDeviceManager::DnDActions supported, DataDeviceManager::DnDAction preferred); /** * The currently selected drag and drop action by the compositor. * @see selectedDragAndDropActionChanged * @since 5.42 **/ DataDeviceManager::DnDAction selectedDragAndDropAction() const; operator wl_data_offer*(); operator wl_data_offer*() const; Q_SIGNALS: void mimeTypeOffered(const QString&); /** * Emitted whenever the @link{sourceDragAndDropActions} changed, e.g. on enter or when * the DataSource changes the supported actions. * @see sourceDragAndDropActions * @since 5.42 **/ void sourceDragAndDropActionsChanged(); /** * Emitted whenever the selected drag and drop action changes. * @see selectedDragAndDropAction * @since 5.42 **/ void selectedDragAndDropActionChanged(); private: friend class DataDevice; explicit DataOffer(DataDevice *parent, wl_data_offer *dataOffer); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::DataOffer*) #endif diff --git a/src/client/datasource.cpp b/src/client/datasource.cpp index 615fe8c..aaf0629 100644 --- a/src/client/datasource.cpp +++ b/src/client/datasource.cpp @@ -1,218 +1,204 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datasource.h" #include "wayland_pointer_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DataSource::Private { public: explicit Private(DataSource *q); void setup(wl_data_source *s); WaylandPointer source; DataDeviceManager::DnDAction selectedAction = DataDeviceManager::DnDAction::None; private: void setAction(DataDeviceManager::DnDAction action); static void targetCallback(void *data, wl_data_source *dataSource, const char *mimeType); static void sendCallback(void *data, wl_data_source *dataSource, const char *mimeType, int32_t fd); static void cancelledCallback(void *data, wl_data_source *dataSource); static void dndDropPerformedCallback(void *data, wl_data_source *wl_data_source); static void dndFinishedCallback(void *data, wl_data_source *wl_data_source); static void actionCallback(void *data, wl_data_source *wl_data_source, uint32_t dnd_action); static const struct wl_data_source_listener s_listener; DataSource *q; }; const wl_data_source_listener DataSource::Private::s_listener = { targetCallback, sendCallback, cancelledCallback, dndDropPerformedCallback, dndFinishedCallback, actionCallback }; DataSource::Private::Private(DataSource *q) : q(q) { } void DataSource::Private::targetCallback(void *data, wl_data_source *dataSource, const char *mimeType) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->targetAccepts(QString::fromUtf8(mimeType)); } void DataSource::Private::sendCallback(void *data, wl_data_source *dataSource, const char *mimeType, int32_t fd) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->sendDataRequested(QString::fromUtf8(mimeType), fd); } void DataSource::Private::cancelledCallback(void *data, wl_data_source *dataSource) { auto d = reinterpret_cast(data); Q_ASSERT(d->source == dataSource); emit d->q->cancelled(); } void DataSource::Private::dndDropPerformedCallback(void *data, wl_data_source *wl_data_source) { Q_UNUSED(wl_data_source) auto d = reinterpret_cast(data); emit d->q->dragAndDropPerformed(); } void DataSource::Private::dndFinishedCallback(void *data, wl_data_source *wl_data_source) { Q_UNUSED(wl_data_source) auto d = reinterpret_cast(data); emit d->q->dragAndDropFinished(); } void DataSource::Private::actionCallback(void *data, wl_data_source *wl_data_source, uint32_t dnd_action) { Q_UNUSED(wl_data_source) auto d = reinterpret_cast(data); switch(dnd_action) { case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY: d->setAction(DataDeviceManager::DnDAction::Copy); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE: d->setAction(DataDeviceManager::DnDAction::Move); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: d->setAction(DataDeviceManager::DnDAction::Ask); break; case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: d->setAction(DataDeviceManager::DnDAction::None); break; default: Q_UNREACHABLE(); } } void DataSource::Private::setAction(DataDeviceManager::DnDAction action) { if (action == selectedAction) { return; } selectedAction = action; emit q->selectedDragAndDropActionChanged(); } void DataSource::Private::setup(wl_data_source *s) { Q_ASSERT(!source.isValid()); Q_ASSERT(s); source.setup(s); wl_data_source_add_listener(s, &s_listener, this); } DataSource::DataSource(QObject *parent) : QObject(parent) , d(new Private(this)) { } DataSource::~DataSource() { release(); } void DataSource::release() { d->source.release(); } void DataSource::destroy() { d->source.destroy(); } bool DataSource::isValid() const { return d->source.isValid(); } void DataSource::setup(wl_data_source *dataSource) { d->setup(dataSource); } void DataSource::offer(const QString &mimeType) { wl_data_source_offer(d->source, mimeType.toUtf8().constData()); } void DataSource::offer(const QMimeType &mimeType) { if (!mimeType.isValid()) { return; } offer(mimeType.name()); } DataSource::operator wl_data_source*() const { return d->source; } DataSource::operator wl_data_source*() { return d->source; } void DataSource::setDragAndDropActions(DataDeviceManager::DnDActions actions) { uint32_t wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; if (actions.testFlag(DataDeviceManager::DnDAction::Copy)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } if (actions.testFlag(DataDeviceManager::DnDAction::Move)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; } if (actions.testFlag(DataDeviceManager::DnDAction::Ask)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } wl_data_source_set_actions(d->source, wlActions); } DataDeviceManager::DnDAction DataSource::selectedDragAndDropAction() const { return d->selectedAction; } } } diff --git a/src/client/datasource.h b/src/client/datasource.h index d38a885..477b3af 100644 --- a/src/client/datasource.h +++ b/src/client/datasource.h @@ -1,174 +1,160 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_DATASOURCE_H #define WAYLAND_DATASOURCE_H #include "buffer.h" #include "datadevicemanager.h" #include #include struct wl_data_source; class QMimeType; namespace KWayland { namespace Client { /** * @short Wrapper for the wl_data_source interface. * * This class is a convenient wrapper for the wl_data_source interface. * To create a DataSource call DataDeviceManager::createDataSource. * * @see DataDeviceManager **/ class KWAYLANDCLIENT_EXPORT DataSource : public QObject { Q_OBJECT public: explicit DataSource(QObject *parent = nullptr); virtual ~DataSource(); /** * Setup this DataSource to manage the @p dataSource. * When using DataDeviceManager::createDataSource there is no need to call this * method. **/ void setup(wl_data_source *dataSource); /** * Releases the wl_data_source interface. * After the interface has been released the DataSource instance is no * longer valid and can be setup with another wl_data_source interface. **/ void release(); /** * Destroys the data held by this DataSource. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_data_source interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DataSource gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_data_source. **/ bool isValid() const; void offer(const QString &mimeType); void offer(const QMimeType &mimeType); /** * Sets the actions that the source side client supports for this * operation. * * This request must be made once only, and can only be made on sources * used in drag-and-drop, so it must be performed before * @link{DataDevice::startDrag}. Attempting to use the source other than * for drag-and-drop will raise a protocol error. * @since 5.42 **/ void setDragAndDropActions(DataDeviceManager::DnDActions actions); /** * The currently selected drag and drop action by the compositor. * @see selectedDragAndDropActionChanged * @since 5.42 **/ DataDeviceManager::DnDAction selectedDragAndDropAction() const; operator wl_data_source*(); operator wl_data_source*() const; Q_SIGNALS: /** * Emitted when a target accepts pointer_focus or motion events. If * a target does not accept any of the offered types, @p mimeType is empty. **/ void targetAccepts(const QString &mimeType); /** * Request for data from the client. Send the data as the * specified @p mimeType over the passed file descriptor @p fd, then close * it. **/ void sendDataRequested(const QString &mimeType, qint32 fd); /** * This DataSource has been replaced by another DataSource. * The client should clean up and destroy this DataSource. **/ void cancelled(); /** * The drag-and-drop operation physically finished. * * The user performed the drop action. This signal does not * indicate acceptance, @link{cancelled} may still be * emitted afterwards if the drop destination does not accept any * mime type. * * However, this signal might not be received if the * compositor cancelled the drag-and-drop operation before this * signal could happen. * * Note that the DataSource may still be used in the future and * should not be destroyed here. * @since 5.42 **/ void dragAndDropPerformed(); /** * The drag-and-drop operation concluded. * * The drop destination finished interoperating with this DataSource, * so the client is now free to destroy this DataSource. * * If the action used to perform the operation was "move", the * source can now delete the transferred data. * @since 5.42 */ void dragAndDropFinished(); /** * Emitted whenever the selected drag and drop action changes. * @see selectedDragAndDropAction * @since 5.42 **/ void selectedDragAndDropActionChanged(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/dpms.cpp b/src/client/dpms.cpp index 0679923..4e4d3d7 100644 --- a/src/client/dpms.cpp +++ b/src/client/dpms.cpp @@ -1,285 +1,271 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "dpms.h" #include "event_queue.h" #include "output.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN DpmsManager::Private { public: WaylandPointer manager; EventQueue *queue = nullptr; }; DpmsManager::DpmsManager(QObject *parent) : QObject(parent) , d(new Private) { } DpmsManager::~DpmsManager() { release(); } void DpmsManager::release() { d->manager.release(); } void DpmsManager::destroy() { d->manager.destroy(); } bool DpmsManager::isValid() const { return d->manager.isValid(); } void DpmsManager::setup(org_kde_kwin_dpms_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager.isValid()); d->manager.setup(manager); } EventQueue *DpmsManager::eventQueue() { return d->queue; } void DpmsManager::setEventQueue(EventQueue *queue) { d->queue = queue; } Dpms *DpmsManager::getDpms(Output *output, QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(output); Dpms *dpms = new Dpms(output, parent); auto w = org_kde_kwin_dpms_manager_get(d->manager, *output); if (d->queue) { d->queue->addProxy(w); } dpms->setup(w); return dpms; } DpmsManager::operator org_kde_kwin_dpms_manager*() const { return d->manager; } DpmsManager::operator org_kde_kwin_dpms_manager*() { return d->manager; } class Q_DECL_HIDDEN Dpms::Private { public: explicit Private(const QPointer &output, Dpms *q); void setup(org_kde_kwin_dpms *d); WaylandPointer dpms; struct Data { bool supported = false; Mode mode = Mode::On; bool supportedChanged = false; bool modeChanged = false; }; Data current; Data pending; QPointer output; private: static void supportedCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms, uint32_t supported); static void modeCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms, uint32_t mode); static void doneCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms); static const struct org_kde_kwin_dpms_listener s_listener; Dpms *q; }; #ifndef K_DOXYGEN const org_kde_kwin_dpms_listener Dpms::Private::s_listener = { supportedCallback, modeCallback, doneCallback }; #endif void Dpms::Private::supportedCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms, uint32_t supported) { Q_UNUSED(org_kde_kwin_dpms) Private *p = reinterpret_cast(data); p->pending.supported = supported == 0 ? false : true; p->pending.supportedChanged = true; } void Dpms::Private::modeCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms, uint32_t mode) { Q_UNUSED(org_kde_kwin_dpms) Mode m; switch (mode) { case ORG_KDE_KWIN_DPMS_MODE_ON: m = Mode::On; break; case ORG_KDE_KWIN_DPMS_MODE_STANDBY: m = Mode::Standby; break; case ORG_KDE_KWIN_DPMS_MODE_SUSPEND: m = Mode::Suspend; break; case ORG_KDE_KWIN_DPMS_MODE_OFF: m = Mode::Off; break; default: return; } Private *p = reinterpret_cast(data); p->pending.mode = m; p->pending.modeChanged = true; } void Dpms::Private::doneCallback(void *data, org_kde_kwin_dpms *org_kde_kwin_dpms) { Q_UNUSED(org_kde_kwin_dpms) Private *p = reinterpret_cast(data); const bool supportedChanged = p->pending.supportedChanged && p->pending.supported != p->current.supported; const bool modeChanged = p->pending.modeChanged && p->pending.mode != p->current.mode; if (supportedChanged) { p->current.supported = p->pending.supported; emit p->q->supportedChanged(); } if (modeChanged) { p->current.mode = p->pending.mode; emit p->q->modeChanged(); } p->pending = Data(); } Dpms::Private::Private(const QPointer &output, Dpms *q) : output(output) , q(q) { } void Dpms::Private::setup(org_kde_kwin_dpms *d) { Q_ASSERT(d); Q_ASSERT(!dpms.isValid()); dpms.setup(d); org_kde_kwin_dpms_add_listener(dpms, &s_listener, this); } Dpms::Dpms(const QPointer &o, QObject *parent) : QObject(parent) , d(new Private(o, this)) { } Dpms::~Dpms() { release(); } void Dpms::destroy() { d->dpms.destroy(); } void Dpms::release() { d->dpms.release(); } bool Dpms::isValid() const { return d->dpms.isValid(); } void Dpms::setup(org_kde_kwin_dpms *dpms) { d->setup(dpms); } bool Dpms::isSupported() const { return d->current.supported; } Dpms::Mode Dpms::mode() const { return d->current.mode; } void Dpms::requestMode(Dpms::Mode mode) { uint32_t wlMode; switch (mode) { case Mode::On: wlMode = ORG_KDE_KWIN_DPMS_MODE_ON; break; case Mode::Standby: wlMode = ORG_KDE_KWIN_DPMS_MODE_STANDBY; break; case Mode::Suspend: wlMode = ORG_KDE_KWIN_DPMS_MODE_SUSPEND; break; case Mode::Off: wlMode = ORG_KDE_KWIN_DPMS_MODE_OFF; break; default: Q_UNREACHABLE(); } org_kde_kwin_dpms_set(d->dpms, wlMode); } QPointer< Output > Dpms::output() const { return d->output; } Dpms::operator org_kde_kwin_dpms*() { return d->dpms; } Dpms::operator org_kde_kwin_dpms*() const { return d->dpms; } } } diff --git a/src/client/dpms.h b/src/client/dpms.h index 412afab..77d701c 100644 --- a/src/client/dpms.h +++ b/src/client/dpms.h @@ -1,247 +1,233 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_DPMS_H #define KWAYLAND_CLIENT_DPMS_H #include #include struct org_kde_kwin_dpms; struct org_kde_kwin_dpms_manager; namespace KWayland { namespace Client { class EventQueue; class Dpms; class Output; /** * @short This class is a factory for Dpms instances. * * It is a convenience wrapper for the org_kde_kwin_dpms_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the DpmsManager interface: * @code * DpmsManager *m = registry->createDpmsManager(name, version); * @endcode * * This creates the DpmsManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * DpmsManager *m = new DpmsManager; * m->setup(registry->bindDpmsManager(name, version)); * @endcode * * The DpmsManager can be used as a drop-in replacement for any org_kde_kwin_dpms_manager * pointer as it provides matching cast operators. * * @see Registry, Dpms * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT DpmsManager : public QObject { Q_OBJECT public: /** * Creates a new DpmsManager. * Note: after constructing the DpmsManager it is not yet valid and one needs * to call setup. In order to get a ready to use DpmsManager prefer using * Registry::createDpmsManager. **/ explicit DpmsManager(QObject *parent = nullptr); virtual ~DpmsManager(); /** * @returns @c true if managing a org_kde_kwin_dpms_manager. **/ bool isValid() const; /** * Setup this DpmsManager to manage the @p manager. * When using Registry::createDpmsManager there is no need to call this * method. **/ void setup(org_kde_kwin_dpms_manager *manager); /** * Releases the org_kde_kwin_dpms_manager interface. * After the interface has been released the DpmsManager instance is no * longer valid and can be setup with another org_kde_kwin_dpms_manager interface. **/ void release(); /** * Destroys the data held by this DpmsManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_dpms_manager interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * DPMS gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Dpms. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Dpms. **/ EventQueue *eventQueue(); Dpms *getDpms(Output *output, QObject *parent = nullptr); operator org_kde_kwin_dpms_manager*(); operator org_kde_kwin_dpms_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the DpmsManager got created by * Registry::createDpmsManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Power management for monitors. * * Display Power Management Signaling allows power management for monitors. * This class is a convenient wrapper for the org_kde_kwin_dpms interface. * To create a Dpms call DpmsManager::getDpms. * * @see DpmsManager **/ class KWAYLANDCLIENT_EXPORT Dpms : public QObject { Q_OBJECT public: virtual ~Dpms(); enum class Mode { On, Standby, Suspend, Off }; /** * Setup this Dpms to manage the @p dpms. * When using DpmsManager::createDpms there is no need to call this * method. **/ void setup(org_kde_kwin_dpms *dpms); /** * Releases the org_kde_kwin_dpms interface. * After the interface has been released the Dpms instance is no * longer valid and can be setup with another org_kde_kwin_dpms interface. **/ void release(); /** * Destroys the data held by this Dpms. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_dpms interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &Dpms::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_dpms. **/ bool isValid() const; /** * @returns the Output for which this Dpms got created **/ QPointer output() const; /** * Whether Dpms is supported for the Output. * Initially set to @c false. * @returns whether Dpms is supported for the Output. * @see supportedChanged **/ bool isSupported() const; /** * The current Dpms mode. * Initially set to @c Mode::On. * @returns the current Dpms mode of the Output * @see modeChanged **/ Mode mode() const; /** * Request to change the Output into Dpms @p mode. * The Wayland compositor is not obliged to honor the request. * If the mode changes the client is notified and @link modeChanged @endlink gets emitted. * @param mode The requested Dpms mode. **/ void requestMode(Mode mode); operator org_kde_kwin_dpms*(); operator org_kde_kwin_dpms*() const; Q_SIGNALS: /** * Emitted if the supported state on the Output changes. * @see isSupported **/ void supportedChanged(); /** * Emitted if the Dpms mode on the Output changes. * @see mode **/ void modeChanged(); private: friend class DpmsManager; explicit Dpms(const QPointer &o, QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Dpms::Mode) #endif diff --git a/src/client/event_queue.cpp b/src/client/event_queue.cpp index 1e421d1..9921682 100644 --- a/src/client/event_queue.cpp +++ b/src/client/event_queue.cpp @@ -1,117 +1,103 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "event_queue.h" #include "connection_thread.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN EventQueue::Private { public: Private(EventQueue *q); wl_display *display = nullptr; WaylandPointer queue; private: EventQueue *q; }; EventQueue::Private::Private(EventQueue *q) : q(q) { } EventQueue::EventQueue(QObject *parent) : QObject(parent) , d(new Private(this)) { } EventQueue::~EventQueue() { release(); } void EventQueue::release() { d->queue.release(); d->display = nullptr; } void EventQueue::destroy() { d->queue.destroy(); d->display = nullptr; } bool EventQueue::isValid() { return d->queue.isValid(); } void EventQueue::setup(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!d->display); Q_ASSERT(!d->queue); d->display = display; d->queue.setup(wl_display_create_queue(display)); } void EventQueue::setup(ConnectionThread *connection) { setup(connection->display()); connect(connection, &ConnectionThread::eventsRead, this, &EventQueue::dispatch, Qt::QueuedConnection); } void EventQueue::dispatch() { if (!d->display || !d->queue) { return; } wl_display_dispatch_queue_pending(d->display, d->queue); wl_display_flush(d->display); } void EventQueue::addProxy(wl_proxy *proxy) { Q_ASSERT(d->queue); wl_proxy_set_queue(proxy, d->queue); } EventQueue::operator wl_event_queue*() const { return d->queue; } EventQueue::operator wl_event_queue*() { return d->queue; } } } diff --git a/src/client/event_queue.h b/src/client/event_queue.h index cc23b5f..3db36bf 100644 --- a/src/client/event_queue.h +++ b/src/client/event_queue.h @@ -1,164 +1,150 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_EVENT_QUEUE_H #define WAYLAND_EVENT_QUEUE_H #include #include struct wl_display; struct wl_proxy; struct wl_event_queue; namespace KWayland { namespace Client { class ConnectionThread; /** * @short Wrapper class for wl_event_queue interface. * * The EventQueue is needed if a different thread is used for the connection. * If the interface wrappers are held in a different thread than the connection thread * an EventQueue is needed for the thread which holds the interface wrappers. A common * example is a dedicated connection thread while the interface wrappers are created * in the main thread. * * All interface wrappers are set up to support the EventQueue in the most convenient * way. The EventQueue needs only to be passed to the Registry. The EventQueue will then * be passed to all created wrappers through the tree. * * @code * ConnectionThread connection; * EventQueue queue; * Registry registry; * * connect(&connection, &ConnectionThread::connected, this, [&] { * queue.setup(&connection); * registry.setEventQueue(&queue); * registry.setup(&connection); * registry.create(); * }); * * connection.initConnection(); * @endcode * * The EventQueue can be used as a drop-in replacement for any wl_event_queue * pointer as it provides matching cast operators. **/ class KWAYLANDCLIENT_EXPORT EventQueue : public QObject { Q_OBJECT public: explicit EventQueue(QObject *parent = nullptr); virtual ~EventQueue(); /** * Creates the event queue for the @p display. * * Note: this will not automatically setup the dispatcher. * When using this method one needs to ensure that dispatch * gets invoked whenever new events need to be dispatched. * @see dispatch **/ void setup(wl_display *display); /** * Creates the event queue for the @p connection. * * This method also connects the eventsRead signal of the ConnectionThread * to the dispatch method. Events will be automatically dispatched without * the need to call dispatch manually. * @see dispatch **/ void setup(ConnectionThread *connection); /** * @returns @c true if EventQueue is setup. **/ bool isValid(); /** * Releases the wl_event_queue interface. * After the interface has been released the EventQueue instance is no * longer valid and can be setup with another wl_event_queue interface. **/ void release(); /** * Destroys the data held by this EventQueue. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_event_queue interface * once there is a new connection available. * * @see release **/ void destroy(); /** * Adds the @p proxy to the EventQueue. **/ void addProxy(wl_proxy *proxy); /** * Adds the @p proxy of type wl_interface (e.g. wl_compositor) to the EventQueue. **/ template void addProxy(wl_interface *proxy); /** * Adds the @p proxy wrapper class of type T referencing the wl_interface to the EventQueue. **/ template void addProxy(T *proxy); operator wl_event_queue*(); operator wl_event_queue*() const; public Q_SLOTS: /** * Dispatches all pending events on the EventQueue. **/ void dispatch(); private: class Private; QScopedPointer d; }; template inline void EventQueue::addProxy(wl_interface *proxy) { addProxy(reinterpret_cast(proxy)); } template inline void EventQueue::addProxy(T *proxy) { addProxy(reinterpret_cast((wl_interface*)*(proxy))); } } } #endif diff --git a/src/client/fakeinput.cpp b/src/client/fakeinput.cpp index 8673540..d447f2b 100644 --- a/src/client/fakeinput.cpp +++ b/src/client/fakeinput.cpp @@ -1,251 +1,237 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "fakeinput.h" #include "event_queue.h" #include "seat.h" #include "wayland_pointer_p.h" #include #include #include #if HAVE_LINUX_INPUT_H #include #endif #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN FakeInput::Private { public: WaylandPointer manager; EventQueue *queue = nullptr; void sendPointerButtonState(Qt::MouseButton button, quint32 state); }; FakeInput::FakeInput(QObject *parent) : QObject(parent) , d(new Private) { } FakeInput::~FakeInput() { release(); } void FakeInput::release() { d->manager.release(); } void FakeInput::destroy() { d->manager.destroy(); } bool FakeInput::isValid() const { return d->manager.isValid(); } void FakeInput::setup(org_kde_kwin_fake_input *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager.isValid()); d->manager.setup(manager); } EventQueue *FakeInput::eventQueue() { return d->queue; } void FakeInput::setEventQueue(EventQueue *queue) { d->queue = queue; } void FakeInput::authenticate(const QString &applicationName, const QString &reason) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_authenticate(d->manager, applicationName.toUtf8().constData(), reason.toUtf8().constData()); } void FakeInput::requestPointerMove(const QSizeF &delta) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_pointer_motion(d->manager, wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height())); } void FakeInput::requestPointerMoveAbsolute(const QPointF &pos) { Q_ASSERT(d->manager.isValid()); if (wl_proxy_get_version(d->manager) < ORG_KDE_KWIN_FAKE_INPUT_POINTER_MOTION_ABSOLUTE_SINCE_VERSION) { return; } org_kde_kwin_fake_input_pointer_motion_absolute(d->manager, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } void FakeInput::Private::sendPointerButtonState(Qt::MouseButton button, quint32 state) { #if HAVE_LINUX_INPUT_H Q_ASSERT(manager.isValid()); uint32_t b = 0; switch (button) { case Qt::LeftButton: b = BTN_LEFT; break; case Qt::RightButton: b = BTN_RIGHT; break; case Qt::MiddleButton: b = BTN_MIDDLE; break; default: // TODO: more buttons, check implementation in QtWayland // unsupported button return; } org_kde_kwin_fake_input_button(manager, b, state); #endif } void FakeInput::requestPointerButtonPress(Qt::MouseButton button) { d->sendPointerButtonState(button, WL_POINTER_BUTTON_STATE_PRESSED); } void FakeInput::requestPointerButtonPress(quint32 linuxButton) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_button(d->manager, linuxButton, WL_POINTER_BUTTON_STATE_PRESSED); } void FakeInput::requestPointerButtonRelease(Qt::MouseButton button) { d->sendPointerButtonState(button, WL_POINTER_BUTTON_STATE_RELEASED); } void FakeInput::requestPointerButtonRelease(quint32 linuxButton) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_button(d->manager, linuxButton, WL_POINTER_BUTTON_STATE_RELEASED); } void FakeInput::requestPointerButtonClick(Qt::MouseButton button) { requestPointerButtonPress(button); requestPointerButtonRelease(button); } void FakeInput::requestPointerButtonClick(quint32 linuxButton) { requestPointerButtonPress(linuxButton); requestPointerButtonRelease(linuxButton); } void FakeInput::requestPointerAxis(Qt::Orientation axis, qreal delta) { Q_ASSERT(d->manager.isValid()); uint32_t a; switch (axis) { case Qt::Horizontal: a = WL_POINTER_AXIS_HORIZONTAL_SCROLL; break; case Qt::Vertical: a = WL_POINTER_AXIS_VERTICAL_SCROLL; break; default: Q_UNREACHABLE(); break; } org_kde_kwin_fake_input_axis(d->manager, a, wl_fixed_from_double(delta)); } void FakeInput::requestTouchDown(quint32 id, const QPointF &pos) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_touch_down(d->manager, id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } void FakeInput::requestTouchMotion(quint32 id, const QPointF &pos) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_touch_motion(d->manager, id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } void FakeInput::requestTouchUp(quint32 id) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_touch_up(d->manager, id); } void FakeInput::requestTouchCancel() { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_touch_cancel(d->manager); } void FakeInput::requestTouchFrame() { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_touch_frame(d->manager); } void FakeInput::requestKeyboardKeyPress(quint32 linuxKey) { Q_ASSERT(d->manager.isValid()); if (wl_proxy_get_version(d->manager) < ORG_KDE_KWIN_FAKE_INPUT_KEYBOARD_KEY_SINCE_VERSION) { return; } org_kde_kwin_fake_input_keyboard_key(d->manager, linuxKey, WL_KEYBOARD_KEY_STATE_PRESSED); } void FakeInput::requestKeyboardKeyRelease(quint32 linuxKey) { Q_ASSERT(d->manager.isValid()); if (wl_proxy_get_version(d->manager) < ORG_KDE_KWIN_FAKE_INPUT_KEYBOARD_KEY_SINCE_VERSION) { return; } org_kde_kwin_fake_input_keyboard_key(d->manager, linuxKey, WL_KEYBOARD_KEY_STATE_RELEASED); } FakeInput::operator org_kde_kwin_fake_input*() const { return d->manager; } FakeInput::operator org_kde_kwin_fake_input*() { return d->manager; } } } diff --git a/src/client/fakeinput.h b/src/client/fakeinput.h index bf8c4a6..93ff46a 100644 --- a/src/client/fakeinput.h +++ b/src/client/fakeinput.h @@ -1,250 +1,236 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_FAKEINPUT_H #define KWAYLAND_FAKEINPUT_H #include #include struct org_kde_kwin_fake_input; namespace KWayland { namespace Client { class EventQueue; class FakeInputTimeout; class Seat; /** * @short Wrapper for the org_kde_kwin_fake_input interface. * * FakeInput allows to fake input events into the Wayland server. This is a privileged * Wayland interface and the Wayland server is allowed to ignore all events. * * This class provides a convenient wrapper for the org_kde_kwin_fake_input interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the FakeInput interface: * @code * FakeInput *m = registry->createFakeInput(name, version); * @endcode * * This creates the FakeInput and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * FakeInput *m = new FakeInput; * m->setup(registry->bindFakeInput(name, version)); * @endcode * * The FakeInput can be used as a drop-in replacement for any org_kde_kwin_fake_input * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT FakeInput : public QObject { Q_OBJECT public: /** * Creates a new FakeInput. * Note: after constructing the FakeInput it is not yet valid and one needs * to call setup. In order to get a ready to use FakeInput prefer using * Registry::createFakeInput. **/ explicit FakeInput(QObject *parent = nullptr); virtual ~FakeInput(); /** * @returns @c true if managing a org_kde_kwin_fake_input. **/ bool isValid() const; /** * Setup this FakeInput to manage the @p manager. * When using Registry::createFakeInput there is no need to call this * method. **/ void setup(org_kde_kwin_fake_input *manager); /** * Releases the org_kde_kwin_fake_input interface. * After the interface has been released the FakeInput instance is no * longer valid and can be setup with another org_kde_kwin_fake_input interface. **/ void release(); /** * Destroys the data held by this FakeInput. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_fake_input interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * FakeInput gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * Authenticate with the Wayland server in order to request sending fake input events. * The Wayland server might ignore all requests without a prior authentication. * * The Wayland server might use the provided @p applicationName and @p reason to ask * the user whether this request should get authenticated. * * There is no way for the client to figure out whether the authentication was granted * or denied. The client should assume that it wasn't granted. * * @param applicationName A human readable description of the application * @param reason A human readable explanation why this application wants to send fake requests **/ void authenticate(const QString &applicationName, const QString &reason); /** * Request a relative pointer motion of @p delta pixels. **/ void requestPointerMove(const QSizeF &delta); /** * Request an absolute pointer motion to @p pos position. * * @since 5.54 **/ void requestPointerMoveAbsolute(const QPointF &pos); /** * Convenience overload. * @see requestPointerButtonPress(quint32) **/ void requestPointerButtonPress(Qt::MouseButton button); /** * Request a pointer button press. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonPress(quint32 linuxButton); /** * Convenience overload. * @see requestPointerButtonRelease(quint32) **/ void requestPointerButtonRelease(Qt::MouseButton button); /** * Request a pointer button release. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonRelease(quint32 linuxButton); /** * Convenience overload. * @see requestPointerButtonClick(quint32) **/ void requestPointerButtonClick(Qt::MouseButton button); /** * Requests a pointer button click, that is a press directly followed by a release. * @param linuxButton The button code as defined in linux/input-event-codes.h **/ void requestPointerButtonClick(quint32 linuxButton); /** * Request a scroll of the pointer @p axis with @p delta. **/ void requestPointerAxis(Qt::Orientation axis, qreal delta); /** * Request a touch down at @p pos in global coordinates. * * If this is the first touch down it starts a touch sequence. * @param id The id to identify the touch point * @param pos The global position of the touch point * @see requestTouchMotion * @see requestTouchUp * @since 5.23 **/ void requestTouchDown(quint32 id, const QPointF &pos); /** * Request a move of the touch point identified by @p id to new global @p pos. * @param id The id to identify the touch point * @param pos The global position of the touch point * @see requestTouchDown * @since 5.23 **/ void requestTouchMotion(quint32 id, const QPointF &pos); /** * Requests a touch up of the touch point identified by @p id. * @param id The id to identify the touch point * @since 5.23 **/ void requestTouchUp(quint32 id); /** * Requests to cancel the current touch event sequence. * @since 5.23 **/ void requestTouchCancel(); /** * Requests a touch frame. This allows to manipulate multiple touch points in one * event and notify that the set of touch events for the current frame are finished. * @since 5.23 **/ void requestTouchFrame(); /** * Request a keyboard key press. * @param linuxButton The button code as defined in linux/input-event-codes.h * * @since 5.63 **/ void requestKeyboardKeyPress(quint32 linuxKey); /** * Request a keyboard button release. * @param linuxButton The button code as defined in linux/input-event-codes.h * * @since 5.63 **/ void requestKeyboardKeyRelease(quint32 linuxKey); operator org_kde_kwin_fake_input*(); operator org_kde_kwin_fake_input*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createFakeInput * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/fullscreen_shell.cpp b/src/client/fullscreen_shell.cpp index 8d4d8f2..57bf62c 100644 --- a/src/client/fullscreen_shell.cpp +++ b/src/client/fullscreen_shell.cpp @@ -1,154 +1,140 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "fullscreen_shell.h" #include "surface.h" #include "output.h" #include "wayland_pointer_p.h" #include // wayland #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN FullscreenShell::Private { public: Private(FullscreenShell *q); void setup(_wl_fullscreen_shell *shell); WaylandPointer<_wl_fullscreen_shell, _wl_fullscreen_shell_release> shell; EventQueue *queue = nullptr; bool capabilityArbitraryModes = false; bool capabilityCursorPlane = false; private: void handleCapabilities(uint32_t capability); static void capabilitiesAnnounce(void *data, struct _wl_fullscreen_shell *shell, uint32_t capability); static _wl_fullscreen_shell_listener s_fullscreenShellListener; FullscreenShell *q; }; _wl_fullscreen_shell_listener FullscreenShell::Private::s_fullscreenShellListener = { FullscreenShell::Private::capabilitiesAnnounce }; FullscreenShell::Private::Private(FullscreenShell *q) : q(q) { } void FullscreenShell::Private::setup(_wl_fullscreen_shell *s) { Q_ASSERT(!shell); Q_ASSERT(s); shell.setup(s); _wl_fullscreen_shell_add_listener(shell, &s_fullscreenShellListener, this); } void FullscreenShell::Private::capabilitiesAnnounce(void *data, _wl_fullscreen_shell *shell, uint32_t capability) { auto s = reinterpret_cast(data); Q_ASSERT(shell == s->shell); s->handleCapabilities(capability); } void FullscreenShell::Private::handleCapabilities(uint32_t capability) { if (capability & _WL_FULLSCREEN_SHELL_CAPABILITY_ARBITRARY_MODES) { capabilityArbitraryModes = true; emit q->capabilityArbitraryModesChanged(capabilityArbitraryModes); } if (capability & _WL_FULLSCREEN_SHELL_CAPABILITY_CURSOR_PLANE) { capabilityCursorPlane = true; emit q->capabilityCursorPlaneChanged(capabilityCursorPlane); } } FullscreenShell::FullscreenShell(QObject *parent) : QObject(parent) , d(new Private(this)) { } FullscreenShell::~FullscreenShell() { release(); } void FullscreenShell::release() { d->shell.release(); } void FullscreenShell::destroy() { d->shell.destroy(); } void FullscreenShell::setup(_wl_fullscreen_shell *shell) { d->setup(shell); } EventQueue *FullscreenShell::eventQueue() const { return d->queue; } void FullscreenShell::setEventQueue(EventQueue *queue) { d->queue = queue; } void FullscreenShell::present(wl_surface *surface, wl_output *output) { Q_ASSERT(d->shell); _wl_fullscreen_shell_present_surface(d->shell, surface, _WL_FULLSCREEN_SHELL_PRESENT_METHOD_DEFAULT, output); } void FullscreenShell::present(Surface *surface, Output *output) { Q_ASSERT(surface); Q_ASSERT(output); present(*surface, *output); } bool FullscreenShell::isValid() const { return d->shell.isValid(); } bool FullscreenShell::hasCapabilityArbitraryModes() const { return d->capabilityArbitraryModes; } bool FullscreenShell::hasCapabilityCursorPlane() const { return d->capabilityCursorPlane; } } } diff --git a/src/client/fullscreen_shell.h b/src/client/fullscreen_shell.h index b44f3fd..5163be7 100644 --- a/src/client/fullscreen_shell.h +++ b/src/client/fullscreen_shell.h @@ -1,112 +1,98 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_FULLSCREEN_SHELL_H #define WAYLAND_FULLSCREEN_SHELL_H #include #include struct _wl_fullscreen_shell; struct wl_output; struct wl_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class Output; /** * @short Wrapper for the _wl_fullscreen_shell interface. * * This class provides a convenient wrapper for the _wl_fullscreen_shell interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the FullscreenShell interface: * @code * FullscreenShell *f = registry->createFullscreenShell(name, version); * @endcode * * This creates the FullscreenShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * FullscreenShell *f = new FullscreenShell; * f->setup(registry->bindFullscreenShell(name, version)); * @endcode * * The FullscreenShell can be used as a drop-in replacement for any _wl_fullscreen_shell * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT FullscreenShell : public QObject { Q_OBJECT Q_PROPERTY(bool capabilityArbitraryModes READ hasCapabilityArbitraryModes NOTIFY capabilityArbitraryModesChanged) Q_PROPERTY(bool capabilityCursorPlane READ hasCapabilityCursorPlane NOTIFY capabilityCursorPlaneChanged) public: explicit FullscreenShell(QObject *parent = nullptr); virtual ~FullscreenShell(); bool isValid() const; void release(); void destroy(); bool hasCapabilityArbitraryModes() const; bool hasCapabilityCursorPlane() const; void setup(_wl_fullscreen_shell *shell); void present(wl_surface *surface, wl_output *output); void present(Surface *surface, Output *output); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; Q_SIGNALS: void capabilityArbitraryModesChanged(bool); void capabilityCursorPlaneChanged(bool); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createFullscreenShell * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/idle.cpp b/src/client/idle.cpp index 006c69e..75724ff 100644 --- a/src/client/idle.cpp +++ b/src/client/idle.cpp @@ -1,199 +1,185 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "idle.h" #include "event_queue.h" #include "seat.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Idle::Private { public: WaylandPointer manager; EventQueue *queue = nullptr; }; Idle::Idle(QObject *parent) : QObject(parent) , d(new Private) { } Idle::~Idle() { release(); } void Idle::release() { d->manager.release(); } void Idle::destroy() { d->manager.destroy(); } bool Idle::isValid() const { return d->manager.isValid(); } void Idle::setup(org_kde_kwin_idle *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager.isValid()); d->manager.setup(manager); } EventQueue *Idle::eventQueue() { return d->queue; } void Idle::setEventQueue(EventQueue *queue) { d->queue = queue; } IdleTimeout *Idle::getTimeout(quint32 msecs, Seat *seat, QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(seat); IdleTimeout *idle = new IdleTimeout(parent); auto i = org_kde_kwin_idle_get_idle_timeout(d->manager, *seat, msecs); if (d->queue) { d->queue->addProxy(i); } idle->setup(i); return idle; } Idle::operator org_kde_kwin_idle*() const { return d->manager; } Idle::operator org_kde_kwin_idle*() { return d->manager; } class Q_DECL_HIDDEN IdleTimeout::Private { public: explicit Private(IdleTimeout *q); void setup(org_kde_kwin_idle_timeout *d); WaylandPointer timeout; private: static void idleCallback(void *data, org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout); static void resumedCallback(void *data, org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout); static const struct org_kde_kwin_idle_timeout_listener s_listener; IdleTimeout *q; }; const org_kde_kwin_idle_timeout_listener IdleTimeout::Private::s_listener = { idleCallback, resumedCallback }; void IdleTimeout::Private::idleCallback(void *data, org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) { Q_UNUSED(org_kde_kwin_idle_timeout) emit reinterpret_cast(data)->q->idle(); } void IdleTimeout::Private::resumedCallback(void *data, org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) { Q_UNUSED(org_kde_kwin_idle_timeout) emit reinterpret_cast(data)->q->resumeFromIdle(); } IdleTimeout::Private::Private(IdleTimeout *q) : q(q) { } void IdleTimeout::Private::setup(org_kde_kwin_idle_timeout *d) { Q_ASSERT(d); Q_ASSERT(!timeout.isValid()); timeout.setup(d); org_kde_kwin_idle_timeout_add_listener(timeout, &s_listener, this); } IdleTimeout::IdleTimeout(QObject *parent) : QObject(parent) , d(new Private(this)) { } IdleTimeout::~IdleTimeout() { release(); } void IdleTimeout::destroy() { d->timeout.destroy(); } void IdleTimeout::release() { d->timeout.release(); } bool IdleTimeout::isValid() const { return d->timeout.isValid(); } void IdleTimeout::setup(org_kde_kwin_idle_timeout *dataDevice) { d->setup(dataDevice); } void IdleTimeout::simulateUserActivity() { Q_ASSERT(isValid()); org_kde_kwin_idle_timeout_simulate_user_activity(d->timeout); } IdleTimeout::operator org_kde_kwin_idle_timeout*() { return d->timeout; } IdleTimeout::operator org_kde_kwin_idle_timeout*() const { return d->timeout; } } } diff --git a/src/client/idle.h b/src/client/idle.h index c339240..5616891 100644 --- a/src/client/idle.h +++ b/src/client/idle.h @@ -1,239 +1,225 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_IDLE_H #define KWAYLAND_IDLE_H #include #include struct org_kde_kwin_idle; struct org_kde_kwin_idle_timeout; namespace KWayland { namespace Client { class EventQueue; class IdleTimeout; class Seat; /** * @short Wrapper for the org_kde_kwin_idle interface. * * With the help of Idle it is possible to get notified when a Seat is not being * used. E.g. a chat application which wants to set the user automatically to away * if the user did not interact with the Seat for 5 minutes can create an IdleTimeout * to get notified when the Seat has been idle for the given amount of time. * * This class provides a convenient wrapper for the org_kde_kwin_idle interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Idle interface: * @code * Idle *m = registry->createIdle(name, version); * @endcode * * This creates the Idle and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Idle *m = new Idle; * m->setup(registry->bindIdle(name, version)); * @endcode * * The Idle can be used as a drop-in replacement for any org_kde_kwin_idle * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT Idle : public QObject { Q_OBJECT public: /** * Creates a new Idle. * Note: after constructing the Idle it is not yet valid and one needs * to call setup. In order to get a ready to use Idle prefer using * Registry::createIdle. **/ explicit Idle(QObject *parent = nullptr); virtual ~Idle(); /** * @returns @c true if managing a org_kde_kwin_idle. **/ bool isValid() const; /** * Setup this Idle to manage the @p manager. * When using Registry::createIdle there is no need to call this * method. **/ void setup(org_kde_kwin_idle *manager); /** * Releases the org_kde_kwin_idle interface. * After the interface has been released the Idle instance is no * longer valid and can be setup with another org_kde_kwin_idle interface. **/ void release(); /** * Destroys the data held by this Idle. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_idle interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Idle gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a IdleTimeout. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a IdleTimeout. **/ EventQueue *eventQueue(); /** * Creates a new IdleTimeout for the @p seat. If the @p seat has been idle, * that is none of the connected input devices got used for @p msec, the * IdleTimeout will emit the {@link IdleTimeout::idle} signal. * * It is not guaranteed that the signal will be emitted exactly at the given * timeout. A Wayland server might for example have a minimum timeout which is * larger than @p msec. * * @param msec The duration in milliseconds after which an idle timeout should fire * @param seat The Seat on which the user activity should be monitored. **/ IdleTimeout *getTimeout(quint32 msecs, Seat *seat, QObject *parent = nullptr); operator org_kde_kwin_idle*(); operator org_kde_kwin_idle*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createIdle * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_idle_timeout interface. * * This class is a convenient wrapper for the org_kde_kwin_idle_timeout interface. * To create a IdleTimeout call IdleTimeoutManager::getIdleTimeout. * * @see IdleTimeoutManager **/ class KWAYLANDCLIENT_EXPORT IdleTimeout : public QObject { Q_OBJECT public: /** * To create an IdleTimeout prefer using {@link Idle::getTimeout} which sets up the * IdleTimeout to be fully functional. **/ explicit IdleTimeout(QObject *parent = nullptr); virtual ~IdleTimeout(); /** * Setup this IdleTimeout to manage the @p timeout. * When using IdleTimeoutManager::createIdleTimeout there is no need to call this * method. **/ void setup(org_kde_kwin_idle_timeout *timeout); /** * Releases the org_kde_kwin_idle_timeout interface. * After the interface has been released the IdleTimeout instance is no * longer valid and can be setup with another org_kde_kwin_idle_timeout interface. **/ void release(); /** * Destroys the data held by this IdleTimeout. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_idle_timeout interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &IdleTimeout::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_idle_timeout. **/ bool isValid() const; operator org_kde_kwin_idle_timeout*(); operator org_kde_kwin_idle_timeout*() const; /** * Simulates user activity. If the IdleTimeout is in idle state this will trigger the * {@link resumeFromIdle} signal. The current idle duration is reset, so the {@link idle} * will only be emitted after a complete idle duration as requested for this IdleTimeout. */ void simulateUserActivity(); Q_SIGNALS: /** * Emitted when this IdleTimeout triggered. This means the system has been idle for * the duration specified when creating the IdleTimeout. * @see Idle::getTimeout. * @see resumeFromIdle **/ void idle(); /** * Emitted when the system shows activity again after the idle state was reached. * @see idle **/ void resumeFromIdle(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/idleinhibit.cpp b/src/client/idleinhibit.cpp index ae21320..36f30ec 100644 --- a/src/client/idleinhibit.cpp +++ b/src/client/idleinhibit.cpp @@ -1,176 +1,162 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "idleinhibit.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN IdleInhibitManager::Private { public: Private() = default; void setup(zwp_idle_inhibit_manager_v1 *arg); WaylandPointer idleinhibitmanager; EventQueue *queue = nullptr; }; IdleInhibitManager::IdleInhibitManager(QObject *parent) : QObject(parent) , d(new Private) { } void IdleInhibitManager::Private::setup(zwp_idle_inhibit_manager_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!idleinhibitmanager); idleinhibitmanager.setup(arg); } IdleInhibitManager::~IdleInhibitManager() { release(); } void IdleInhibitManager::setup(zwp_idle_inhibit_manager_v1 *idleinhibitmanager) { d->setup(idleinhibitmanager); } void IdleInhibitManager::release() { d->idleinhibitmanager.release(); } void IdleInhibitManager::destroy() { d->idleinhibitmanager.destroy(); } IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() { return d->idleinhibitmanager; } IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() const { return d->idleinhibitmanager; } bool IdleInhibitManager::isValid() const { return d->idleinhibitmanager.isValid(); } void IdleInhibitManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *IdleInhibitManager::eventQueue() { return d->queue; } IdleInhibitor *IdleInhibitManager::createInhibitor(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new IdleInhibitor(parent); auto w = zwp_idle_inhibit_manager_v1_create_inhibitor(d->idleinhibitmanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN IdleInhibitor::Private { public: Private(IdleInhibitor *q); void setup(zwp_idle_inhibitor_v1 *arg); WaylandPointer idleinhibitor; private: IdleInhibitor *q; }; IdleInhibitor::Private::Private(IdleInhibitor *q) : q(q) { } IdleInhibitor::IdleInhibitor(QObject *parent) : QObject(parent) , d(new Private(this)) { } void IdleInhibitor::Private::setup(zwp_idle_inhibitor_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!idleinhibitor); idleinhibitor.setup(arg); } IdleInhibitor::~IdleInhibitor() { release(); } void IdleInhibitor::setup(zwp_idle_inhibitor_v1 *idleinhibitor) { d->setup(idleinhibitor); } void IdleInhibitor::release() { d->idleinhibitor.release(); } void IdleInhibitor::destroy() { d->idleinhibitor.destroy(); } IdleInhibitor::operator zwp_idle_inhibitor_v1*() { return d->idleinhibitor; } IdleInhibitor::operator zwp_idle_inhibitor_v1*() const { return d->idleinhibitor; } bool IdleInhibitor::isValid() const { return d->idleinhibitor.isValid(); } } } diff --git a/src/client/idleinhibit.h b/src/client/idleinhibit.h index f538a13..fc7a103 100644 --- a/src/client/idleinhibit.h +++ b/src/client/idleinhibit.h @@ -1,217 +1,203 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_IDLEINHIBIT_H #define KWAYLAND_CLIENT_IDLEINHIBIT_H #include #include struct zwp_idle_inhibit_manager_v1; struct zwp_idle_inhibitor_v1; namespace KWayland { namespace Client { class EventQueue; class Surface; class IdleInhibitor; /** * @short Wrapper for the zwp_idle_inhibit_manager_v1 interface. * * This class provides a convenient wrapper for the zwp_idle_inhibit_manager_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the IdleInhibitManager interface: * @code * IdleInhibitManager *c = registry->createIdleInhibitManager(name, version); * @endcode * * This creates the IdleInhibitManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * IdleInhibitManager *c = new IdleInhibitManager; * c->setup(registry->bindIdleInhibitManager(name, version)); * @endcode * * The IdleInhibitManager can be used as a drop-in replacement for any zwp_idle_inhibit_manager_v1 * pointer as it provides matching cast operators. * * @see Registry * @since 5.41 **/ class KWAYLANDCLIENT_EXPORT IdleInhibitManager : public QObject { Q_OBJECT public: /** * Creates a new IdleInhibitManager. * Note: after constructing the IdleInhibitManager it is not yet valid and one needs * to call setup. In order to get a ready to use IdleInhibitManager prefer using * Registry::createIdleInhibitManager. **/ explicit IdleInhibitManager(QObject *parent = nullptr); virtual ~IdleInhibitManager(); /** * Setup this IdleInhibitManager to manage the @p idleinhibitmanager. * When using Registry::createIdleInhibitManager there is no need to call this * method. **/ void setup(zwp_idle_inhibit_manager_v1 *idleinhibitmanager); /** * @returns @c true if managing a zwp_idle_inhibit_manager_v1. **/ bool isValid() const; /** * Releases the zwp_idle_inhibit_manager_v1 interface. * After the interface has been released the IdleInhibitManager instance is no * longer valid and can be setup with another zwp_idle_inhibit_manager_v1 interface. **/ void release(); /** * Destroys the data held by this IdleInhibitManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_idle_inhibit_manager_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, idleinhibitmanager, &IdleInhibitManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this IdleInhibitManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this IdleInhibitManager. **/ EventQueue *eventQueue(); /** * Creates an IdleInhibitor for the given @p surface. * While the IdleInhibitor exists the @p surface is marked to inhibit idle. * @param surface The Surface which should have idle inhibited * @param parent The parent object for the IdleInhibitor * @returns The created IdleInhibitor **/ IdleInhibitor *createInhibitor(Surface *surface, QObject *parent = nullptr); operator zwp_idle_inhibit_manager_v1*(); operator zwp_idle_inhibit_manager_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the IdleInhibitManager got created by * Registry::createIdleInhibitManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * An IdleInhibitor prevents the Output that the associated Surface is visible on from being * set to a state where it is not visually usable due to lack of user interaction * (e.g. blanked, dimmed, locked, set to power save, etc.) Any screensaver processes are * also blocked from displaying. * * If the Surface is destroyed, unmapped, becomes occluded, loses visibility, or otherwise * becomes not visually relevant for the user, the IdleInhibitor will not be honored by * the compositor; if the Surface subsequently regains visibility the inhibitor takes effect * once again. * Likewise, the IdleInhibitor isn't honored if the system was already idled at the time the * IdleInhibitor was established, although if the system later de-idles and re-idles the * IdleInhibitor will take effect. * * @see IdleInhibitManager * @see Surface * @since 5.41 **/ class KWAYLANDCLIENT_EXPORT IdleInhibitor : public QObject { Q_OBJECT public: virtual ~IdleInhibitor(); /** * Setup this IdleInhibitor to manage the @p idleinhibitor. * When using IdleInhibitManager::createIdleInhibitor there is no need to call this * method. **/ void setup(zwp_idle_inhibitor_v1 *idleinhibitor); /** * @returns @c true if managing a zwp_idle_inhibitor_v1. **/ bool isValid() const; /** * Releases the zwp_idle_inhibitor_v1 interface. * After the interface has been released the IdleInhibitor instance is no * longer valid and can be setup with another zwp_idle_inhibitor_v1 interface. **/ void release(); /** * Destroys the data held by this IdleInhibitor. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_idle_inhibitor_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, idleinhibitor, &IdleInhibitor::destroy); * @endcode * * @see release **/ void destroy(); operator zwp_idle_inhibitor_v1*(); operator zwp_idle_inhibitor_v1*() const; private: friend class IdleInhibitManager; explicit IdleInhibitor(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/keyboard.cpp b/src/client/keyboard.cpp index 73bcd48..8d1afa9 100644 --- a/src/client/keyboard.cpp +++ b/src/client/keyboard.cpp @@ -1,219 +1,205 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "keyboard.h" #include "surface.h" #include "wayland_pointer_p.h" #include // wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Keyboard::Private { public: Private(Keyboard *q); void setup(wl_keyboard *k); WaylandPointer keyboard; QPointer enteredSurface; struct { qint32 charactersPerSecond = 0; qint32 delay = 0; } repeatInfo; private: void enter(uint32_t serial, wl_surface *surface, wl_array *keys); void leave(uint32_t serial); static void keymapCallback(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size); static void enterCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys); static void leaveCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface); static void keyCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state); static void modifiersCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); static void repeatInfoCallback(void *data, wl_keyboard *keyboard, int32_t charactersPerSecond, int32_t delay); Keyboard *q; static const wl_keyboard_listener s_listener; }; Keyboard::Private::Private(Keyboard *q) : q(q) { } void Keyboard::Private::setup(wl_keyboard *k) { Q_ASSERT(k); Q_ASSERT(!keyboard); keyboard.setup(k); wl_keyboard_add_listener(keyboard, &s_listener, this); } const wl_keyboard_listener Keyboard::Private::s_listener = { keymapCallback, enterCallback, leaveCallback, keyCallback, modifiersCallback, repeatInfoCallback }; Keyboard::Keyboard(QObject *parent) : QObject(parent) , d(new Private(this)) { } Keyboard::~Keyboard() { release(); } void Keyboard::release() { d->keyboard.release(); } void Keyboard::destroy() { d->keyboard.destroy(); } void Keyboard::setup(wl_keyboard *keyboard) { d->setup(keyboard); } void Keyboard::Private::enterCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); k->enter(serial, surface, keys); } void Keyboard::Private::enter(uint32_t serial, wl_surface *surface, wl_array *keys) { Q_UNUSED(keys) enteredSurface = Surface::get(surface); emit q->entered(serial); } void Keyboard::Private::leaveCallback(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { Q_UNUSED(surface) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); k->leave(serial); } void Keyboard::Private::leave(uint32_t serial) { enteredSurface.clear(); emit q->left(serial); } void Keyboard::Private::keyCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { Q_UNUSED(serial) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); auto toState = [state] { if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { return KeyState::Released; } else { return KeyState::Pressed; } }; emit k->q->keyChanged(key, toState(), time); } void Keyboard::Private::keymapCallback(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { return; } emit k->q->keymapChanged(fd, size); } void Keyboard::Private::modifiersCallback(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { Q_UNUSED(serial) auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); emit k->q->modifiersChanged(modsDepressed, modsLatched, modsLocked, group); } void Keyboard::Private::repeatInfoCallback(void *data, wl_keyboard *keyboard, int32_t charactersPerSecond, int32_t delay) { auto k = reinterpret_cast(data); Q_ASSERT(k->keyboard == keyboard); k->repeatInfo.charactersPerSecond = qMax(charactersPerSecond, 0); k->repeatInfo.delay = qMax(delay, 0); emit k->q->keyRepeatChanged(); } Surface *Keyboard::enteredSurface() { return d->enteredSurface.data(); } Surface *Keyboard::enteredSurface() const { return d->enteredSurface.data(); } bool Keyboard::isValid() const { return d->keyboard.isValid(); } bool Keyboard::isKeyRepeatEnabled() const { return d->repeatInfo.charactersPerSecond > 0; } qint32 Keyboard::keyRepeatDelay() const { return d->repeatInfo.delay; } qint32 Keyboard::keyRepeatRate() const { return d->repeatInfo.charactersPerSecond; } Keyboard::operator wl_keyboard*() { return d->keyboard; } Keyboard::operator wl_keyboard*() const { return d->keyboard; } } } diff --git a/src/client/keyboard.h b/src/client/keyboard.h index a68b3fd..5b25781 100644 --- a/src/client/keyboard.h +++ b/src/client/keyboard.h @@ -1,185 +1,171 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_KEYBOARD_H #define WAYLAND_KEYBOARD_H #include #include struct wl_keyboard; namespace KWayland { namespace Client { class Surface; /** * @short Wrapper for the wl_keyboard interface. * * This class is a convenient wrapper for the wl_keyboard interface. * * To create an instance use Seat::createKeyboard. * * @see Seat **/ class KWAYLANDCLIENT_EXPORT Keyboard : public QObject { Q_OBJECT public: enum class KeyState { Released, Pressed }; explicit Keyboard(QObject *parent = nullptr); virtual ~Keyboard(); /** * @returns @c true if managing a wl_keyboard. **/ bool isValid() const; /** * Setup this Keyboard to manage the @p keyboard. * When using Seat::createKeyboard there is no need to call this * method. **/ void setup(wl_keyboard *keyboard); /** * Releases the wl_keyboard interface. * After the interface has been released the Keyboard instance is no * longer valid and can be setup with another wl_keyboard interface. * * This method is automatically invoked when the Seat which created this * Keyboard gets released. **/ void release(); /** * Destroys the data held by this Keyboard. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_keyboard interface * once there is a new connection available. * * This method is automatically invoked when the Seat which created this * Keyboard gets destroyed. * * @see release **/ void destroy(); /** * @returns The Surface the Keyboard is on, may be @c null. **/ Surface *enteredSurface() const; /** * @overload **/ Surface *enteredSurface(); /** * @returns Whether key repeat is enabled on this keyboard * @see keyRepeatRate * @see keyRepeatDelay * @see keyRepeatChanged * @since 5.5 **/ bool isKeyRepeatEnabled() const; /** * @returns the key repeat rate in characters per second. * @see isKeyRepeatEnabled * @see keyRepeatDelay * @see keyRepeatChanged * @since 5.5 **/ qint32 keyRepeatRate() const; /** * @returns the delay in millisecond for key repeat after a press. * @see isKeyRepeatEnabled * @see keyRepeatRate * @see keyRepeatChanged * @since 5.5 **/ qint32 keyRepeatDelay() const; operator wl_keyboard*(); operator wl_keyboard*() const; Q_SIGNALS: /** * Notification that this seat's Keyboard is focused on a certain surface. * * @param serial The serial for this enter **/ void entered(quint32 serial); /** * Notification that this seat's Keyboard is no longer focused on a certain surface. * * The leave notification is sent before the enter notification for the new focus. * * @param serial The serial of this enter **/ void left(quint32 serial); /** * This signal provides a file descriptor to the client which can * be memory-mapped to provide a keyboard mapping description. * * The signal is only emitted if the keymap format is libxkbcommon compatible. * * @param fd file descriptor of the keymap * @param size The size of the keymap **/ void keymapChanged(int fd, quint32 size); /** * A key was pressed or released. * The time argument is a timestamp with millisecond granularity, with an undefined base. * @param key The key which was pressed * @param state Whether the key got @c Released or @c Pressed * @param time The timestamp **/ void keyChanged(quint32 key, KWayland::Client::Keyboard::KeyState state, quint32 time); /** * Notifies clients that the modifier and/or group state has changed, * and it should update its local state. **/ void modifiersChanged(quint32 depressed, quint32 latched, quint32 locked, quint32 group); /** * Emitted whenever information on key repeat changed. * @see isKeyRepeatEnabled * @see keyRepeatRate * @see keyRepeatDelay * @since 5.5 **/ void keyRepeatChanged(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Keyboard::KeyState) #endif diff --git a/src/client/keystate.cpp b/src/client/keystate.cpp index aaafc09..0d9eb69 100644 --- a/src/client/keystate.cpp +++ b/src/client/keystate.cpp @@ -1,81 +1,67 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "keystate.h" #include "wayland_pointer_p.h" #include #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Keystate::Private { public: Private() {} WaylandPointer keystate; static void org_kde_kwin_keystate_stateChanged(void *data, struct org_kde_kwin_keystate */*keystate*/, uint32_t k, uint32_t s) { auto q = static_cast(data); q->stateChanged(Key(k), State(s)); } static const org_kde_kwin_keystate_listener s_listener; }; const org_kde_kwin_keystate_listener Keystate::Private::s_listener = { org_kde_kwin_keystate_stateChanged }; Keystate::Keystate(QObject *parent) : QObject(parent) , d(new Private()) { } Keystate::~Keystate() = default; void Keystate::fetchStates() { org_kde_kwin_keystate_fetchStates(d->keystate); } void Keystate::setup(org_kde_kwin_keystate* keystate) { d->keystate.setup(keystate); org_kde_kwin_keystate_add_listener(keystate, &Keystate::Private::s_listener, this); } void Keystate::destroy() { d->keystate.destroy(); } void Keystate::setEventQueue(KWayland::Client::EventQueue* /*queue*/) { } } } diff --git a/src/client/keystate.h b/src/client/keystate.h index ec3d7fd..8c74680 100644 --- a/src/client/keystate.h +++ b/src/client/keystate.h @@ -1,81 +1,67 @@ -/******************************************************************** -Copyright 2019 Aleix Pol Gonzalez +/* + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_KEYSTATE_H #define WAYLAND_KEYSTATE_H #include #include struct org_kde_kwin_keystate; namespace KWayland { namespace Client { class EventQueue; class KWAYLANDCLIENT_EXPORT Keystate : public QObject { Q_OBJECT public: enum class Key { CapsLock = 0, NumLock = 1, ScrollLock = 2, }; Q_ENUM(Key); enum State { Unlocked = 0, Latched = 1, Locked = 2, }; Q_ENUM(State) Keystate(QObject* parent); ~Keystate() override; void setEventQueue(EventQueue *queue); void destroy(); void setup(org_kde_kwin_keystate* keystate); void fetchStates(); Q_SIGNALS: /** * State of the @p key changed to @p state */ void stateChanged(Key key, State state); /** * The corresponding global for this interface on the Registry got removed. **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/output.cpp b/src/client/output.cpp index a372f0d..1fc7d53 100644 --- a/src/client/output.cpp +++ b/src/client/output.cpp @@ -1,400 +1,386 @@ -/******************************************************************** -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2013 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "output.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // wayland #include namespace KWayland { namespace Client { namespace { typedef QList Modes; } class Q_DECL_HIDDEN Output::Private { public: Private(Output *q); ~Private(); void setup(wl_output *o); WaylandPointer output; EventQueue *queue = nullptr; QSize physicalSize; QPoint globalPosition; QString manufacturer; QString model; int scale = 1; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; Modes modes; Modes::iterator currentMode = modes.end(); static Output *get(wl_output *o); private: static void geometryCallback(void *data, wl_output *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform); static void modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh); static void doneCallback(void *data, wl_output *output); static void scaleCallback(void *data, wl_output *output, int32_t scale); void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(int scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh); Output *q; static struct wl_output_listener s_outputListener; static QVector s_allOutputs; }; QVector Output::Private::s_allOutputs; Output::Private::Private(Output *q) : q(q) { s_allOutputs << this; } Output::Private::~Private() { s_allOutputs.removeOne(this); } Output *Output::Private::get(wl_output *o) { auto it = std::find_if(s_allOutputs.constBegin(), s_allOutputs.constEnd(), [o] (Private *p) { const wl_output *reference = p->output; return reference == o; } ); if (it != s_allOutputs.constEnd()) { return (*it)->q; } return nullptr; } void Output::Private::setup(wl_output *o) { Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); wl_output_add_listener(output, &s_outputListener, this); } bool Output::Mode::operator==(const Output::Mode &m) const { return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output; } Output::Output(QObject *parent) : QObject(parent) , d(new Private(this)) { } Output::~Output() { d->output.release(); } wl_output_listener Output::Private::s_outputListener = { geometryCallback, modeCallback, doneCallback, scaleCallback }; void Output::Private::geometryCallback(void *data, wl_output *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform) { Q_UNUSED(transform) auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setGlobalPosition(QPoint(x, y)); o->setManufacturer(make); o->setModel(model); o->setPhysicalSize(QSize(physicalWidth, physicalHeight)); auto toSubPixel = [subPixel]() { switch (subPixel) { case WL_OUTPUT_SUBPIXEL_NONE: return SubPixel::None; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return SubPixel::HorizontalRGB; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return SubPixel::HorizontalBGR; case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return SubPixel::VerticalRGB; case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return SubPixel::VerticalBGR; case WL_OUTPUT_SUBPIXEL_UNKNOWN: default: return SubPixel::Unknown; } }; o->setSubPixel(toSubPixel()); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return Transform::Normal; } }; o->setTransform(toTransform()); } void Output::Private::modeCallback(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->addMode(flags, width, height, refresh); } void Output::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh) { Mode mode; mode.output = QPointer(q); mode.refreshRate = refresh; mode.size = QSize(width, height); if (flags & WL_OUTPUT_MODE_CURRENT) { mode.flags |= Mode::Flag::Current; } if (flags & WL_OUTPUT_MODE_PREFERRED) { mode.flags |= Mode::Flag::Preferred; } auto currentIt = modes.insert(modes.end(), mode); bool existing = false; if (flags & WL_OUTPUT_MODE_CURRENT) { auto it = modes.begin(); while (it != currentIt) { auto &m = (*it); if (m.flags.testFlag(Mode::Flag::Current)) { m.flags &= ~Mode::Flags(Mode::Flag::Current); emit q->modeChanged(m); } if (m.refreshRate == mode.refreshRate && m.size == mode.size) { it = modes.erase(it); existing = true; } else { it++; } } currentMode = currentIt; } if (existing) { emit q->modeChanged(mode); } else { emit q->modeAdded(mode); } } void Output::Private::scaleCallback(void *data, wl_output *output, int32_t scale) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(scale); } void Output::Private::doneCallback(void *data, wl_output *output) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); emit o->q->changed(); } void Output::setup(wl_output *output) { d->setup(output); } EventQueue *Output::eventQueue() const { return d->queue; } void Output::setEventQueue(EventQueue *queue) { d->queue = queue; } void Output::Private::setGlobalPosition(const QPoint &pos) { globalPosition = pos; } void Output::Private::setManufacturer(const QString &m) { manufacturer = m; } void Output::Private::setModel(const QString &m) { model = m; } void Output::Private::setPhysicalSize(const QSize &size) { physicalSize = size; } void Output::Private::setScale(int s) { scale = s; } QRect Output::geometry() const { if (d->currentMode == d->modes.end()) { return QRect(); } return QRect(d->globalPosition, pixelSize()); } void Output::Private::setSubPixel(Output::SubPixel s) { subPixel = s; } void Output::Private::setTransform(Output::Transform t) { transform = t; } QPoint Output::globalPosition() const { return d->globalPosition; } QString Output::manufacturer() const { return d->manufacturer; } QString Output::model() const { return d->model; } wl_output *Output::output() { return d->output; } QSize Output::physicalSize() const { return d->physicalSize; } QSize Output::pixelSize() const { if (d->currentMode == d->modes.end()) { return QSize(); } return (*d->currentMode).size; } int Output::refreshRate() const { if (d->currentMode == d->modes.end()) { return 0; } return (*d->currentMode).refreshRate; } int Output::scale() const { return d->scale; } bool Output::isValid() const { return d->output.isValid(); } Output::SubPixel Output::subPixel() const { return d->subPixel; } Output::Transform Output::transform() const { return d->transform; } QList< Output::Mode > Output::modes() const { return d->modes; } Output::operator wl_output*() { return d->output; } Output::operator wl_output*() const { return d->output; } Output *Output::get(wl_output *o) { return Private::get(o); } void Output::destroy() { d->output.destroy(); } } } diff --git a/src/client/output.h b/src/client/output.h index 6a76341..d5ed166 100644 --- a/src/client/output.h +++ b/src/client/output.h @@ -1,264 +1,250 @@ -/******************************************************************** -Copyright 2013 Martin Gräßlin +/* + SPDX-FileCopyrightText: 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_OUTPUT_H #define WAYLAND_OUTPUT_H #include #include #include #include struct wl_output; class QPoint; class QRect; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper for the wl_output interface. * * This class provides a convenient wrapper for the wl_output interface. * Its main purpose is to hold the information about one Output. * * To use this class one needs to interact with the Registry. There are two * possible ways to create an Output interface: * @code * Output *c = registry->createOutput(name, version); * @endcode * * This creates the Output and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Output *c = new Output; * c->setup(registry->bindOutput(name, version)); * @endcode * * The Output can be used as a drop-in replacement for any wl_output * pointer as it provides matching cast operators. * * Please note that all properties of Output are not valid until the * changed signal has been emitted. The wayland server is pushing the * information in an async way to the Output instance. By emitting changed * the Output indicates that all relevant information is available. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT Output : public QObject { Q_OBJECT public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; struct Mode { enum class Flag { None = 0, Current = 1 << 0, Preferred = 1 << 1 }; Q_DECLARE_FLAGS(Flags, Flag) /** * The size of this Mode in pixel space. **/ QSize size; /** * The refresh rate in mHz of this Mode. **/ int refreshRate = 0; /** * The flags of this Mode, that is whether it's the * Current and/or Preferred Mode of the Output. **/ Flags flags = Flag::None; /** * The Output to which this Mode belongs. **/ QPointer output; bool operator==(const Mode &m) const; }; explicit Output(QObject *parent = nullptr); virtual ~Output(); /** * Setup this Compositor to manage the @p output. * When using Registry::createOutput there is no need to call this * method. **/ void setup(wl_output *output); /** * @returns @c true if managing a wl_output. **/ bool isValid() const; operator wl_output*(); operator wl_output*() const; wl_output *output(); /** * Size in millimeters. **/ QSize physicalSize() const; /** * Position within the global compositor space. **/ QPoint globalPosition() const; /** * Textual description of the manufacturer. **/ QString manufacturer() const; /** * Textual description of the model. **/ QString model() const; /** * Size in the current mode. **/ QSize pixelSize() const; /** * The geometry of this Output in pixels. * Convenient for QRect(globalPosition(), pixelSize()). * @see globalPosition * @see pixelSize **/ QRect geometry() const; /** * Refresh rate in mHz of the current mode. **/ int refreshRate() const; /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. **/ int scale() const; /** * Subpixel orientation of this Output. **/ SubPixel subPixel() const; /** * Transform that maps framebuffer to Output. * * The purpose is mainly to allow clients render accordingly and tell the compositor, * so that for fullscreen surfaces, the compositor will still be able to scan out * directly from client surfaces. **/ Transform transform() const; /** * @returns The Modes of this Output. **/ QList modes() const; /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; /** * @returns The Output for the @p native wl_output. @c null if there is no Output for it. * @since 5.27 **/ static Output *get(wl_output *native); /** * Destroys the data hold by this Output. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more as that calls into the Wayland * connection and the call would fail. * * This method is automatically invoked when the Registry which created this * Output gets destroyed. * **/ void destroy(); Q_SIGNALS: /** * Emitted whenever at least one of the data changed. **/ void changed(); /** * Emitted whenever a new Mode is added. * This normally only happens during the initial promoting of modes. * Afterwards only modeChanged should be emitted. * @param mode The newly added Mode. * @see modeChanged **/ void modeAdded(const KWayland::Client::Output::Mode &mode); /** * Emitted whenever a Mode changes. * This normally means that the @c Mode::Flag::Current is added or removed. * @param mode The changed Mode **/ void modeChanged(const KWayland::Client::Output::Mode &mode); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createOutput * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Output*) Q_DECLARE_METATYPE(KWayland::Client::Output::SubPixel) Q_DECLARE_METATYPE(KWayland::Client::Output::Transform) Q_DECLARE_METATYPE(KWayland::Client::Output::Mode) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::Output::Mode::Flags) #endif diff --git a/src/client/outputconfiguration.cpp b/src/client/outputconfiguration.cpp index e4f3ee1..91f2e44 100644 --- a/src/client/outputconfiguration.cpp +++ b/src/client/outputconfiguration.cpp @@ -1,232 +1,218 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputconfiguration.h" #include "outputdevice.h" #include "outputmanagement.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "wayland-output-management-client-protocol.h" #include "wayland-org_kde_kwin_outputdevice-client-protocol.h" namespace KWayland { namespace Client { class Q_DECL_HIDDEN OutputConfiguration::Private { public: Private() = default; void setup(org_kde_kwin_outputconfiguration *outputconfiguration); WaylandPointer outputconfiguration; static struct org_kde_kwin_outputconfiguration_listener s_outputconfigurationListener; EventQueue *queue = nullptr; OutputConfiguration *q; private: static void appliedCallback(void *data, org_kde_kwin_outputconfiguration *config); static void failedCallback(void *data, org_kde_kwin_outputconfiguration *config); }; OutputConfiguration::OutputConfiguration(QObject *parent) : QObject(parent) , d(new Private) { d->q = this; } OutputConfiguration::~OutputConfiguration() { release(); } void OutputConfiguration::setup(org_kde_kwin_outputconfiguration *outputconfiguration) { Q_ASSERT(outputconfiguration); Q_ASSERT(!d->outputconfiguration); d->outputconfiguration.setup(outputconfiguration); d->setup(outputconfiguration); } void OutputConfiguration::Private::setup(org_kde_kwin_outputconfiguration* outputconfiguration) { org_kde_kwin_outputconfiguration_add_listener(outputconfiguration, &s_outputconfigurationListener, this); } void OutputConfiguration::release() { d->outputconfiguration.release(); } void OutputConfiguration::destroy() { d->outputconfiguration.destroy(); } void OutputConfiguration::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *OutputConfiguration::eventQueue() { return d->queue; } OutputConfiguration::operator org_kde_kwin_outputconfiguration*() { return d->outputconfiguration; } OutputConfiguration::operator org_kde_kwin_outputconfiguration*() const { return d->outputconfiguration; } bool OutputConfiguration::isValid() const { return d->outputconfiguration.isValid(); } // Requests void OutputConfiguration::setEnabled(OutputDevice *outputdevice, OutputDevice::Enablement enable) { qint32 _enable = ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_DISABLED; if (enable == OutputDevice::Enablement::Enabled) { _enable = ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_ENABLED; } org_kde_kwin_outputdevice *od = outputdevice->output(); org_kde_kwin_outputconfiguration_enable(d->outputconfiguration, od, _enable); } void OutputConfiguration::setMode(OutputDevice* outputdevice, const int modeId) { org_kde_kwin_outputdevice *od = outputdevice->output(); org_kde_kwin_outputconfiguration_mode(d->outputconfiguration, od, modeId); } void OutputConfiguration::setTransform(OutputDevice *outputdevice, KWayland::Client::OutputDevice::Transform transform) { auto toTransform = [transform]() { switch (transform) { using KWayland::Client::OutputDevice; case KWayland::Client::OutputDevice::Transform::Normal: return WL_OUTPUT_TRANSFORM_NORMAL; case KWayland::Client::OutputDevice::Transform::Rotated90: return WL_OUTPUT_TRANSFORM_90; case KWayland::Client::OutputDevice::Transform::Rotated180: return WL_OUTPUT_TRANSFORM_180; case KWayland::Client::OutputDevice::Transform::Rotated270: return WL_OUTPUT_TRANSFORM_270; case KWayland::Client::OutputDevice::Transform::Flipped: return WL_OUTPUT_TRANSFORM_FLIPPED; case KWayland::Client::OutputDevice::Transform::Flipped90: return WL_OUTPUT_TRANSFORM_FLIPPED_90; case KWayland::Client::OutputDevice::Transform::Flipped180: return WL_OUTPUT_TRANSFORM_FLIPPED_180; case KWayland::Client::OutputDevice::Transform::Flipped270: return WL_OUTPUT_TRANSFORM_FLIPPED_270; } abort(); }; org_kde_kwin_outputdevice *od = outputdevice->output(); org_kde_kwin_outputconfiguration_transform(d->outputconfiguration, od, toTransform()); } void OutputConfiguration::setPosition(OutputDevice *outputdevice, const QPoint &pos) { org_kde_kwin_outputdevice *od = outputdevice->output(); org_kde_kwin_outputconfiguration_position(d->outputconfiguration, od, pos.x(), pos.y()); } void OutputConfiguration::setScale(OutputDevice *outputdevice, qint32 scale) { setScaleF(outputdevice, scale); } void OutputConfiguration::setScaleF(OutputDevice *outputdevice, qreal scale) { org_kde_kwin_outputdevice *od = outputdevice->output(); if (wl_proxy_get_version(d->outputconfiguration) < ORG_KDE_KWIN_OUTPUTCONFIGURATION_SCALEF_SINCE_VERSION) { org_kde_kwin_outputconfiguration_scale(d->outputconfiguration, od, qRound(scale)); } else { org_kde_kwin_outputconfiguration_scalef(d->outputconfiguration, od, wl_fixed_from_double(scale)); } } void OutputConfiguration::setColorCurves(OutputDevice *outputdevice, QVector red, QVector green, QVector blue) { org_kde_kwin_outputdevice *od = outputdevice->output(); wl_array wlRed, wlGreen, wlBlue; auto fillArray = [](QVector &origin, wl_array *dest) { wl_array_init(dest); const size_t memLength = sizeof(uint16_t) * origin.size(); void *s = wl_array_add(dest, memLength); memcpy(s, origin.data(), memLength); }; fillArray(red, &wlRed); fillArray(green, &wlGreen); fillArray(blue, &wlBlue); org_kde_kwin_outputconfiguration_colorcurves(d->outputconfiguration, od, &wlRed, &wlGreen, &wlBlue); wl_array_release(&wlRed); wl_array_release(&wlGreen); wl_array_release(&wlBlue); } void OutputConfiguration::apply() { org_kde_kwin_outputconfiguration_apply(d->outputconfiguration); } // Callbacks org_kde_kwin_outputconfiguration_listener OutputConfiguration::Private::s_outputconfigurationListener = { appliedCallback, failedCallback }; void OutputConfiguration::Private::appliedCallback(void* data, org_kde_kwin_outputconfiguration* config) { Q_UNUSED(config); auto o = reinterpret_cast(data); emit o->q->applied(); } void OutputConfiguration::Private::failedCallback(void* data, org_kde_kwin_outputconfiguration* config) { Q_UNUSED(config); auto o = reinterpret_cast(data); emit o->q->failed(); } } } diff --git a/src/client/outputconfiguration.h b/src/client/outputconfiguration.h index 3f7c3c3..f8a5de5 100644 --- a/src/client/outputconfiguration.h +++ b/src/client/outputconfiguration.h @@ -1,276 +1,262 @@ -/**************************************************************************** -* 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) 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 6 of version 3 of the license. -* -* 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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_OUTPUTCONFIGURATION_H #define KWAYLAND_CLIENT_OUTPUTCONFIGURATION_H #include #include #include #include "outputdevice.h" #include struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputconfiguration; namespace KWayland { namespace Client { class EventQueue; /** @class OutputConfiguration * * OutputConfiguration provides access to changing OutputDevices. The interface is async * and atomic. An OutputConfiguration is created through OutputManagement::createConfiguration(). * * The overall mechanism is to get a new OutputConfiguration from the OutputManagement global and * apply changes through the OutputConfiguration::set* calls. When all changes are set, the client * calls apply, which asks the server to look at the changes and apply them. The server will then * signal back whether the changes have been applied successfully (@c applied()) or were rejected * or failed to apply (@c failed()). * * The current settings for outputdevices can be gotten from @c Registry::outputDevices(), these * are used in the set* calls to identify the output the setting applies to. * * These KWayland classes will not apply changes to the OutputDevices, this is the compositor's * task. As such, the configuration set through this interface can be seen as a hint what the * compositor should set up, but whether or not the compositor does it (based on hardware or * rendering policies, for example), is up to the compositor. The mode setting is passed on to * the DRM subsystem through the compositor. The compositor also saves this configuration and reads * it on startup, this interface is not involved in that process. * * @c apply() should only be called after changes to all output devices have been made, not after * each change. This allows to test the new configuration as a whole, and is a lot faster since * hardware changes can be tested in their new combination, they done in parallel.and rolled back * as a whole. * * \verbatim // We're just picking the first of our outputdevices KWayland::Client::OutputDevice *output = m_clientOutputs.first(); // Create a new configuration object auto config = m_outputManagement.createConfiguration(); // handle applied and failed signals connect(config, &OutputConfiguration::applied, []() { qDebug() << "Configuration applied!"; }); connect(config, &OutputConfiguration::failed, []() { qDebug() << "Configuration failed!"; }); // Change settings config->setMode(output, m_clientOutputs.first()->modes().last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(0, 1920)); config->setScale(output, 2); // Now ask the compositor to apply the changes config->apply(); // You may wait for the applied() or failed() signal here \endverbatim * @see OutputDevice * @see OutputManagement * @see OutputManagement::createConfiguration() * @since 5.5 */ class KWAYLANDCLIENT_EXPORT OutputConfiguration : public QObject { Q_OBJECT public: virtual ~OutputConfiguration(); /** * Setup this OutputConfiguration to manage the @p outputconfiguration. * When using OutputManagement::createOutputConfiguration there is no need to call this * method. * @param outputconfiguration the outputconfiguration object to set up. **/ void setup(org_kde_kwin_outputconfiguration *outputconfiguration); /** * @returns @c true if managing a org_kde_kwin_outputconfiguration. **/ bool isValid() const; /** * Releases the org_kde_kwin_outputconfiguration interface. * After the interface has been released the OutputConfiguration instance is no * longer valid and can be setup with another org_kde_kwin_outputconfiguration interface. **/ void release(); /** * Destroys the data held by this OutputConfiguration. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or setup to a new org_kde_kwin_outputconfiguration interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * OutputConfiguration gets destroyed. * * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a OutputConfiguration. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a OutputConfiguration **/ EventQueue *eventQueue(); /** * Enable or disable an output. Enabled means it's used by the * compositor for rendering, Disabled means, that no wl_output * is connected to this, and the device is sitting there unused * by this compositor. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param enable new Enablement state of this output device. */ void setEnabled(OutputDevice *outputdevice, OutputDevice::Enablement enable); /** * Set the mode of this output, identified by its mode id. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param modeId the id of the mode. */ void setMode(OutputDevice *outputdevice, const int modeId); /** * Set transformation for this output, for example rotated or flipped. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param scale the scaling factor for this output device. */ void setTransform(OutputDevice *outputdevice, KWayland::Client::OutputDevice::Transform transform); /** * Position this output in the global space, relative to other outputs. * QPoint(0, 0) for top-left. The position is the top-left corner of this output. * There may not be gaps between outputs, they have to be positioned adjacent to * each other. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param outputdevice the OutputDevice this change applies to. * @param pos the OutputDevice global position relative to other outputs, * */ void setPosition(OutputDevice *outputdevice, const QPoint &pos); #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 50) /** * Scale rendering of this output. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param scale the scaling factor for this output device. * @param outputdevice the OutputDevice this change applies to. * @deprecated Since 5.50, use setScaleF(OutputDevice *, qreal) */ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 50, "Use OutputConfiguration::setScaleF(OutputDevice *, qreal)") void setScale(OutputDevice *outputdevice, qint32 scale); #endif /** * Scale rendering of this output. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param scale the scaling factor for this output device. * @param outputdevice the OutputDevice this change applies to. * @since 5.50 */ void setScaleF(OutputDevice *outputdevice, qreal scale); /* Set color curves for this output. The respective color curve vector * lengths must equal the current ones in the OutputDevice. The codomain * of the curves is always the full uint16 value range, such that any vector * is accepted as long as it has the right size. * The changes done in this call will be recorded in the * OutputDevice and only applied after apply() has been called. * * @param red color curve of red channel. * @param green color curve of green channel. * @param blue color curve of blue channel. * @param outputdevice the OutputDevice this change applies to. * @since 5.50 */ void setColorCurves(OutputDevice *outputdevice, QVector red, QVector green, QVector blue); /** * Ask the compositor to apply the changes. * This results in the compositor looking at all outputdevices and if they have * pending changes from the set* calls, these changes will be tested with the * hardware and applied if possible. The compositor will react to these changes * with the applied() or failed() signals. Note that mode setting may take a * while, so the interval between calling apply() and receiving the applied() * signal may be considerable, depending on the hardware. * * @see applied() * @see failed() */ void apply(); operator org_kde_kwin_outputconfiguration*(); operator org_kde_kwin_outputconfiguration*() const; Q_SIGNALS: /** * The server has applied all settings successfully. Pending changes in the * OutputDevices have been cleared, changed signals from the OutputDevice have * been emitted. */ void applied(); /** * The server has failed to apply the settings or rejected them. Pending changes * in the * OutputDevices have been cleared. No changes have been applied to the * OutputDevices. */ void failed(); private: friend class OutputManagement; explicit OutputConfiguration(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::OutputConfiguration*) #endif diff --git a/src/client/outputdevice.cpp b/src/client/outputdevice.cpp index 141a997..45afa94 100644 --- a/src/client/outputdevice.cpp +++ b/src/client/outputdevice.cpp @@ -1,555 +1,541 @@ -/******************************************************************** -Copyright 2013 Martin Gräßlin -Copyright 2018 Roman Gilg - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2013 Martin Gräßlin + SPDX-FileCopyrightText: 2018 Roman Gilg + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputdevice.h" #include "logging.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // wayland #include "wayland-org_kde_kwin_outputdevice-client-protocol.h" #include namespace KWayland { namespace Client { typedef QList Modes; class Q_DECL_HIDDEN OutputDevice::Private { public: Private(OutputDevice *q); void setup(org_kde_kwin_outputdevice *o); WaylandPointer output; EventQueue *queue = nullptr; QSize physicalSize; QPoint globalPosition; QString manufacturer; QString model; qreal scale = 1.0; QString serialNumber; QString eisaId; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; Modes modes; Modes::iterator currentMode = modes.end(); QByteArray edid; OutputDevice::Enablement enabled = OutputDevice::Enablement::Enabled; QByteArray uuid; ColorCurves colorCurves; bool done = false; private: static void geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform); static void modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); static void doneCallback(void *data, org_kde_kwin_outputdevice *output); static void scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale); static void scaleFCallback(void *data, org_kde_kwin_outputdevice *output, wl_fixed_t scale); static void edidCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw); static void enabledCallback(void *data, org_kde_kwin_outputdevice *output, int32_t enabled); static void uuidCallback(void *data, org_kde_kwin_outputdevice *output, const char *uuid); static void colorcurvesCallback(void *data, org_kde_kwin_outputdevice *output, wl_array *red, wl_array *green, wl_array *blue); static void serialNumberCallback(void *data, org_kde_kwin_outputdevice *output, const char *serialNumber); static void eisaIdCallback(void *data, org_kde_kwin_outputdevice *output, const char *eisa); void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(qreal scale); void setSerialNumber(const QString &serialNumber); void setEisaId(const QString &eisaId); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id); OutputDevice *q; static struct org_kde_kwin_outputdevice_listener s_outputListener; }; OutputDevice::Private::Private(OutputDevice *q) : q(q) { } void OutputDevice::Private::setup(org_kde_kwin_outputdevice *o) { Q_ASSERT(o); Q_ASSERT(!output); output.setup(o); org_kde_kwin_outputdevice_add_listener(output, &s_outputListener, this); } bool OutputDevice::Mode::operator==(const OutputDevice::Mode &m) const { return size == m.size && refreshRate == m.refreshRate && flags == m.flags && output == m.output; } bool OutputDevice::ColorCurves::operator==(const OutputDevice::ColorCurves &cc) const { return red == cc.red && green == cc.green && blue == cc.blue; } bool OutputDevice::ColorCurves::operator!=(const ColorCurves &cc) const { return !operator==(cc); } OutputDevice::OutputDevice(QObject *parent) : QObject(parent) , d(new Private(this)) { } OutputDevice::~OutputDevice() { d->output.release(); } org_kde_kwin_outputdevice_listener OutputDevice::Private::s_outputListener = { geometryCallback, modeCallback, doneCallback, scaleCallback, edidCallback, enabledCallback, uuidCallback, scaleFCallback, colorcurvesCallback, serialNumberCallback, eisaIdCallback }; void OutputDevice::Private::geometryCallback(void *data, org_kde_kwin_outputdevice *output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subPixel, const char *make, const char *model, int32_t transform) { Q_UNUSED(transform) auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setGlobalPosition(QPoint(x, y)); o->setManufacturer(make); o->setModel(model); o->setPhysicalSize(QSize(physicalWidth, physicalHeight)); auto toSubPixel = [subPixel]() { switch (subPixel) { case WL_OUTPUT_SUBPIXEL_NONE: return SubPixel::None; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: return SubPixel::HorizontalRGB; case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: return SubPixel::HorizontalBGR; case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: return SubPixel::VerticalRGB; case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: return SubPixel::VerticalBGR; case WL_OUTPUT_SUBPIXEL_UNKNOWN: default: return SubPixel::Unknown; } }; o->setSubPixel(toSubPixel()); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return Transform::Normal; } }; o->setTransform(toTransform()); } void OutputDevice::Private::modeCallback(void *data, org_kde_kwin_outputdevice *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->addMode(flags, width, height, refresh, mode_id); } void OutputDevice::Private::addMode(uint32_t flags, int32_t width, int32_t height, int32_t refresh, int32_t mode_id) { Mode mode; mode.output = QPointer(q); mode.refreshRate = refresh; mode.size = QSize(width, height); mode.id = mode_id; if (flags & WL_OUTPUT_MODE_CURRENT) { mode.flags |= Mode::Flag::Current; } if (flags & WL_OUTPUT_MODE_PREFERRED) { mode.flags |= Mode::Flag::Preferred; } auto currentIt = modes.insert(modes.end(), mode); bool existing = false; if (flags & WL_OUTPUT_MODE_CURRENT) { auto it = modes.begin(); while (it != currentIt) { auto &m = (*it); if (m.flags.testFlag(Mode::Flag::Current)) { m.flags &= ~Mode::Flags(Mode::Flag::Current); emit q->modeChanged(m); } if (m.refreshRate == mode.refreshRate && m.size == mode.size) { it = modes.erase(it); existing = true; } else { it++; } } currentMode = currentIt; } if (existing) { emit q->modeChanged(mode); } else { emit q->modeAdded(mode); } } KWayland::Client::OutputDevice::Mode OutputDevice::currentMode() const { for (const auto &m: modes()) { if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Current)) { return m; } } qCWarning(KWAYLAND_CLIENT) << "current mode not found"; return Mode(); } void OutputDevice::Private::scaleCallback(void *data, org_kde_kwin_outputdevice *output, int32_t scale) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(scale); } void OutputDevice::Private::scaleFCallback(void *data, org_kde_kwin_outputdevice *output, wl_fixed_t scale_fixed) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->setScale(wl_fixed_to_double(scale_fixed)); } void OutputDevice::Private::doneCallback(void *data, org_kde_kwin_outputdevice *output) { auto o = reinterpret_cast(data); Q_ASSERT(o->output == output); o->done = true; emit o->q->changed(); emit o->q->done(); } void OutputDevice::Private::edidCallback(void* data, org_kde_kwin_outputdevice* output, const char* raw) { Q_UNUSED(output); auto o = reinterpret_cast(data); o->edid = QByteArray::fromBase64(raw); } void OutputDevice::Private::enabledCallback(void* data, org_kde_kwin_outputdevice* output, int32_t enabled) { Q_UNUSED(output); auto o = reinterpret_cast(data); OutputDevice::Enablement _enabled = OutputDevice::Enablement::Disabled; if (enabled == ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_ENABLED) { _enabled = OutputDevice::Enablement::Enabled; } if (o->enabled != _enabled) { o->enabled = _enabled; emit o->q->enabledChanged(o->enabled); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::uuidCallback(void* data, org_kde_kwin_outputdevice* output, const char *uuid) { Q_UNUSED(output); auto o = reinterpret_cast(data); if (o->uuid != uuid) { o->uuid = uuid; emit o->q->uuidChanged(o->uuid); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::colorcurvesCallback(void *data, org_kde_kwin_outputdevice *output, wl_array *red, wl_array *green, wl_array *blue) { Q_UNUSED(output); auto o = reinterpret_cast(data); auto cc = ColorCurves(); auto setCurve = [](const wl_array *curve, QVector *destination) { destination->resize(curve->size / sizeof(uint16_t)); memcpy(destination->data(), curve->data, curve->size); }; setCurve(red, &cc.red); setCurve(green, &cc.green); setCurve(blue, &cc.blue); if (o->colorCurves != cc) { o->colorCurves = cc; emit o->q->colorCurvesChanged(); if (o->done) { emit o->q->changed(); } } } void OutputDevice::Private::serialNumberCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw) { auto o = reinterpret_cast(data); Q_UNUSED(output); o->setSerialNumber(raw); } void OutputDevice::Private::eisaIdCallback(void *data, org_kde_kwin_outputdevice *output, const char *raw) { auto o = reinterpret_cast(data); Q_UNUSED(output); o->setEisaId(raw); } void OutputDevice::setup(org_kde_kwin_outputdevice *output) { d->setup(output); } EventQueue *OutputDevice::eventQueue() const { return d->queue; } void OutputDevice::setEventQueue(EventQueue *queue) { d->queue = queue; } void OutputDevice::Private::setGlobalPosition(const QPoint &pos) { globalPosition = pos; } void OutputDevice::Private::setManufacturer(const QString &m) { manufacturer = m; } void OutputDevice::Private::setModel(const QString &m) { model = m; } void OutputDevice::Private::setSerialNumber(const QString &sn) { serialNumber = sn; } void OutputDevice::Private::setEisaId(const QString &e) { eisaId = e; } void OutputDevice::Private::setPhysicalSize(const QSize &size) { physicalSize = size; } void OutputDevice::Private::setScale(qreal s) { scale = s; } QRect OutputDevice::geometry() const { if (d->currentMode == d->modes.end()) { return QRect(); } return QRect(d->globalPosition, pixelSize()); } void OutputDevice::Private::setSubPixel(OutputDevice::SubPixel s) { subPixel = s; } void OutputDevice::Private::setTransform(OutputDevice::Transform t) { transform = t; } QPoint OutputDevice::globalPosition() const { return d->globalPosition; } QString OutputDevice::manufacturer() const { return d->manufacturer; } QString OutputDevice::model() const { return d->model; } QString OutputDevice::serialNumber() const { return d->serialNumber; } QString OutputDevice::eisaId() const { return d->eisaId; } org_kde_kwin_outputdevice *OutputDevice::output() { return d->output; } QSize OutputDevice::physicalSize() const { return d->physicalSize; } QSize OutputDevice::pixelSize() const { if (d->currentMode == d->modes.end()) { return QSize(); } return (*d->currentMode).size; } int OutputDevice::refreshRate() const { if (d->currentMode == d->modes.end()) { return 0; } return (*d->currentMode).refreshRate; } int OutputDevice::scale() const { return qRound(d->scale); } qreal OutputDevice::scaleF() const { return d->scale; } bool OutputDevice::isValid() const { return d->output.isValid(); } OutputDevice::SubPixel OutputDevice::subPixel() const { return d->subPixel; } OutputDevice::Transform OutputDevice::transform() const { return d->transform; } QList< OutputDevice::Mode > OutputDevice::modes() const { return d->modes; } OutputDevice::operator org_kde_kwin_outputdevice*() { return d->output; } OutputDevice::operator org_kde_kwin_outputdevice*() const { return d->output; } QByteArray OutputDevice::edid() const { return d->edid; } OutputDevice::Enablement OutputDevice::enabled() const { return d->enabled; } QByteArray OutputDevice::uuid() const { return d->uuid; } OutputDevice::ColorCurves OutputDevice::colorCurves() const { return d->colorCurves; } void OutputDevice::destroy() { d->output.destroy(); } } } diff --git a/src/client/outputdevice.h b/src/client/outputdevice.h index 7fb90d6..400f9fc 100644 --- a/src/client/outputdevice.h +++ b/src/client/outputdevice.h @@ -1,340 +1,326 @@ -/******************************************************************** -Copyright 2013 Martin Gräßlin -Copyright 2018 Roman Gilg +/* + SPDX-FileCopyrightText: 2013 Martin Gräßlin + SPDX-FileCopyrightText: 2018 Roman Gilg -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_OUTPUTDEVICE_H #define WAYLAND_OUTPUTDEVICE_H #include #include #include #include #include struct org_kde_kwin_outputdevice; class QPoint; class QRect; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper for the org_kde_kwin_outputdevice interface. * * This class provides a convenient wrapper for the org_kde_kwin_outputdevice interface. * Its main purpose is to hold the information about one OutputDevice. * * To use this class one needs to interact with the Registry. There are two * possible ways to create an OutputDevice interface: * @code * OutputDevice *c = registry->createOutputDevice(name, version); * @endcode * * This creates the OutputDevice and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * OutputDevice *c = new OutputDevice; * c->setup(registry->bindOutputDevice(name, version)); * @endcode * * The OutputDevice can be used as a drop-in replacement for any org_kde_kwin_outputdevice * pointer as it provides matching cast operators. * * Please note that all properties of OutputDevice are not valid until the * changed signal has been emitted. The wayland server is pushing the * information in an async way to the OutputDevice instance. By emitting changed * the OutputDevice indicates that all relevant information is available. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT OutputDevice : public QObject { Q_OBJECT public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class Enablement { Disabled = 0, Enabled = 1 }; struct Mode { enum class Flag { None = 0, Current = 1 << 0, Preferred = 1 << 1 }; Q_DECLARE_FLAGS(Flags, Flag) /** * The size of this Mode in pixel space. **/ QSize size; /** * The refresh rate in mHz of this Mode. **/ int refreshRate = 0; /** * The flags of this Mode, that is whether it's the * Current and/or Preferred Mode of the OutputDevice. **/ Flags flags = Flag::None; /** * The OutputDevice to which this Mode belongs. **/ QPointer output; /** * The id of this mode, unique per OutputDevice. This id can be used to call * OutputConfiguration->setMode(); * @see OutputConfiguration::setMode **/ int id; bool operator==(const Mode &m) const; }; struct ColorCurves { QVector red, green, blue; bool operator==(const ColorCurves &cc) const; bool operator!=(const ColorCurves &cc) const; }; explicit OutputDevice(QObject *parent = nullptr); virtual ~OutputDevice(); /** * Setup this Compositor to manage the @p output. * When using Registry::createOutputDevice there is no need to call this * method. **/ void setup(org_kde_kwin_outputdevice *output); /** * @returns @c true if managing a org_kde_kwin_outputdevice. **/ bool isValid() const; operator org_kde_kwin_outputdevice*(); operator org_kde_kwin_outputdevice*() const; org_kde_kwin_outputdevice *output(); /** * Size in millimeters. **/ QSize physicalSize() const; /** * Position within the global compositor space. **/ QPoint globalPosition() const; /** * Textual description of the manufacturer. **/ QString manufacturer() const; /** * Textual description of the model. **/ QString model() const; /** * Textual representation of serial number. */ QString serialNumber() const; /** * Textual representation of EISA identifier. */ QString eisaId() const; /** * Size in the current mode. **/ QSize pixelSize() const; /** * The geometry of this OutputDevice in pixels. * Convenient for QRect(globalPosition(), pixelSize()). * @see globalPosition * @see pixelSize **/ QRect geometry() const; /** * Refresh rate in mHz of the current mode. **/ int refreshRate() const; #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 50) /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. * @deprecated Since 5.50, use scaleF() **/ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 50, "Use OutputDevice::scaleF()") int scale() const; #endif /** * Scaling factor of this output. * * A scale larger than 1 means that the compositor will automatically scale surface buffers * by this amount when rendering. This is used for very high resolution displays where * applications rendering at the native resolution would be too small to be legible. * @since 5.50 **/ qreal scaleF() const; /** * Subpixel orientation of this OutputDevice. **/ SubPixel subPixel() const; /** * Transform that maps framebuffer to OutputDevice. * * The purpose is mainly to allow clients render accordingly and tell the compositor, * so that for fullscreen surfaces, the compositor will still be able to scan out * directly from client surfaces. **/ Transform transform() const; /** * Color curves. * @since 5.50 **/ ColorCurves colorCurves() const; /** * @returns The Modes of this OutputDevice. **/ QList modes() const; KWayland::Client::OutputDevice::Mode currentMode() const; /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue() const; /** * @returns The EDID information for this output. **/ QByteArray edid() const; /** * @returns Whether this output is enabled or not. **/ OutputDevice::Enablement enabled() const; /** * @returns A unique identifier for this outputdevice, determined by the server. **/ QByteArray uuid() const; /** * Destroys the data hold by this OutputDevice. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more as that calls into the Wayland * connection and the call would fail. * * This method is automatically invoked when the Registry which created this * Output gets destroyed. **/ void destroy(); Q_SIGNALS: /** * Emitted when the output is fully initialized. **/ void done(); /** * Emitted whenever at least one of the data changed. **/ void changed(); /** * Emitted whenever the enabled property changes. **/ void enabledChanged(OutputDevice::Enablement enabled); /** * Emitted whenever the id property changes. **/ void uuidChanged(const QByteArray &uuid); /** * Emitted whenever a new Mode is added. * This normally only happens during the initial promoting of modes. * Afterwards only modeChanged should be emitted. * @param mode The newly added Mode. * @see modeChanged **/ void modeAdded(const KWayland::Client::OutputDevice::Mode &mode); /** * Emitted whenever a Mode changes. * This normally means that the @c Mode::Flag::Current is added or removed. * @param mode The changed Mode **/ void modeChanged(const KWayland::Client::OutputDevice::Mode &mode); /** * Emitted whenever the color curves changed. * * @since 5.TODO **/ void colorCurvesChanged(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the OutputDevice got created by * Registry::createOutputDevice * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::SubPixel) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Transform) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Enablement) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::Mode) Q_DECLARE_METATYPE(KWayland::Client::OutputDevice::ColorCurves) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::OutputDevice::Mode::Flags) #endif diff --git a/src/client/outputmanagement.cpp b/src/client/outputmanagement.cpp index d58ce06..9687ad1 100644 --- a/src/client/outputmanagement.cpp +++ b/src/client/outputmanagement.cpp @@ -1,108 +1,94 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputmanagement.h" #include "outputconfiguration.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "wayland-output-management-client-protocol.h" namespace KWayland { namespace Client { class Q_DECL_HIDDEN OutputManagement::Private { public: Private() = default; WaylandPointer outputmanagement; EventQueue *queue = nullptr; }; OutputManagement::OutputManagement(QObject *parent) : QObject(parent) , d(new Private) { } OutputManagement::~OutputManagement() { d->outputmanagement.release(); } void OutputManagement::setup(org_kde_kwin_outputmanagement *outputmanagement) { Q_ASSERT(outputmanagement); Q_ASSERT(!d->outputmanagement); d->outputmanagement.setup(outputmanagement); } void OutputManagement::release() { d->outputmanagement.release(); } void OutputManagement::destroy() { d->outputmanagement.destroy(); } void OutputManagement::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *OutputManagement::eventQueue() { return d->queue; } OutputManagement::operator org_kde_kwin_outputmanagement*() { return d->outputmanagement; } OutputManagement::operator org_kde_kwin_outputmanagement*() const { return d->outputmanagement; } bool OutputManagement::isValid() const { return d->outputmanagement.isValid(); } OutputConfiguration *OutputManagement::createConfiguration(QObject *parent) { Q_UNUSED(parent); OutputConfiguration *config = new OutputConfiguration(this); auto w = org_kde_kwin_outputmanagement_create_configuration(d->outputmanagement); if (d->queue) { d->queue->addProxy(w); } config->setup(w); return config; } } } diff --git a/src/client/outputmanagement.h b/src/client/outputmanagement.h index cb74638..c865c0e 100644 --- a/src/client/outputmanagement.h +++ b/src/client/outputmanagement.h @@ -1,140 +1,126 @@ -/**************************************************************************** -* 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) 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 6 of version 3 of the license. -* -* 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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_OUTPUTMANAGEMENT_H #define KWAYLAND_CLIENT_OUTPUTMANAGEMENT_H #include #include struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputconfiguration; namespace KWayland { namespace Client { class EventQueue; class OutputDevice; class OutputConfiguration; /** * @short Wrapper for the org_kde_kwin_outputmanagement interface. * * This class provides a convenient wrapper for the org_kde_kwin_outputmanagement interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the OutputManagement interface: * @code * OutputManagement *c = registry->createOutputManagement(name, version); * @endcode * * This creates the OutputManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * OutputManagement *c = new OutputManagement; * c->setup(registry->bindOutputManagement(name, version)); * @endcode * * The OutputManagement can be used as a drop-in replacement for any org_kde_kwin_outputmanagement * pointer as it provides matching cast operators. * * @see Registry * @since 5.5 **/ class KWAYLANDCLIENT_EXPORT OutputManagement : public QObject { Q_OBJECT public: /** * Creates a new OutputManagement. * Note: after constructing the OutputManagement it is not yet valid and one needs * to call setup. In order to get a ready to use OutputManagement prefer using * Registry::createOutputManagement. **/ explicit OutputManagement(QObject *parent = nullptr); virtual ~OutputManagement(); /** * Setup this OutputManagement to manage the @p outputmanagement. * When using Registry::createOutputManagement there is no need to call this * method. **/ void setup(org_kde_kwin_outputmanagement *outputmanagement); /** * @returns @c true if managing a org_kde_kwin_outputmanagement. **/ bool isValid() const; /** * Releases the org_kde_kwin_outputmanagement interface. * After the interface has been released the OutputManagement instance is no * longer valid and can be setup with another org_kde_kwin_outputmanagement interface. **/ void release(); /** * Destroys the data hold by this OutputManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid any more, it's not * possible to call release any more as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or setup to a new org_kde_kwin_outputmanagement interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * OutputManagement gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this OutputManagement. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this OutputManagement. **/ EventQueue *eventQueue(); OutputConfiguration *createConfiguration(QObject *parent = nullptr); operator org_kde_kwin_outputmanagement*(); operator org_kde_kwin_outputmanagement*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the OutputManagement got created by * Registry::createOutputManagement **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/plasmashell.cpp b/src/client/plasmashell.cpp index 554a27c..8e4ae30 100644 --- a/src/client/plasmashell.cpp +++ b/src/client/plasmashell.cpp @@ -1,354 +1,340 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmashell.h" #include "event_queue.h" #include "output.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaShell::Private { public: WaylandPointer shell; EventQueue *queue = nullptr; }; class Q_DECL_HIDDEN PlasmaShellSurface::Private { public: Private(PlasmaShellSurface *q); ~Private(); void setup(org_kde_plasma_surface *surface); WaylandPointer surface; QSize size; QPointer parentSurface; PlasmaShellSurface::Role role; static PlasmaShellSurface *get(Surface *surface); private: static void autoHidingPanelHiddenCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface); static void autoHidingPanelShownCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface); PlasmaShellSurface *q; static QVector s_surfaces; static const org_kde_plasma_surface_listener s_listener; }; QVector PlasmaShellSurface::Private::s_surfaces; PlasmaShell::PlasmaShell(QObject *parent) : QObject(parent) , d(new Private) { } PlasmaShell::~PlasmaShell() { release(); } void PlasmaShell::destroy() { if (!d->shell) { return; } emit interfaceAboutToBeDestroyed(); d->shell.destroy(); } void PlasmaShell::release() { if (!d->shell) { return; } emit interfaceAboutToBeReleased(); d->shell.release(); } void PlasmaShell::setup(org_kde_plasma_shell *shell) { Q_ASSERT(!d->shell); Q_ASSERT(shell); d->shell.setup(shell); } void PlasmaShell::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaShell::eventQueue() { return d->queue; } PlasmaShellSurface *PlasmaShell::createSurface(wl_surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto kwS = Surface::get(surface); if (kwS) { if (auto s = PlasmaShellSurface::Private::get(kwS)) { return s; } } PlasmaShellSurface *s = new PlasmaShellSurface(parent); connect(this, &PlasmaShell::interfaceAboutToBeReleased, s, &PlasmaShellSurface::release); connect(this, &PlasmaShell::interfaceAboutToBeDestroyed, s, &PlasmaShellSurface::destroy); auto w = org_kde_plasma_shell_get_surface(d->shell, surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); s->d->parentSurface = QPointer(kwS); return s; } PlasmaShellSurface *PlasmaShell::createSurface(Surface *surface, QObject *parent) { return createSurface(*surface, parent); } bool PlasmaShell::isValid() const { return d->shell.isValid(); } PlasmaShell::operator org_kde_plasma_shell*() { return d->shell; } PlasmaShell::operator org_kde_plasma_shell*() const { return d->shell; } PlasmaShellSurface::Private::Private(PlasmaShellSurface *q) : role(PlasmaShellSurface::Role::Normal), q(q) { s_surfaces << this; } PlasmaShellSurface::Private::~Private() { s_surfaces.removeAll(this); } PlasmaShellSurface *PlasmaShellSurface::Private::get(Surface *surface) { if (!surface) { return nullptr; } for (auto it = s_surfaces.constBegin(); it != s_surfaces.constEnd(); ++it) { if ((*it)->parentSurface == surface) { return (*it)->q; } } return nullptr; } void PlasmaShellSurface::Private::setup(org_kde_plasma_surface *s) { Q_ASSERT(s); Q_ASSERT(!surface); surface.setup(s); org_kde_plasma_surface_add_listener(surface, &s_listener, this); } const org_kde_plasma_surface_listener PlasmaShellSurface::Private::s_listener = { autoHidingPanelHiddenCallback, autoHidingPanelShownCallback }; void PlasmaShellSurface::Private::autoHidingPanelHiddenCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface) { auto p = reinterpret_cast(data); Q_ASSERT(p->surface == org_kde_plasma_surface); emit p->q->autoHidePanelHidden(); } void PlasmaShellSurface::Private::autoHidingPanelShownCallback(void *data, org_kde_plasma_surface *org_kde_plasma_surface) { auto p = reinterpret_cast(data); Q_ASSERT(p->surface == org_kde_plasma_surface); emit p->q->autoHidePanelShown(); } PlasmaShellSurface::PlasmaShellSurface(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaShellSurface::~PlasmaShellSurface() { release(); } void PlasmaShellSurface::release() { d->surface.release(); } void PlasmaShellSurface::destroy() { d->surface.destroy(); } void PlasmaShellSurface::setup(org_kde_plasma_surface *surface) { d->setup(surface); } PlasmaShellSurface *PlasmaShellSurface::get(Surface *surface) { if (auto s = PlasmaShellSurface::Private::get(surface)) { return s; } return nullptr; } bool PlasmaShellSurface::isValid() const { return d->surface.isValid(); } PlasmaShellSurface::operator org_kde_plasma_surface*() { return d->surface; } PlasmaShellSurface::operator org_kde_plasma_surface*() const { return d->surface; } void PlasmaShellSurface::setPosition(const QPoint& point) { Q_ASSERT(isValid()); org_kde_plasma_surface_set_position(d->surface, point.x(), point.y()); } void PlasmaShellSurface::setRole(PlasmaShellSurface::Role role) { Q_ASSERT(isValid()); uint32_t wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL; switch (role) { case Role::Normal: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL; break; case Role::Desktop: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_DESKTOP; break; case Role::Panel: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_PANEL; break; case Role::OnScreenDisplay: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_ONSCREENDISPLAY; break; case Role::Notification: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION; break; case Role::ToolTip: wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_TOOLTIP; break; case Role::CriticalNotification: if (wl_proxy_get_version(d->surface) < ORG_KDE_PLASMA_SURFACE_ROLE_CRITICALNOTIFICATION_SINCE_VERSION) { // Fall back to generic notification type if not supported wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION; } else { wlRole = ORG_KDE_PLASMA_SURFACE_ROLE_CRITICALNOTIFICATION; } break; default: Q_UNREACHABLE(); break; } org_kde_plasma_surface_set_role(d->surface, wlRole); d->role = role; } PlasmaShellSurface::Role PlasmaShellSurface::role() const { return d->role; } void PlasmaShellSurface::setPanelBehavior(PlasmaShellSurface::PanelBehavior behavior) { Q_ASSERT(isValid()); uint32_t wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE; switch (behavior) { case PanelBehavior::AlwaysVisible: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE; break; case PanelBehavior::AutoHide: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_AUTO_HIDE; break; case PanelBehavior::WindowsCanCover: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_CAN_COVER; break; case PanelBehavior::WindowsGoBelow: wlRole = ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_GO_BELOW; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_surface_set_panel_behavior(d->surface, wlRole); } void PlasmaShellSurface::setSkipTaskbar(bool skip) { org_kde_plasma_surface_set_skip_taskbar(d->surface, skip); } void PlasmaShellSurface::setSkipSwitcher(bool skip) { org_kde_plasma_surface_set_skip_switcher(d->surface, skip); } void PlasmaShellSurface::requestHideAutoHidingPanel() { org_kde_plasma_surface_panel_auto_hide_hide(d->surface); } void PlasmaShellSurface::requestShowAutoHidingPanel() { org_kde_plasma_surface_panel_auto_hide_show(d->surface); } void PlasmaShellSurface::setPanelTakesFocus(bool takesFocus) { org_kde_plasma_surface_set_panel_takes_focus(d->surface, takesFocus); } } } diff --git a/src/client/plasmashell.h b/src/client/plasmashell.h index 33d0e3a..2429485 100644 --- a/src/client/plasmashell.h +++ b/src/client/plasmashell.h @@ -1,369 +1,355 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_PLASMASHELL_H #define WAYLAND_PLASMASHELL_H #include #include #include struct wl_surface; struct org_kde_plasma_shell; struct org_kde_plasma_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class PlasmaShellSurface; /** * @short Wrapper for the org_kde_plasma_shell interface. * * This class provides a convenient wrapper for the org_kde_plasma_shell interface. * It's main purpose is to create a PlasmaShellSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaShell *s = registry->createPlasmaShell(name, version); * @endcode * * This creates the PlasmaShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaShell *s = new PlasmaShell; * s->setup(registry->bindPlasmaShell(name, version)); * @endcode * * The PlasmaShell can be used as a drop-in replacement for any org_kde_plasma_shell * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaShellSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaShell : public QObject { Q_OBJECT public: explicit PlasmaShell(QObject *parent = nullptr); virtual ~PlasmaShell(); /** * @returns @c true if managing a org_kde_plasma_shell. **/ bool isValid() const; /** * Releases the org_kde_plasma_shell interface. * After the interface has been released the PlasmaShell instance is no * longer valid and can be setup with another org_kde_plasma_shell interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaShell. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_shell interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * PlasmaShell gets destroyed. * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_shell *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The native surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(wl_surface *surface, QObject *parent = nullptr); /** * Creates a PlasmaShellSurface for the given @p surface and sets it up. * * If a PlasmaShellSurface for the given @p surface has already been created * a pointer to the existing one is returned instead of creating a new surface. * * @param surface The Surface to create the PlasmaShellSurface for * @param parent The parent to use for the PlasmaShellSurface * @returns created PlasmaShellSurface **/ PlasmaShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); operator org_kde_plasma_shell*(); operator org_kde_plasma_shell*() const; Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaShell * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_surface interface. * * This class is a convenient wrapper for the org_kde_plasma_surface interface. * * To create an instance use PlasmaShell::createSurface. * * A PlasmaShellSurface is a privileged Surface which can add further hints to the * Wayland server about it's position and the usage role. The Wayland server is allowed * to ignore all requests. * * Even if a PlasmaShellSurface is created for a Surface a normal ShellSurface (or similar) * needs to be created to have the Surface mapped as a window by the Wayland server. * * @see PlasmaShell * @see Surface **/ class KWAYLANDCLIENT_EXPORT PlasmaShellSurface : public QObject { Q_OBJECT public: explicit PlasmaShellSurface(QObject *parent); virtual ~PlasmaShellSurface(); /** * Releases the org_kde_plasma_surface interface. * After the interface has been released the PlasmaShellSurface instance is no * longer valid and can be setup with another org_kde_plasma_surface interface. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets released. **/ void release(); /** * Destroys the data held by this PlasmaShellSurface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_surface interface * once there is a new connection available. * * This method is automatically invoked when the PlasmaShell which created this * PlasmaShellSurface gets destroyed. * * @see release **/ void destroy(); /** * Setup this PlasmaShellSurface to manage the @p surface. * There is normally no need to call this method as it's invoked by * PlasmaShell::createSurface. **/ void setup(org_kde_plasma_surface *surface); /** * @returns the PlasmaShellSurface * associated with surface, * if any, nullptr if not found. * @since 5.6 */ static PlasmaShellSurface *get(Surface *surf); /** * @returns @c true if managing a org_kde_plasma_surface. **/ bool isValid() const; operator org_kde_plasma_surface*(); operator org_kde_plasma_surface*() const; /** * Describes possible roles this PlasmaShellSurface can have. * The role can be used by the Wayland server to e.g. change the stacking order accordingly. **/ enum class Role { Normal, ///< A normal Surface Desktop, ///< The Surface represents a desktop, normally stacked below all other surfaces Panel, ///< The Surface represents a panel (dock), normally stacked above normal surfaces OnScreenDisplay, ///< The Surface represents an on screen display, like a volume changed notification Notification, ///< The Surface represents a notification @since 5.24 ToolTip, ///< The Surface represents a tooltip @since 5.24 CriticalNotification, ///< The Surface represents a critical notification, like battery is running out @since 5.58 }; /** * Changes the requested Role to @p role. * @see role **/ void setRole(Role role); /** * @returns The requested Role, default value is @c Role::Normal. * @see setRole **/ Role role() const; /** * Requests to position this PlasmaShellSurface at @p point in global coordinates. **/ void setPosition(const QPoint &point); /** * Describes how a PlasmaShellSurface with role @c Role::Panel should behave. * @see Role **/ enum class PanelBehavior { AlwaysVisible, AutoHide, WindowsCanCover, WindowsGoBelow }; /** * Sets the PanelBehavior for a PlasmaShellSurface with Role @c Role::Panel * @see setRole **/ void setPanelBehavior(PanelBehavior behavior); /** * Setting this bit to the window, will make it say it prefers * to not be listed in the taskbar. Taskbar implementations * may or may not follow this hint. * @since 5.5 */ void setSkipTaskbar(bool skip); /** * Setting this bit on a window will indicate it does not prefer * to be included in a window switcher. * @since 5.47 */ void setSkipSwitcher(bool skip); /** * Requests to hide a surface with Role Panel and PanelBahvior AutoHide. * * Once the compositor has hidden the panel the signal {@link autoHidePanelHidden} gets * emitted. Once it is shown again the signal {@link autoHidePanelShown} gets emitted. * * To show the surface again from client side use {@link requestShowAutoHidingPanel}. * * @see autoHidePanelHidden * @see autoHidePanelShown * @see requestShowAutoHidingPanel * @since 5.28 **/ void requestHideAutoHidingPanel(); /** * Requests to show a surface with Role Panel and PanelBahvior AutoHide. * * This request allows the client to show a surface which it previously * requested to be hidden with {@link requestHideAutoHidingPanel}. * * @see autoHidePanelHidden * @see autoHidePanelShown * @see requestHideAutoHidingPanel * @since 5.28 **/ void requestShowAutoHidingPanel(); /** * Set whether a PlasmaShellSurface should get focus or not. * * By default some roles do not take focus. With this request the compositor * can be instructed to also pass focus. * * @param takesFocus Set to @c true if the surface should gain focus. * @since 5.28 **/ //KF6 TODO rename to make it generic void setPanelTakesFocus(bool takesFocus); Q_SIGNALS: /** * Emitted when the compositor hided an auto hiding panel. * @see requestHideAutoHidingPanel * @see autoHidePanelShown * @see requestShowAutoHidingPanel * @since 5.28 **/ void autoHidePanelHidden(); /** * Emitted when the compositor showed an auto hiding panel. * @see requestHideAutoHidingPanel * @see autoHidePanelHidden * @see requestShowAutoHidingPanel * @since 5.28 **/ void autoHidePanelShown(); private: friend class PlasmaShell; class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::Role) Q_DECLARE_METATYPE(KWayland::Client::PlasmaShellSurface::PanelBehavior) #endif diff --git a/src/client/plasmavirtualdesktop.cpp b/src/client/plasmavirtualdesktop.cpp index 8a6bd0c..e247a01 100644 --- a/src/client/plasmavirtualdesktop.cpp +++ b/src/client/plasmavirtualdesktop.cpp @@ -1,403 +1,389 @@ -/**************************************************************************** -Copyright 2018 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmavirtualdesktop.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaVirtualDesktopManagement::Private { public: Private(PlasmaVirtualDesktopManagement *q); void setup(org_kde_plasma_virtual_desktop_management *arg); WaylandPointer plasmavirtualdesktopmanagement; EventQueue *queue = nullptr; quint32 rows = 1; QList desktops; inline QList::const_iterator constFindDesktop(const QString &id); inline QList::iterator findDesktop(const QString &id); private: static void createdCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position); static void removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id); static void rowsCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, uint32_t rows); static void doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management); PlasmaVirtualDesktopManagement *q; static const org_kde_plasma_virtual_desktop_management_listener s_listener; }; class Q_DECL_HIDDEN PlasmaVirtualDesktop::Private { public: Private(PlasmaVirtualDesktop *q); void setup(org_kde_plasma_virtual_desktop *arg); WaylandPointer plasmavirtualdesktop; QString id; QString name; bool active = false; private: PlasmaVirtualDesktop *q; private: static void idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id); static void nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name); static void activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); static void deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); static void doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); static void removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); static const org_kde_plasma_virtual_desktop_listener s_listener; }; inline QList::const_iterator PlasmaVirtualDesktopManagement::Private::constFindDesktop(const QString &id) { return std::find_if( desktops.constBegin(), desktops.constEnd(), [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); } inline QList::iterator PlasmaVirtualDesktopManagement::Private::findDesktop(const QString &id) { return std::find_if( desktops.begin(), desktops.end(), [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); } const org_kde_plasma_virtual_desktop_management_listener PlasmaVirtualDesktopManagement::Private::s_listener = { createdCallback, removedCallback, doneCallback, rowsCallback }; void PlasmaVirtualDesktopManagement::Private::createdCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); const QString stringId = QString::fromUtf8(id); PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); Q_ASSERT(vd); p->desktops.insert(position, vd); //TODO: emit a lot of desktopMoved? emit p->q->desktopCreated(stringId, position); } void PlasmaVirtualDesktopManagement::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); const QString stringId = QString::fromUtf8(id); PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); //TODO: emit a lot of desktopMoved? Q_ASSERT(vd); auto i = p->findDesktop(stringId); p->desktops.erase(i); vd->release(); vd->destroy(); vd->deleteLater(); emit p->q->desktopRemoved(stringId); } void PlasmaVirtualDesktopManagement::Private::rowsCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, uint32_t rows) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); if (rows == 0) { return; } p->rows = rows; emit p->q->rowsChanged(rows); } void PlasmaVirtualDesktopManagement::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); emit p->q->done(); } PlasmaVirtualDesktopManagement::PlasmaVirtualDesktopManagement(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaVirtualDesktopManagement::Private::Private(PlasmaVirtualDesktopManagement *q) : q(q) {} void PlasmaVirtualDesktopManagement::Private::setup(org_kde_plasma_virtual_desktop_management *arg) { Q_ASSERT(arg); Q_ASSERT(!plasmavirtualdesktopmanagement); plasmavirtualdesktopmanagement.setup(arg); org_kde_plasma_virtual_desktop_management_add_listener(plasmavirtualdesktopmanagement, &s_listener, this); } PlasmaVirtualDesktopManagement::~PlasmaVirtualDesktopManagement() { release(); } void PlasmaVirtualDesktopManagement::setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement) { d->setup(plasmavirtualdesktopmanagement); } void PlasmaVirtualDesktopManagement::release() { d->plasmavirtualdesktopmanagement.release(); } void PlasmaVirtualDesktopManagement::destroy() { d->plasmavirtualdesktopmanagement.destroy(); } PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() { return d->plasmavirtualdesktopmanagement; } PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() const { return d->plasmavirtualdesktopmanagement; } bool PlasmaVirtualDesktopManagement::isValid() const { return d->plasmavirtualdesktopmanagement.isValid(); } void PlasmaVirtualDesktopManagement::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaVirtualDesktopManagement::eventQueue() { return d->queue; } PlasmaVirtualDesktop *PlasmaVirtualDesktopManagement::getVirtualDesktop(const QString &id) { Q_ASSERT(isValid()); if (id.isEmpty()) { return nullptr; } auto i = d->constFindDesktop(id); if (i != d->desktops.constEnd()) { return *i; } auto w = org_kde_plasma_virtual_desktop_management_get_virtual_desktop(d->plasmavirtualdesktopmanagement, id.toUtf8()); if (!w) { return nullptr; } if (d->queue) { d->queue->addProxy(w); } auto desktop = new PlasmaVirtualDesktop(this); desktop->setup(w); desktop->d->id = id; return desktop; } void PlasmaVirtualDesktopManagement::requestRemoveVirtualDesktop(const QString &id) { Q_ASSERT(isValid()); org_kde_plasma_virtual_desktop_management_request_remove_virtual_desktop(d->plasmavirtualdesktopmanagement, id.toUtf8()); } void PlasmaVirtualDesktopManagement::requestCreateVirtualDesktop(const QString &name, quint32 position) { Q_ASSERT(isValid()); org_kde_plasma_virtual_desktop_management_request_create_virtual_desktop(d->plasmavirtualdesktopmanagement, name.toUtf8(), position); } QList PlasmaVirtualDesktopManagement::desktops() const { return d->desktops; } quint32 PlasmaVirtualDesktopManagement::rows() const { return d->rows; } const org_kde_plasma_virtual_desktop_listener PlasmaVirtualDesktop::Private::s_listener = { idCallback, nameCallback, activatedCallback, deactivatedCallback, doneCallback, removedCallback }; void PlasmaVirtualDesktop::Private::idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); p->id = QString::fromUtf8(id); } void PlasmaVirtualDesktop::Private::nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); p->name = QString::fromUtf8(name); } void PlasmaVirtualDesktop::Private::activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); p->active = true; emit p->q->activated(); } void PlasmaVirtualDesktop::Private::deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); p->active = false; emit p->q->deactivated(); } void PlasmaVirtualDesktop::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); emit p->q->done(); } void PlasmaVirtualDesktop::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) { auto p = reinterpret_cast(data); Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); emit p->q->removed(); } PlasmaVirtualDesktop::Private::Private(PlasmaVirtualDesktop *q) : q(q) { } PlasmaVirtualDesktop::PlasmaVirtualDesktop(QObject *parent) : QObject(parent) , d(new Private(this)) { } void PlasmaVirtualDesktop::Private::setup(org_kde_plasma_virtual_desktop *arg) { Q_ASSERT(arg); Q_ASSERT(!plasmavirtualdesktop); plasmavirtualdesktop.setup(arg); org_kde_plasma_virtual_desktop_add_listener(plasmavirtualdesktop, &s_listener, this); } PlasmaVirtualDesktop::~PlasmaVirtualDesktop() { release(); } void PlasmaVirtualDesktop::setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop) { d->setup(plasmavirtualdesktop); } void PlasmaVirtualDesktop::release() { d->plasmavirtualdesktop.release(); } void PlasmaVirtualDesktop::destroy() { d->plasmavirtualdesktop.destroy(); } PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() { return d->plasmavirtualdesktop; } PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() const { return d->plasmavirtualdesktop; } bool PlasmaVirtualDesktop::isValid() const { return d->plasmavirtualdesktop.isValid(); } void PlasmaVirtualDesktop::requestActivate() { Q_ASSERT(isValid()); org_kde_plasma_virtual_desktop_request_activate(d->plasmavirtualdesktop); } QString PlasmaVirtualDesktop::id() const { return d->id; } QString PlasmaVirtualDesktop::name() const { return d->name; } bool PlasmaVirtualDesktop::isActive() const { return d->active; } } } diff --git a/src/client/plasmavirtualdesktop.h b/src/client/plasmavirtualdesktop.h index 404cd4c..f8d8bc5 100644 --- a/src/client/plasmavirtualdesktop.h +++ b/src/client/plasmavirtualdesktop.h @@ -1,298 +1,284 @@ -/**************************************************************************** -Copyright 2018 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H #define KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H #include #include struct org_kde_plasma_virtual_desktop_management; struct org_kde_plasma_virtual_desktop; namespace KWayland { namespace Client { class EventQueue; class PlasmaVirtualDesktop; /** * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. * * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the PlasmaVirtualDesktopManagement interface: * @code * PlasmaVirtualDesktopManagement *c = registry->createPlasmaVirtualDesktopManagement(name, version); * @endcode * * This creates the PlasmaVirtualDesktopManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaVirtualDesktopManagement *c = new PlasmaVirtualDesktopManagement; * c->setup(registry->bindPlasmaVirtualDesktopManagement(name, version)); * @endcode * * The PlasmaVirtualDesktopManagement can be used as a drop-in replacement for any org_kde_plasma_virtual_desktop_management * pointer as it provides matching cast operators. * @since 5.52 * * @see Registry **/ class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktopManagement : public QObject { Q_OBJECT public: /** * Creates a new PlasmaVirtualDesktopManagement. * Note: after constructing the PlasmaVirtualDesktopManagement it is not yet valid and one needs * to call setup. In order to get a ready to use PlasmaVirtualDesktopManagement prefer using * Registry::createPlasmaVirtualDesktopManagement. **/ explicit PlasmaVirtualDesktopManagement(QObject *parent = nullptr); virtual ~PlasmaVirtualDesktopManagement(); /** * Setup this PlasmaVirtualDesktopManagement to manage the @p plasmavirtualdesktopmanagement. * When using Registry::createPlasmaVirtualDesktopManagement there is no need to call this * method. **/ void setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement); /** * @returns @c true if managing a org_kde_plasma_virtual_desktop_management. **/ bool isValid() const; /** * Releases the org_kde_plasma_virtual_desktop_management interface. * After the interface has been released the PlasmaVirtualDesktopManagement instance is no * longer valid and can be setup with another org_kde_plasma_virtual_desktop_management interface. **/ void release(); /** * Destroys the data held by this PlasmaVirtualDesktopManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop_management interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktopmanagement, &PlasmaVirtualDesktopManagement::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this PlasmaVirtualDesktopManagement. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this PlasmaVirtualDesktopManagement. * The object is owned by the manager and the caller should not delete it. **/ EventQueue *eventQueue(); /** * @returns the PlasmaVirtualDesktop representing the desktop id. * The PlasmaVirtualDesktop instance is guaranteed to be unique for each id. */ PlasmaVirtualDesktop *getVirtualDesktop(const QString &id); /** * Requests for the desktop identified by id to be removed. * The server may or may not acconsent to the request. */ void requestRemoveVirtualDesktop(const QString &id); /** - * Ask the server to create a new virtual desktop, and position it at a specified position. + * Ask the server to create a new virtual desktop, and position it at a specified position. * If the position is zero or less, it will be positioned at the beginning, * if the cosition is the count or more, it will be positioned at the end. * @param name The name we want for the desktop * @param position The position for the desktop to be created */ void requestCreateVirtualDesktop(const QString &name, quint32 position = std::numeric_limits::max()); /** * @returns All the existent virtual desktops */ QList desktops() const; /** * @returns How many rows the virtual desktops should be laid out into * @since 5.55 */ quint32 rows() const; operator org_kde_plasma_virtual_desktop_management*(); operator org_kde_plasma_virtual_desktop_management*() const; Q_SIGNALS: void removed(); /** * Emitted when a new desktop has been added */ void desktopCreated(const QString &id, quint32 position); /** * Emitted when a desktop has been removed */ void desktopRemoved(const QString &id); /** * Emitted when the number of rows of virtual desktops has been changed by the server * @since 5.55 */ void rowsChanged(quint32 rows); /** * This event is sent after all other properties has been * sent after binding to the desktop manager object and after any * other property changes done after that. This allows * changes to the org_kde_plasma_virtual_desktop_management properties * to be seen as atomic, even if they happen via multiple events. */ void done(); private: class Private; QScopedPointer d; }; class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktop : public QObject { Q_OBJECT public: virtual ~PlasmaVirtualDesktop(); /** * Setup this PlasmaVirtualDesktop to manage the @p plasmavirtualdesktop. * When using PlasmaVirtualDesktopManagement::createPlasmaVirtualDesktop there is no need to call this * method. **/ void setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop); /** * @returns @c true if managing a org_kde_plasma_virtual_desktop. **/ bool isValid() const; /** * Releases the org_kde_plasma_virtual_desktop interface. * After the interface has been released the PlasmaVirtualDesktop instance is no * longer valid and can be setup with another org_kde_plasma_virtual_desktop interface. **/ void release(); /** * Destroys the data held by this PlasmaVirtualDesktop. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktop, &PlasmaVirtualDesktop::destroy); * @endcode * * @see release **/ void destroy(); /** * Requests this desktop to be activated. * The server may or may not decide to consent to the request. */ void requestActivate(); /** * @returns The unique id of this desktop. The format of the id is decided by the compositor */ QString id() const; /** * @returns User readable name for the desktop. */ QString name() const; /** * @returns True if the desktop is the active one. * when this property changes, activated or deactivated will be emitted. * @see activated * @see deactivated */ bool isActive() const; operator org_kde_plasma_virtual_desktop*(); operator org_kde_plasma_virtual_desktop*() const; Q_SIGNALS: /** * TODO: activeChanged(bool)? * Emitted when this desktop has been activated by the server */ void activated(); /** * Emitted when this desktop has been activated by the server */ void deactivated(); /** * This event is sent after all other properties has been * sent after binding to the desktop manager object and after any * other property changes done after that. This allows * changes to the org_kde_plasma_virtual_desktop properties * to be seen as atomic, even if they happen via multiple events. */ void done(); /** * This virtual desktop has just been removed by the server: * This object itself is about to be deleted. All windows will * lose the association to this desktop. */ void removed(); private: friend class PlasmaVirtualDesktopManagement; explicit PlasmaVirtualDesktop(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp index 02b3769..de3ba91 100644 --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -1,1095 +1,1081 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmawindowmanagement.h" #include "plasmawindowmodel.h" #include "plasmavirtualdesktop.h" #include "event_queue.h" #include "output.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include #include #include #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaWindowManagement::Private { public: Private(PlasmaWindowManagement *q); WaylandPointer wm; EventQueue *queue = nullptr; bool showingDesktop = false; QList windows; PlasmaWindow *activeWindow = nullptr; void setup(org_kde_plasma_window_management *wm); private: static void showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state); static void windowCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t id); void setShowDesktop(bool set); void windowCreated(org_kde_plasma_window *id, quint32 internalId); static struct org_kde_plasma_window_management_listener s_listener; PlasmaWindowManagement *q; }; class Q_DECL_HIDDEN PlasmaWindow::Private { public: Private(org_kde_plasma_window *window, quint32 internalId, PlasmaWindow *q); WaylandPointer window; quint32 internalId; QString title; QString appId; quint32 desktop = 0; bool active = false; bool minimized = false; bool maximized = false; bool fullscreen = false; bool keepAbove = false; bool keepBelow = false; bool onAllDesktops = false; bool demandsAttention = false; bool closeable = false; bool minimizeable = false; bool maximizeable = false; bool fullscreenable = false; bool skipTaskbar = false; bool skipSwitcher = false; bool shadeable = false; bool shaded = false; bool movable = false; bool resizable = false; bool virtualDesktopChangeable = false; QIcon icon; PlasmaWindowManagement *wm = nullptr; bool unmapped = false; QPointer parentWindow; QMetaObject::Connection parentWindowUnmappedConnection; QStringList plasmaVirtualDesktops; QRect geometry; quint32 pid = 0; QString applicationMenuServiceName; QString applicationMenuObjectPath; private: static void titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title); static void appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *app_id); static void pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid); static void stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state); static void virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number); static void themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name); static void unmappedCallback(void *data, org_kde_plasma_window *window); static void initialStateCallback(void *data, org_kde_plasma_window *window); static void parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent); static void windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height); static void iconChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window); static void virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id); static void virtualDesktopLeftCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id); static void appmenuChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *service_name, const char *object_path); void setActive(bool set); void setMinimized(bool set); void setMaximized(bool set); void setFullscreen(bool set); void setKeepAbove(bool set); void setKeepBelow(bool set); void setOnAllDesktops(bool set); void setDemandsAttention(bool set); void setCloseable(bool set); void setMinimizeable(bool set); void setMaximizeable(bool set); void setFullscreenable(bool set); void setSkipTaskbar(bool skip); void setSkipSwitcher(bool skip); void setShadeable(bool set); void setShaded(bool set); void setMovable(bool set); void setResizable(bool set); void setVirtualDesktopChangeable(bool set); void setParentWindow(PlasmaWindow *parentWindow); void setPid(const quint32 pid); static Private *cast(void *data) { return reinterpret_cast(data); } PlasmaWindow *q; static struct org_kde_plasma_window_listener s_listener; }; PlasmaWindowManagement::Private::Private(PlasmaWindowManagement *q) : q(q) { } org_kde_plasma_window_management_listener PlasmaWindowManagement::Private::s_listener = { showDesktopCallback, windowCallback }; void PlasmaWindowManagement::Private::setup(org_kde_plasma_window_management *windowManagement) { Q_ASSERT(!wm); Q_ASSERT(windowManagement); wm.setup(windowManagement); org_kde_plasma_window_management_add_listener(windowManagement, &s_listener, this); } void PlasmaWindowManagement::Private::showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == org_kde_plasma_window_management); switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: wm->setShowDesktop(true); break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: wm->setShowDesktop(false); break; default: Q_UNREACHABLE(); break; } } void PlasmaWindowManagement::Private::setShowDesktop(bool set) { if (showingDesktop == set) { return; } showingDesktop = set; emit q->showingDesktopChanged(showingDesktop); } void PlasmaWindowManagement::Private::windowCallback(void *data, org_kde_plasma_window_management *interface, uint32_t id) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == interface); QTimer *timer = new QTimer(); timer->setSingleShot(true); timer->setInterval(0); QObject::connect(timer, &QTimer::timeout, wm->q, [timer, wm, id] { wm->windowCreated(org_kde_plasma_window_management_get_window(wm->wm, id), id); timer->deleteLater(); }, Qt::QueuedConnection ); timer->start(); } void PlasmaWindowManagement::Private::windowCreated(org_kde_plasma_window *id, quint32 internalId) { if (queue) { queue->addProxy(id); } PlasmaWindow *window = new PlasmaWindow(q, id, internalId); window->d->wm = q; windows << window; QObject::connect(window, &QObject::destroyed, q, [this, window] { windows.removeAll(window); if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::unmapped, q, [this, window] { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::activeChanged, q, [this, window] { if (window->isActive()) { if (activeWindow == window) { return; } activeWindow = window; emit q->activeWindowChanged(); } else { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } } ); } PlasmaWindowManagement::PlasmaWindowManagement(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaWindowManagement::~PlasmaWindowManagement() { release(); } void PlasmaWindowManagement::destroy() { if (!d->wm) { return; } emit interfaceAboutToBeDestroyed(); d->wm.destroy(); } void PlasmaWindowManagement::release() { if (!d->wm) { return; } emit interfaceAboutToBeReleased(); d->wm.release(); } void PlasmaWindowManagement::setup(org_kde_plasma_window_management *wm) { d->setup(wm); } void PlasmaWindowManagement::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaWindowManagement::eventQueue() { return d->queue; } bool PlasmaWindowManagement::isValid() const { return d->wm.isValid(); } PlasmaWindowManagement::operator org_kde_plasma_window_management*() { return d->wm; } PlasmaWindowManagement::operator org_kde_plasma_window_management*() const { return d->wm; } void PlasmaWindowManagement::hideDesktop() { setShowingDesktop(false); } void PlasmaWindowManagement::showDesktop() { setShowingDesktop(true); } void PlasmaWindowManagement::setShowingDesktop(bool show) { org_kde_plasma_window_management_show_desktop(d->wm, show ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED : ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED); } bool PlasmaWindowManagement::isShowingDesktop() const { return d->showingDesktop; } QList< PlasmaWindow* > PlasmaWindowManagement::windows() const { return d->windows; } PlasmaWindow *PlasmaWindowManagement::activeWindow() const { return d->activeWindow; } PlasmaWindowModel *PlasmaWindowManagement::createWindowModel() { return new PlasmaWindowModel(this); } org_kde_plasma_window_listener PlasmaWindow::Private::s_listener = { titleChangedCallback, appIdChangedCallback, stateChangedCallback, virtualDesktopChangedCallback, themedIconNameChangedCallback, unmappedCallback, initialStateCallback, parentWindowCallback, windowGeometryCallback, iconChangedCallback, pidChangedCallback, virtualDesktopEnteredCallback, virtualDesktopLeftCallback, appmenuChangedCallback }; void PlasmaWindow::Private::appmenuChangedCallback(void *data, org_kde_plasma_window *window, const char *service_name, const char *object_path) { Q_UNUSED(window) - + Private *p = cast(data); p->applicationMenuServiceName = QString::fromUtf8(service_name); p->applicationMenuObjectPath = QString::fromUtf8(object_path); emit p->q->applicationMenuChanged(); } void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent) { Q_UNUSED(window) Private *p = cast(data); const auto windows = p->wm->windows(); auto it = std::find_if(windows.constBegin(), windows.constEnd(), [parent] (const PlasmaWindow *w) { return *w == parent; } ); p->setParentWindow(it != windows.constEnd() ? *it : nullptr); } void PlasmaWindow::Private::windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height) { Q_UNUSED(window) Private *p = cast(data); QRect geo(x, y, width, height); if (geo == p->geometry) { return; } p->geometry = geo; emit p->q->geometryChanged(); } void PlasmaWindow::Private::setParentWindow(PlasmaWindow *parent) { const auto old = parentWindow; QObject::disconnect(parentWindowUnmappedConnection); if (parent && !parent->d->unmapped) { parentWindow = QPointer(parent); parentWindowUnmappedConnection = QObject::connect(parent, &PlasmaWindow::unmapped, q, [this] { setParentWindow(nullptr); } ); } else { parentWindow = QPointer(); parentWindowUnmappedConnection = QMetaObject::Connection(); } if (parentWindow.data() != old.data()) { emit q->parentWindowChanged(); } } void PlasmaWindow::Private::initialStateCallback(void *data, org_kde_plasma_window *window) { Q_UNUSED(window) Private *p = cast(data); if (!p->unmapped) { emit p->wm->windowCreated(p->q); } } void PlasmaWindow::Private::titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title) { Q_UNUSED(window) Private *p = cast(data); const QString t = QString::fromUtf8(title); if (p->title == t) { return; } p->title = t; emit p->q->titleChanged(); } void PlasmaWindow::Private::appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *appId) { Q_UNUSED(window) Private *p = cast(data); const QString s = QString::fromUtf8(appId); if (s == p->appId) { return; } p->appId = s; emit p->q->appIdChanged(); } void PlasmaWindow::Private::pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid) { Q_UNUSED(window) Private *p = cast(data); if (p->pid == static_cast(pid)) { return; } p->pid = pid; } void PlasmaWindow::Private::virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number) { Q_UNUSED(window) Private *p = cast(data); if (p->desktop == static_cast(number)) { return; } p->desktop = number; emit p->q->virtualDesktopChanged(); } void PlasmaWindow::Private::unmappedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); p->unmapped = true; emit p->q->unmapped(); p->q->deleteLater(); } void PlasmaWindow::Private::virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *window, const char *id) { auto p = cast(data); Q_UNUSED(window); const QString stringId(QString::fromUtf8(id)); p->plasmaVirtualDesktops << stringId; emit p->q->plasmaVirtualDesktopEntered(stringId); if (p->plasmaVirtualDesktops.count() == 1) { emit p->q->onAllDesktopsChanged(); } } void PlasmaWindow::Private::virtualDesktopLeftCallback(void *data, org_kde_plasma_window *window, const char *id) { auto p = cast(data); Q_UNUSED(window); const QString stringId(QString::fromUtf8(id)); p->plasmaVirtualDesktops.removeAll(stringId); emit p->q->plasmaVirtualDesktopLeft(stringId); if (p->plasmaVirtualDesktops.isEmpty()) { emit p->q->onAllDesktopsChanged(); } } void PlasmaWindow::Private::stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state) { auto p = cast(data); Q_UNUSED(window); p->setActive(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); p->setMinimized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); p->setMaximized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); p->setFullscreen(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); p->setKeepAbove(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); p->setKeepBelow(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); p->setOnAllDesktops(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS); p->setDemandsAttention(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); p->setCloseable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); p->setFullscreenable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); p->setMaximizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); p->setMinimizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); p->setSkipTaskbar(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); p->setSkipSwitcher(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); p->setShadeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); p->setShaded(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); p->setMovable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); p->setResizable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); p->setVirtualDesktopChangeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } void PlasmaWindow::Private::themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name) { auto p = cast(data); Q_UNUSED(window); const QString themedName = QString::fromUtf8(name); if (!themedName.isEmpty()) { QIcon icon = QIcon::fromTheme(themedName); p->icon = icon; } else { p->icon = QIcon(); } emit p->q->iconChanged(); } static int readData(int fd, QByteArray &data) { // implementation based on QtWayland file qwaylanddataoffer.cpp char buf[4096]; int retryCount = 0; int n; while (true) { n = QT_READ(fd, buf, sizeof buf); if (n == -1 && (errno == EAGAIN) && ++retryCount < 1000) { usleep(1000); } else { break; } } if (n > 0) { data.append(buf, n); n = readData(fd, data); } return n; } void PlasmaWindow::Private::iconChangedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); int pipeFds[2]; if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { return; } org_kde_plasma_window_get_icon(p->window, pipeFds[1]); close(pipeFds[1]); const int pipeFd = pipeFds[0]; auto readIcon = [pipeFd] () -> QIcon { QByteArray content; if (readData(pipeFd, content) != 0) { close(pipeFd); return QIcon(); } close(pipeFd); QDataStream ds(content); QIcon icon; ds >> icon; return icon; }; QFutureWatcher *watcher = new QFutureWatcher(p->q); QObject::connect(watcher, &QFutureWatcher::finished, p->q, [p, watcher] { watcher->deleteLater(); QIcon icon = watcher->result(); if (!icon.isNull()) { p->icon = icon; } else { p->icon = QIcon::fromTheme(QStringLiteral("wayland")); } emit p->q->iconChanged(); } ); watcher->setFuture(QtConcurrent::run(readIcon)); } void PlasmaWindow::Private::setActive(bool set) { if (active == set) { return; } active = set; emit q->activeChanged(); } void PlasmaWindow::Private::setFullscreen(bool set) { if (fullscreen == set) { return; } fullscreen = set; emit q->fullscreenChanged(); } void PlasmaWindow::Private::setKeepAbove(bool set) { if (keepAbove == set) { return; } keepAbove = set; emit q->keepAboveChanged(); } void PlasmaWindow::Private::setKeepBelow(bool set) { if (keepBelow == set) { return; } keepBelow = set; emit q->keepBelowChanged(); } void PlasmaWindow::Private::setMaximized(bool set) { if (maximized == set) { return; } maximized = set; emit q->maximizedChanged(); } void PlasmaWindow::Private::setMinimized(bool set) { if (minimized == set) { return; } minimized = set; emit q->minimizedChanged(); } void PlasmaWindow::Private::setOnAllDesktops(bool set) { if (onAllDesktops == set) { return; } onAllDesktops = set; emit q->onAllDesktopsChanged(); } void PlasmaWindow::Private::setDemandsAttention(bool set) { if (demandsAttention == set) { return; } demandsAttention = set; emit q->demandsAttentionChanged(); } void PlasmaWindow::Private::setCloseable(bool set) { if (closeable == set) { return; } closeable = set; emit q->closeableChanged(); } void PlasmaWindow::Private::setFullscreenable(bool set) { if (fullscreenable == set) { return; } fullscreenable = set; emit q->fullscreenableChanged(); } void PlasmaWindow::Private::setMaximizeable(bool set) { if (maximizeable == set) { return; } maximizeable = set; emit q->maximizeableChanged(); } void PlasmaWindow::Private::setMinimizeable(bool set) { if (minimizeable == set) { return; } minimizeable = set; emit q->minimizeableChanged(); } void PlasmaWindow::Private::setSkipTaskbar(bool skip) { if (skipTaskbar == skip) { return; } skipTaskbar = skip; emit q->skipTaskbarChanged(); } void PlasmaWindow::Private::setSkipSwitcher(bool skip) { if (skipSwitcher == skip) { return; } skipSwitcher = skip; emit q->skipSwitcherChanged(); } void PlasmaWindow::Private::setShadeable(bool set) { if (shadeable == set) { return; } shadeable = set; emit q->shadeableChanged(); } void PlasmaWindow::Private::setShaded(bool set) { if (shaded == set) { return; } shaded = set; emit q->shadedChanged(); } void PlasmaWindow::Private::setMovable(bool set) { if (movable == set) { return; } movable = set; emit q->movableChanged(); } void PlasmaWindow::Private::setResizable(bool set) { if (resizable == set) { return; } resizable = set; emit q->resizableChanged(); } void PlasmaWindow::Private::setVirtualDesktopChangeable(bool set) { if (virtualDesktopChangeable == set) { return; } virtualDesktopChangeable = set; emit q->virtualDesktopChangeableChanged(); } PlasmaWindow::Private::Private(org_kde_plasma_window *w, quint32 internalId, PlasmaWindow *q) : internalId(internalId) , q(q) { window.setup(w); org_kde_plasma_window_add_listener(w, &s_listener, this); } PlasmaWindow::PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *window, quint32 internalId) : QObject(parent) , d(new Private(window, internalId, this)) { } PlasmaWindow::~PlasmaWindow() { release(); } void PlasmaWindow::destroy() { d->window.destroy(); } void PlasmaWindow::release() { d->window.release(); } bool PlasmaWindow::isValid() const { return d->window.isValid(); } PlasmaWindow::operator org_kde_plasma_window*() const { return d->window; } PlasmaWindow::operator org_kde_plasma_window*() { return d->window; } QString PlasmaWindow::appId() const { return d->appId; } quint32 PlasmaWindow::pid() const { return d->pid; } QString PlasmaWindow::title() const { return d->title; } quint32 PlasmaWindow::virtualDesktop() const { return d->desktop; } bool PlasmaWindow::isActive() const { return d->active; } bool PlasmaWindow::isFullscreen() const { return d->fullscreen; } bool PlasmaWindow::isKeepAbove() const { return d->keepAbove; } bool PlasmaWindow::isKeepBelow() const { return d->keepBelow; } bool PlasmaWindow::isMaximized() const { return d->maximized; } bool PlasmaWindow::isMinimized() const { return d->minimized; } bool PlasmaWindow::isOnAllDesktops() const { //from protocol version 8 virtual desktops are managed by plasmaVirtualDesktops if (org_kde_plasma_window_get_version(d->window) < 8) { return d->onAllDesktops; } else { return d->plasmaVirtualDesktops.isEmpty(); } - + } bool PlasmaWindow::isDemandingAttention() const { return d->demandsAttention; } bool PlasmaWindow::isCloseable() const { return d->closeable; } bool PlasmaWindow::isFullscreenable() const { return d->fullscreenable; } bool PlasmaWindow::isMaximizeable() const { return d->maximizeable; } bool PlasmaWindow::isMinimizeable() const { return d->minimizeable; } bool PlasmaWindow::skipTaskbar() const { return d->skipTaskbar; } bool PlasmaWindow::skipSwitcher() const { return d->skipSwitcher; } QIcon PlasmaWindow::icon() const { return d->icon; } bool PlasmaWindow::isShadeable() const { return d->shadeable; } bool PlasmaWindow::isShaded() const { return d->shaded; } bool PlasmaWindow::isResizable() const { return d->resizable; } bool PlasmaWindow::isMovable() const { return d->movable; } bool PlasmaWindow::isVirtualDesktopChangeable() const { return d->virtualDesktopChangeable; } QString PlasmaWindow::applicationMenuObjectPath() const { return d->applicationMenuObjectPath; } QString PlasmaWindow::applicationMenuServiceName() const { return d->applicationMenuServiceName; } void PlasmaWindow::requestActivate() { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } void PlasmaWindow::requestClose() { org_kde_plasma_window_close(d->window); } void PlasmaWindow::requestMove() { org_kde_plasma_window_request_move(d->window); } void PlasmaWindow::requestResize() { org_kde_plasma_window_request_resize(d->window); } void PlasmaWindow::requestVirtualDesktop(quint32 desktop) { org_kde_plasma_window_set_virtual_desktop(d->window, desktop); } void PlasmaWindow::requestToggleKeepAbove() { if (d->keepAbove) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); } } void PlasmaWindow::requestToggleKeepBelow() { if (d->keepBelow) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); } } void PlasmaWindow::requestToggleMinimized() { if (d->minimized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } } void PlasmaWindow::requestToggleMaximized() { if (d->maximized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } } void PlasmaWindow::setMinimizedGeometry(Surface *panel, const QRect &geom) { org_kde_plasma_window_set_minimized_geometry(d->window, *panel, geom.x(), geom.y(), geom.width(), geom.height()); } void PlasmaWindow::unsetMinimizedGeometry(Surface *panel) { org_kde_plasma_window_unset_minimized_geometry(d->window, *panel); } void PlasmaWindow::requestToggleShaded() { if (d->shaded) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } } quint32 PlasmaWindow::internalId() const { return d->internalId; } QPointer PlasmaWindow::parentWindow() const { return d->parentWindow; } QRect PlasmaWindow::geometry() const { return d->geometry; } void PlasmaWindow::requestEnterVirtualDesktop(const QString &id) { org_kde_plasma_window_request_enter_virtual_desktop(d->window, id.toUtf8()); } void PlasmaWindow::requestEnterNewVirtualDesktop() { org_kde_plasma_window_request_enter_new_virtual_desktop(d->window); } void PlasmaWindow::requestLeaveVirtualDesktop(const QString &id) { org_kde_plasma_window_request_leave_virtual_desktop(d->window, id.toUtf8()); } QStringList PlasmaWindow::plasmaVirtualDesktops() const { return d->plasmaVirtualDesktops; } } } diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h index f1db918..3635831 100644 --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -1,719 +1,705 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_PLASMAWINDOWMANAGEMENT_H #define WAYLAND_PLASMAWINDOWMANAGEMENT_H #include #include #include #include struct org_kde_plasma_window_management; struct org_kde_plasma_window; namespace KWayland { namespace Client { class EventQueue; class PlasmaWindow; class PlasmaWindowModel; class Surface; class PlasmaVirtualDesktop; /** * @short Wrapper for the org_kde_plasma_window_management interface. * * PlasmaWindowManagement is a privileged interface. A Wayland compositor is allowed to ignore * any requests. The PlasmaWindowManagement allows to get information about the overall windowing * system. It allows to see which windows are currently available and thus is the base to implement * e.g. a task manager. * * This class provides a convenient wrapper for the org_kde_plasma_window_management interface. * It's main purpose is to create a PlasmaWindowManagementSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaWindowManagement *s = registry->createPlasmaWindowManagement(name, version); * @endcode * * This creates the PlasmaWindowManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaWindowManagement *s = new PlasmaWindowManagement; * s->setup(registry->bindPlasmaWindowManagement(name, version)); * @endcode * * The PlasmaWindowManagement can be used as a drop-in replacement for any org_kde_plasma_window_management * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaWindowManagementSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaWindowManagement : public QObject { Q_OBJECT public: explicit PlasmaWindowManagement(QObject *parent = nullptr); virtual ~PlasmaWindowManagement(); /** * @returns @c true if managing a org_kde_plasma_window_management. **/ bool isValid() const; /** * Releases the org_kde_plasma_window_management interface. * After the interface has been released the PlasmaWindowManagement instance is no * longer valid and can be setup with another org_kde_plasma_window_management interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaWindowManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_window_management interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * PlasmaWindowManagement gets destroyed. * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_window_management *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); operator org_kde_plasma_window_management*(); operator org_kde_plasma_window_management*() const; /** * Whether the system is currently showing the desktop. * This means that the system focuses on the desktop and hides other windows. * @see setShowingDesktop * @see showDesktop * @see hideDesktop * @see showingDesktopChanged **/ bool isShowingDesktop() const; /** * Requests to change the showing desktop state to @p show. * @see isShowingDesktop * @see showDesktop * @see hideDesktop **/ void setShowingDesktop(bool show); /** * Same as calling setShowingDesktop with @c true. * @see setShowingDesktop **/ void showDesktop(); /** * Same as calling setShowingDesktop with @c false. * @see setShowingDesktop **/ void hideDesktop(); /** * @returns All windows currently known to the PlasmaWindowManagement * @see windowCreated **/ QList windows() const; /** * @returns The currently active PlasmaWindow, the PlasmaWindow which * returns @c true in {@link PlasmaWindow::isActive} or @c nullptr in case * there is no active window. **/ PlasmaWindow *activeWindow() const; /** * Factory method to create a PlasmaWindowModel. * @returns a new created PlasmaWindowModel **/ PlasmaWindowModel *createWindowModel(); Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The showing desktop state changed. * @see isShowingDesktop **/ void showingDesktopChanged(bool); /** * A new @p window got created. * @see windows **/ void windowCreated(KWayland::Client::PlasmaWindow *window); /** * The active window changed. * @see activeWindow **/ void activeWindowChanged(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaWindowManagement * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_window interface. * * A PlasmaWindow gets created by the PlasmaWindowManagement and announced through * the {@link PlasmaWindowManagement::windowCreated} signal. The PlasmaWindow encapsulates * state about a window managed by the Wayland server and allows to request state changes. * * The PlasmaWindow will be automatically deleted when the PlasmaWindow gets unmapped. * * This class is a convenient wrapper for the org_kde_plasma_window interface. * The PlasmaWindow gets created by PlasmaWindowManagement. * * @see PlasmaWindowManager **/ class KWAYLANDCLIENT_EXPORT PlasmaWindow : public QObject { Q_OBJECT public: virtual ~PlasmaWindow(); /** * Releases the org_kde_plasma_window interface. * After the interface has been released the PlasmaWindow instance is no * longer valid and can be setup with another org_kde_plasma_window interface. **/ void release(); /** * Destroys the data held by this PlasmaWindow. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_plasma_window interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &PlasmaWindow::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_plasma_window. **/ bool isValid() const; operator org_kde_plasma_window*(); operator org_kde_plasma_window*() const; /** * @returns the window title. * @see titleChanged **/ QString title() const; /** * @returns the application id which should reflect the name of a desktop file. * @see appIdChanged **/ QString appId() const; #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 52) /** * @returns the id of the virtual desktop this PlasmaWindow is on * @see virtualDesktopChanged * @deprecated: Since 5.52, use plasmaVirtualDesktops instead **/ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 52, "Use PlasmaWindow::plasmaVirtualDesktops()") quint32 virtualDesktop() const; #endif /** * @returns Whether the window is currently the active Window. * @see activeChanged **/ bool isActive() const; /** * @returns Whether the window is fullscreen * @see fullscreenChanged **/ bool isFullscreen() const; /** * @returns Whether the window is kept above other windows. * @see keepAboveChanged **/ bool isKeepAbove() const; /** * @returns Whether the window is kept below other window * @see keepBelowChanged **/ bool isKeepBelow() const; /** * @returns Whether the window is currently minimized * @see minimizedChanged **/ bool isMinimized() const; /** * @returns Whether the window is maximized. * @see maximizedChanged **/ bool isMaximized() const; /** * @returns Whether the window is shown on all desktops. * @see virtualDesktop * @see onAllDesktopsChanged **/ bool isOnAllDesktops() const; /** * @returns Whether the window is demanding attention. * @see demandsAttentionChanged **/ bool isDemandingAttention() const; /** * @returns Whether the window can be closed. * @see closeableChanged **/ bool isCloseable() const; /** * @returns Whether the window can be maximized. * @see maximizeableChanged **/ bool isMaximizeable() const; /** * @returns Whether the window can be minimized. * @see minimizeableChanged **/ bool isMinimizeable() const; /** * @returns Whether the window can be set to fullscreen. * @see fullscreenableChanged **/ bool isFullscreenable() const; /** * @returns Whether the window should be ignored by a task bar. * @see skipTaskbarChanged **/ bool skipTaskbar() const; /** * @returns Whether the window should be ignored by a switcher. * @see skipSwitcherChanged **/ bool skipSwitcher() const; /** * @returns The icon of the window. * @see iconChanged **/ QIcon icon() const; /** * @returns Whether the window can be set to the shaded state. * @see isShaded * @see shadeableChanged * @since 5.22 */ bool isShadeable() const; /** * @returns Whether the window is shaded, that is reduced to the window decoration * @see shadedChanged * @since 5.22 */ bool isShaded() const; /** * @returns Whether the window can be moved. * @see movableChanged * @since 5.22 */ bool isMovable() const; /** * @returns Whether the window can be resized. * @see resizableChanged * @since 5.22 */ bool isResizable() const; /** * @returns Whether the virtual desktop can be changed. * @see virtualDesktopChangeableChanged * @since 5.22 */ bool isVirtualDesktopChangeable() const; /** * @returns The process id this window belongs to. * or 0 if unset * @since 5.35 */ quint32 pid() const; /** * Requests to activate the window. **/ void requestActivate(); /** * Requests to close the window. **/ void requestClose(); /** * Requests to start an interactive window move operation. * @since 5.22 */ void requestMove(); /** * Requests to start an interactive resize operation. * @since 5.22 */ void requestResize(); #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 52) /** * Requests to send the window to virtual @p desktop. * @deprecated: Since 5.52, use requestEnterVirtualDesktop instead **/ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 52, "Use PlasmaWindow::requestEnterVirtualDesktop(const QString &)") void requestVirtualDesktop(quint32 desktop); #endif /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ void requestToggleKeepAbove(); /** * Requests the window at this model row index have its keep below state toggled. * @since 5.35 */ void requestToggleKeepBelow(); /** * Requests the window at this model row index have its minimized state toggled. */ void requestToggleMinimized(); /** * Requests the window at this model row index have its maximized state toggled. */ void requestToggleMaximized(); /** * Sets the geometry of the taskbar entry for this window * relative to a panel in particular * @since 5.5 */ void setMinimizedGeometry(Surface *panel, const QRect &geom); /** * Remove the task geometry information for a particular panel * @since 5.5 */ void unsetMinimizedGeometry(Surface *panel); /** * Requests the window at this model row index have its shaded state toggled. * @since 5.22 */ void requestToggleShaded(); /** * An internal window identifier. * This is not a global window identifier. * This identifier does not correspond to QWindow::winId in any way. **/ quint32 internalId() const; /** * The parent window of this PlasmaWindow. * * If there is a parent window, this window is a transient window for the * parent window. If this method returns a null PlasmaWindow it means this * window is a top level window and is not a transient window. * * @see parentWindowChanged * @since 5.24 **/ QPointer parentWindow() const; /** * @returns The window geometry in absolute coordinates. * @see geometryChanged * @since 5.25 **/ QRect geometry() const; /** * Ask the server to make the window enter a virtual desktop. * The server may or may not consent. * A window can enter more than one virtual desktop. * * @since 5.52 */ void requestEnterVirtualDesktop(const QString &id); /** * Make the window enter a new virtual desktop. If the server consents the request, * it will create a new virtual desktop and assign the window to it. * @since 5.52 */ void requestEnterNewVirtualDesktop(); /** * Ask the server to make the window the window exit a virtual desktop. * The server may or may not consent. * If it exits all desktops it will be considered on all of them. * * @since 5.52 */ void requestLeaveVirtualDesktop(const QString &id); /** * Return all the virtual desktop ids this window is associated to. * When a desktop gets deleted, it will be automatically removed from this list. * If this list is empty, assume it's on all desktops. * * @since 5.52 */ QStringList plasmaVirtualDesktops() const; /** * Return the D-BUS service name for a window's * application menu. - * + * * @since 5.69 */ QString applicationMenuServiceName() const; /** * Return the D-BUS object path to a windows's * application menu. - * + * * @since 5.69 */ QString applicationMenuObjectPath() const; Q_SIGNALS: /** * The window title changed. * @see title **/ void titleChanged(); /** * The application id changed. * @see appId **/ void appIdChanged(); #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 52) /** * The virtual desktop changed. * @deprecated Since 5.52, use plasmaVirtualDesktopEntered and plasmaVirtualDesktopLeft instead **/ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 52, "Use PlasmaWindow::plasmaVirtualDesktopEntered(const QString &) and PlasmaWindow::plasmaVirtualDesktopLeft(const QString &)") void virtualDesktopChanged(); #endif /** * The window became active or inactive. * @see isActive **/ void activeChanged(); /** * The fullscreen state changed. * @see isFullscreen **/ void fullscreenChanged(); /** * The keep above state changed. * @see isKeepAbove **/ void keepAboveChanged(); /** * The keep below state changed. * @see isKeepBelow **/ void keepBelowChanged(); /** * The minimized state changed. * @see isMinimized **/ void minimizedChanged(); /** * The maximized state changed. * @see isMaximized **/ void maximizedChanged(); /** * The on all desktops state changed. * @see isOnAllDesktops **/ void onAllDesktopsChanged(); /** * The demands attention state changed. * @see isDemandingAttention **/ void demandsAttentionChanged(); /** * The closeable state changed. * @see isCloseable **/ void closeableChanged(); /** * The minimizeable state changed. * @see isMinimizeable **/ void minimizeableChanged(); /** * The maximizeable state changed. * @see isMaximizeable **/ void maximizeableChanged(); /** * The fullscreenable state changed. * @see isFullscreenable **/ void fullscreenableChanged(); /** * The skip taskbar state changed. * @see skipTaskbar **/ void skipTaskbarChanged(); /** * The skip switcher state changed. * @see skipSwitcher **/ void skipSwitcherChanged(); /** * The window icon changed. * @see icon **/ void iconChanged(); /** * The shadeable state changed. * @see isShadeable * @since 5.22 */ void shadeableChanged(); /** * The shaded state changed. * @see isShaded * @since 5.22 */ void shadedChanged(); /** * The movable state changed. * @see isMovable * @since 5.22 */ void movableChanged(); /** * The resizable state changed. * @see isResizable * @since 5.22 */ void resizableChanged(); /** * The virtual desktop changeable state changed. * @see virtualDesktopChangeable * @since 5.22 */ void virtualDesktopChangeableChanged(); /** * The window got unmapped and is no longer available to the Wayland server. * This instance will be automatically deleted and one should connect to this * signal to perform cleanup. **/ void unmapped(); /** * This signal is emitted whenever the parent window changes. * @see parentWindow * @since 5.24 **/ void parentWindowChanged(); /** * This signal is emitted whenever the window geometry changes. * @see geometry * @since 5.25 **/ void geometryChanged(); /** * This signal is emitted when the window has entered a new virtual desktop. * The window can be on more than one desktop, or none: then is considered on all of them. * @since 5.46 */ void plasmaVirtualDesktopEntered(const QString &id); /** * This signal is emitted when the window left a virtual desktop. * If the window leaves all desktops, it can be considered on all. * * @since 5.46 */ void plasmaVirtualDesktopLeft(const QString &id); /** * This signal is emitted when either the D-BUS service name or * object path for the window's application menu changes. - * + * * @since 5.69 **/ void applicationMenuChanged(); private: friend class PlasmaWindowManagement; explicit PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *dataOffer, quint32 internalId); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaWindow*) #endif diff --git a/src/client/plasmawindowmodel.cpp b/src/client/plasmawindowmodel.cpp index 830f33b..45bcc9b 100644 --- a/src/client/plasmawindowmodel.cpp +++ b/src/client/plasmawindowmodel.cpp @@ -1,379 +1,365 @@ -/******************************************************************** -Copyright 2015 Eike Hein - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmawindowmodel.h" #include "plasmawindowmanagement.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaWindowModel::Private { public: Private(PlasmaWindowModel *q); QList windows; PlasmaWindow *window = nullptr; void addWindow(PlasmaWindow *window); void dataChanged(PlasmaWindow *window, int role); private: PlasmaWindowModel *q; }; PlasmaWindowModel::Private::Private(PlasmaWindowModel *q) : q(q) { } void PlasmaWindowModel::Private::addWindow(PlasmaWindow *window) { if (windows.indexOf(window) != -1) { return; } const int count = windows.count(); q->beginInsertRows(QModelIndex(), count, count); windows.append(window); q->endInsertRows(); auto removeWindow = [window, this] { const int row = windows.indexOf(window); if (row != -1) { q->beginRemoveRows(QModelIndex(), row, row); windows.removeAt(row); q->endRemoveRows(); } }; QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); QObject::connect(window, &QObject::destroyed, q, removeWindow); QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { this->dataChanged(window, Qt::DisplayRole); } ); QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { this->dataChanged(window, Qt::DecorationRole); } ); QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { this->dataChanged(window, PlasmaWindowModel::AppId); } ); QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { this->dataChanged(window, IsActive); } ); QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { this->dataChanged(window, IsFullscreenable); } ); QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { this->dataChanged(window, IsFullscreen); } ); QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { this->dataChanged(window, IsMaximizable); } ); QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { this->dataChanged(window, IsMaximized); } ); QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { this->dataChanged(window, IsMinimizable); } ); QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { this->dataChanged(window, IsMinimized); } ); QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { this->dataChanged(window, IsKeepAbove); } ); QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { this->dataChanged(window, IsKeepBelow); } ); QObject::connect(window, &PlasmaWindow::virtualDesktopChanged, q, [window, this] { this->dataChanged(window, VirtualDesktop); } ); QObject::connect(window, &PlasmaWindow::onAllDesktopsChanged, q, [window, this] { this->dataChanged(window, IsOnAllDesktops); } ); QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { this->dataChanged(window, IsDemandingAttention); } ); QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { this->dataChanged(window, SkipTaskbar); } ); QObject::connect(window, &PlasmaWindow::skipSwitcherChanged, q, [window, this] { this->dataChanged(window, SkipSwitcher); } ); QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { this->dataChanged(window, IsShadeable); } ); QObject::connect(window, &PlasmaWindow::shadedChanged, q, [window, this] { this->dataChanged(window, IsShaded); } ); QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { this->dataChanged(window, IsMovable); } ); QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { this->dataChanged(window, IsResizable); } ); QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { this->dataChanged(window, IsVirtualDesktopChangeable); } ); QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { this->dataChanged(window, IsCloseable); } ); QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { this->dataChanged(window, Geometry); } ); QObject::connect(window, &PlasmaWindow::plasmaVirtualDesktopEntered, q, [window, this] { this->dataChanged(window, VirtualDesktops); } ); QObject::connect(window, &PlasmaWindow::plasmaVirtualDesktopLeft, q, [window, this] { this->dataChanged(window, VirtualDesktops); } ); } void PlasmaWindowModel::Private::dataChanged(PlasmaWindow *window, int role) { QModelIndex idx = q->index(windows.indexOf(window)); emit q->dataChanged(idx, idx, QVector() << role); } PlasmaWindowModel::PlasmaWindowModel(PlasmaWindowManagement *parent) : QAbstractListModel(parent) , d(new Private(this)) { connect(parent, &PlasmaWindowManagement::interfaceAboutToBeReleased, this, [this] { beginResetModel(); d->windows.clear(); endResetModel(); } ); connect(parent, &PlasmaWindowManagement::windowCreated, this, [this](PlasmaWindow *window) { d->addWindow(window); } ); for (auto it = parent->windows().constBegin(); it != parent->windows().constEnd(); ++it) { d->addWindow(*it); } } PlasmaWindowModel::~PlasmaWindowModel() { } QHash PlasmaWindowModel::roleNames() const { QHash roles; roles.insert(Qt::DisplayRole, "DisplayRole"); roles.insert(Qt::DecorationRole, "DecorationRole"); QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); for (int i = 0; i < e.keyCount(); ++i) { roles.insert(e.value(i), e.key(i)); } return roles; } QVariant PlasmaWindowModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= d->windows.count()) { return QVariant(); } const PlasmaWindow *window = d->windows.at(index.row()); if (role == Qt::DisplayRole) { return window->title(); } else if (role == Qt::DecorationRole) { return window->icon(); } else if (role == AppId) { return window->appId(); } else if (role == Pid) { return window->pid(); } else if (role == IsActive) { return window->isActive(); } else if (role == IsFullscreenable) { return window->isFullscreenable(); } else if (role == IsFullscreen) { return window->isFullscreen(); } else if (role == IsMaximizable) { return window->isMaximizeable(); } else if (role == IsMaximized) { return window->isMaximized(); } else if (role == IsMinimizable) { return window->isMinimizeable(); } else if (role == IsMinimized) { return window->isMinimized(); } else if (role == IsKeepAbove) { return window->isKeepAbove(); } else if (role == IsKeepBelow) { return window->isKeepBelow(); } else if (role == VirtualDesktop) { return window->virtualDesktop(); } else if (role == IsOnAllDesktops) { return window->isOnAllDesktops(); } else if (role == IsDemandingAttention) { return window->isDemandingAttention(); } else if (role == SkipTaskbar) { return window->skipTaskbar(); } else if (role == SkipSwitcher) { return window->skipSwitcher(); } else if (role == IsShadeable) { return window->isShadeable(); } else if (role == IsShaded) { return window->isShaded(); } else if (role == IsMovable) { return window->isMovable(); } else if (role == IsResizable) { return window->isResizable(); } else if (role == IsVirtualDesktopChangeable) { return window->isVirtualDesktopChangeable(); } else if (role == IsCloseable) { return window->isCloseable(); } else if (role == Geometry) { return window->geometry(); } else if (role == VirtualDesktops) { return window->plasmaVirtualDesktops(); } return QVariant(); } int PlasmaWindowModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : d->windows.count(); } QModelIndex PlasmaWindowModel::index(int row, int column, const QModelIndex &parent) const { return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row)) : QModelIndex(); } Q_INVOKABLE void PlasmaWindowModel::requestActivate(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestActivate(); } } Q_INVOKABLE void PlasmaWindowModel::requestClose(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestClose(); } } Q_INVOKABLE void PlasmaWindowModel::requestMove(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestMove(); } } Q_INVOKABLE void PlasmaWindowModel::requestResize(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestResize(); } } Q_INVOKABLE void PlasmaWindowModel::requestVirtualDesktop(int row, quint32 desktop) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestVirtualDesktop(desktop); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleKeepAbove(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleKeepAbove(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleKeepBelow(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleKeepBelow(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleMinimized(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleMinimized(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleMaximized(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleMaximized(); } } Q_INVOKABLE void PlasmaWindowModel::setMinimizedGeometry(int row, Surface *panel, const QRect &geom) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->setMinimizedGeometry(panel, geom); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleShaded(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleShaded(); } } } } diff --git a/src/client/plasmawindowmodel.h b/src/client/plasmawindowmodel.h index 0da0b9b..818026e 100644 --- a/src/client/plasmawindowmodel.h +++ b/src/client/plasmawindowmodel.h @@ -1,211 +1,197 @@ -/******************************************************************** -Copyright 2015 Eike Hein - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Eike Hein + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_PLASMAWINDOWMODEL_H #define WAYLAND_PLASMAWINDOWMODEL_H #include #include namespace KWayland { namespace Client { class PlasmaWindowManagement; class Surface; /** * @short Exposes the window list and window state as a Qt item model. * * This class is a QAbstractListModel implementation that exposes information * from a PlasmaWindowManagement instance passed as parent and enables convenient * calls to PlasmaWindow methods through a model row index. * * The model is destroyed when the PlasmaWindowManagement parent is. * * The model resets when the PlasmaWindowManagement parent signals that its * interface is about to be destroyed. * * To use this class you can create an instance yourself, or preferably use the * convenience method in PlasmaWindowManagement: * @code * PlasmaWindowModel *model = wm->createWindowModel(); * @endcode * * @see PlasmaWindowManagement * @see PlasmaWindow **/ class KWAYLANDCLIENT_EXPORT PlasmaWindowModel : public QAbstractListModel { Q_OBJECT public: enum AdditionalRoles { AppId = Qt::UserRole + 1, IsActive, IsFullscreenable, IsFullscreen, IsMaximizable, IsMaximized, IsMinimizable, IsMinimized, IsKeepAbove, IsKeepBelow, #if KWAYLANDCLIENT_ENABLE_DEPRECATED_SINCE(5, 53) /** @deprecated Since 5.53, use VirtualDesktops */ VirtualDesktop, #else VirtualDesktop_DEPRECATED_DO_NOT_USE, #endif IsOnAllDesktops, IsDemandingAttention, SkipTaskbar, /** * @since 5.22 */ IsShadeable, /** * @since 5.22 */ IsShaded, /** * @since 5.22 */ IsMovable, /** * @since 5.22 */ IsResizable, /** * @since 5.22 */ IsVirtualDesktopChangeable, /** * @since 5.22 */ IsCloseable, /** * @since 5.25 */ Geometry, /** * @since 5.35 */ Pid, /** * @since 5.47 */ SkipSwitcher, /** * @since 5.53 */ VirtualDesktops }; Q_ENUM(AdditionalRoles) explicit PlasmaWindowModel(PlasmaWindowManagement *parent); virtual ~PlasmaWindowModel(); QHash roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; /** * Returns an index with internalPointer() pointing to a PlasmaWindow instance. **/ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; /** * Request the window at this model row index be activated. **/ Q_INVOKABLE void requestActivate(int row); /** * Request the window at this model row index be closed. **/ Q_INVOKABLE void requestClose(int row); /** * Request an interactive move for the window at this model row index. * @since 5.22 **/ Q_INVOKABLE void requestMove(int row); /** * Request an interactive resize for the window at this model row index. * @since 5.22 **/ Q_INVOKABLE void requestResize(int row); /** * Request the window at this model row index be moved to this virtual desktop. **/ Q_INVOKABLE void requestVirtualDesktop(int row, quint32 desktop); /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ Q_INVOKABLE void requestToggleKeepAbove(int row); /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ Q_INVOKABLE void requestToggleKeepBelow(int row); /** * Requests the window at this model row index have its minimized state toggled. */ Q_INVOKABLE void requestToggleMinimized(int row); /** * Requests the window at this model row index have its maximized state toggled. */ Q_INVOKABLE void requestToggleMaximized(int row); /** * Sets the geometry of the taskbar entry for the window at the model row * relative to a panel in particular. QRectF, intended for use from QML * @since 5.5 */ Q_INVOKABLE void setMinimizedGeometry(int row, Surface *panel, const QRect &geom); /** * Requests the window at this model row index have its shaded state toggled. * @since 5.22 */ Q_INVOKABLE void requestToggleShaded(int row); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/pointer.cpp b/src/client/pointer.cpp index ffc9a70..79dfd67 100644 --- a/src/client/pointer.cpp +++ b/src/client/pointer.cpp @@ -1,269 +1,255 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointer.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include #include // wayland #include namespace KWayland { namespace Client { static Pointer::Axis wlAxisToPointerAxis(uint32_t axis) { switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: return Pointer::Axis::Vertical; case WL_POINTER_AXIS_HORIZONTAL_SCROLL: return Pointer::Axis::Horizontal; } Q_UNREACHABLE(); } class Q_DECL_HIDDEN Pointer::Private { public: Private(Pointer *q); void setup(wl_pointer *p); WaylandPointer pointer; QPointer enteredSurface; quint32 enteredSerial = 0; private: void enter(uint32_t serial, wl_surface *surface, const QPointF &relativeToSurface); void leave(uint32_t serial); static void enterCallback(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy); static void leaveCallback(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface); static void motionCallback(void *data, wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy); static void buttonCallback(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state); static void axisCallback(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value); static void frameCallback(void *data, wl_pointer *pointer); static void axisSourceCallback(void *data, wl_pointer *pointer, uint32_t axis_source); static void axisStopCallback(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis); static void axisDiscreteCallback(void *data, wl_pointer *pointer, uint32_t axis, int32_t discrete); Pointer *q; static const wl_pointer_listener s_listener; }; Pointer::Private::Private(Pointer *q) : q(q) { } void Pointer::Private::setup(wl_pointer *p) { Q_ASSERT(p); Q_ASSERT(!pointer); pointer.setup(p); wl_pointer_add_listener(pointer, &s_listener, this); } const wl_pointer_listener Pointer::Private::s_listener = { enterCallback, leaveCallback, motionCallback, buttonCallback, axisCallback, frameCallback, axisSourceCallback, axisStopCallback, axisDiscreteCallback }; Pointer::Pointer(QObject *parent) : QObject(parent) , d(new Private(this)) { } Pointer::~Pointer() { release(); } void Pointer::release() { d->pointer.release(); } void Pointer::destroy() { d->pointer.destroy(); } void Pointer::setup(wl_pointer *pointer) { d->setup(pointer); } void Pointer::Private::enterCallback(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); p->enter(serial, surface, QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy))); } void Pointer::Private::enter(uint32_t serial, wl_surface *surface, const QPointF &relativeToSurface) { enteredSurface = QPointer(Surface::get(surface)); enteredSerial = serial; emit q->entered(serial, relativeToSurface); } void Pointer::Private::leaveCallback(void *data, wl_pointer *pointer, uint32_t serial, wl_surface *surface) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); Q_UNUSED(surface) p->leave(serial); } void Pointer::Private::leave(uint32_t serial) { enteredSurface.clear(); emit q->left(serial); } void Pointer::Private::motionCallback(void *data, wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); emit p->q->motion(QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy)), time); } void Pointer::Private::buttonCallback(void *data, wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); auto toState = [state] { if (state == WL_POINTER_BUTTON_STATE_RELEASED) { return ButtonState::Released; } else { return ButtonState::Pressed; } }; emit p->q->buttonStateChanged(serial, time, button, toState()); } void Pointer::Private::axisCallback(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); emit p->q->axisChanged(time, wlAxisToPointerAxis(axis), wl_fixed_to_double(value)); } void Pointer::Private::frameCallback(void *data, wl_pointer *pointer) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); emit p->q->frame(); } void Pointer::Private::axisSourceCallback(void *data, wl_pointer *pointer, uint32_t axis_source) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); AxisSource source; switch (axis_source) { case WL_POINTER_AXIS_SOURCE_WHEEL: source = AxisSource::Wheel; break; case WL_POINTER_AXIS_SOURCE_FINGER: source = AxisSource::Finger; break; case WL_POINTER_AXIS_SOURCE_CONTINUOUS: source = AxisSource::Continuous; break; case WL_POINTER_AXIS_SOURCE_WHEEL_TILT: source = AxisSource::WheelTilt; break; default: Q_UNREACHABLE(); break; } emit p->q->axisSourceChanged(source); } void Pointer::Private::axisStopCallback(void *data, wl_pointer *pointer, uint32_t time, uint32_t axis) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); emit p->q->axisStopped(time, wlAxisToPointerAxis(axis)); } void Pointer::Private::axisDiscreteCallback(void *data, wl_pointer *pointer, uint32_t axis, int32_t discrete) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointer == pointer); emit p->q->axisDiscreteChanged(wlAxisToPointerAxis(axis), discrete); } void Pointer::setCursor(Surface *surface, const QPoint &hotspot) { Q_ASSERT(isValid()); wl_surface *s = nullptr; if (surface) { s = *surface; } wl_pointer_set_cursor(d->pointer, d->enteredSerial, s, hotspot.x(), hotspot.y()); } void Pointer::hideCursor() { setCursor(nullptr); } Surface *Pointer::enteredSurface() { return d->enteredSurface.data(); } Surface *Pointer::enteredSurface() const { return d->enteredSurface.data(); } bool Pointer::isValid() const { return d->pointer.isValid(); } Pointer::operator wl_pointer*() const { return d->pointer; } Pointer::operator wl_pointer*() { return d->pointer; } } } diff --git a/src/client/pointer.h b/src/client/pointer.h index 2212082..7448b74 100644 --- a/src/client/pointer.h +++ b/src/client/pointer.h @@ -1,217 +1,203 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_POINTER_H #define WAYLAND_POINTER_H #include #include #include struct wl_pointer; namespace KWayland { namespace Client { class Surface; /** * @short Wrapper for the wl_pointer interface. * * This class is a convenient wrapper for the wl_pointer interface. * * To create an instance use Seat::createPointer. * * @see Seat **/ class KWAYLANDCLIENT_EXPORT Pointer : public QObject { Q_OBJECT public: enum class ButtonState { Released, Pressed }; enum class Axis { Vertical, Horizontal }; enum class AxisSource { Wheel, Finger, Continuous, WheelTilt }; explicit Pointer(QObject *parent = nullptr); virtual ~Pointer(); /** * @returns @c true if managing a wl_pointer. **/ bool isValid() const; /** * Setup this Pointer to manage the @p pointer. * When using Seat::createPointer there is no need to call this * method. **/ void setup(wl_pointer *pointer); /** * Releases the wl_pointer interface. * After the interface has been released the Pointer instance is no * longer valid and can be setup with another wl_pointer interface. * * This method is automatically invoked when the Seat which created this * Pointer gets released. **/ void release(); /** * Destroys the data held by this Pointer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_pointer interface * once there is a new connection available. * * This method is automatically invoked when the Seat which created this * Pointer gets destroyed. * * @see release **/ void destroy(); /** * Sets the cursor image for this Pointer. * * This has only an effect if a Surface of the same client is focused. * * @param surface The Surface pointing to the image data, if @c null the cursor will be hidden * @param hotspot The hotspot of the cursor image * @see hideCursor * @since 5.3 **/ void setCursor(Surface *surface, const QPoint &hotspot = QPoint()); /** * Hides the cursor. Same as calling setCursor with @c null for surface. * @see setCursor * @since 5.3 **/ void hideCursor(); /** * @returns The Surface the Pointer is on, may be @c null. **/ Surface *enteredSurface() const; /** * @overload **/ Surface *enteredSurface(); operator wl_pointer*(); operator wl_pointer*() const; Q_SIGNALS: /** * Notification that this seat's pointer is focused on a certain surface. * * When an seat's focus enters a surface, the pointer image is undefined * and a client should respond to this event by setting an appropriate pointer * image with the set_cursor request. * * @param serial The serial for this enter * @param relativeToSurface Coordinates relative to the upper-left corner of the Surface. **/ void entered(quint32 serial, const QPointF &relativeToSurface); /** * Notification that this seat's pointer is no longer focused on a certain surface. * * The leave notification is sent before the enter notification for the new focus. * * @param serial The serial of this leave event **/ void left(quint32 serial); /** * Notification of pointer location change. * * @param relativeToSurface Coordinates relative to the upper-left corner of the entered Surface. * @param time timestamp with millisecond granularity **/ void motion(const QPointF &relativeToSurface, quint32 time); /** * Mouse button click and release notifications. * * The location of the click is given by the last motion or enter event. * * @param serial The serial of this button state change * @param time timestamp with millisecond granularity, with an undefined base. * @param button The button which got changed * @param state @c Released or @c Pressed **/ void buttonStateChanged(quint32 serial, quint32 time, quint32 button, KWayland::Client::Pointer::ButtonState state); /** * Scroll and other axis notifications. * * @param time timestamp with millisecond granularity * @param axis @c Vertical or @c Horizontal * @param delta **/ void axisChanged(quint32 time, KWayland::Client::Pointer::Axis axis, qreal delta); /** * Indicates the source of scroll and other axes. * * @since 5.59 **/ void axisSourceChanged(KWayland::Client::Pointer::AxisSource source); /** * Discrete step information for scroll and other axes. * * @since 5.59 **/ void axisDiscreteChanged(KWayland::Client::Pointer::Axis axis, qint32 discreteDelta); /** * Stop notification for scroll and other axes. * * @since 5.59 **/ void axisStopped(quint32 time, KWayland::Client::Pointer::Axis axis); /** * Indicates the end of a set of events that logically belong together. * A client is expected to accumulate the data in all events within the * frame before proceeding. * @since 5.45 **/ void frame(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::Pointer::ButtonState) Q_DECLARE_METATYPE(KWayland::Client::Pointer::Axis) Q_DECLARE_METATYPE(KWayland::Client::Pointer::AxisSource) #endif diff --git a/src/client/pointerconstraints.cpp b/src/client/pointerconstraints.cpp index f8e6c95..60b6897 100644 --- a/src/client/pointerconstraints.cpp +++ b/src/client/pointerconstraints.cpp @@ -1,364 +1,350 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointerconstraints.h" #include "event_queue.h" #include "pointer.h" #include "region.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PointerConstraints::Private { public: Private() = default; void setup(zwp_pointer_constraints_v1 *arg); WaylandPointer pointerconstraints; EventQueue *queue = nullptr; }; PointerConstraints::PointerConstraints(QObject *parent) : QObject(parent) , d(new Private) { } void PointerConstraints::Private::setup(zwp_pointer_constraints_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!pointerconstraints); pointerconstraints.setup(arg); } PointerConstraints::~PointerConstraints() { release(); } void PointerConstraints::setup(zwp_pointer_constraints_v1 *pointerconstraints) { d->setup(pointerconstraints); } void PointerConstraints::release() { d->pointerconstraints.release(); } void PointerConstraints::destroy() { d->pointerconstraints.destroy(); } PointerConstraints::operator zwp_pointer_constraints_v1*() { return d->pointerconstraints; } PointerConstraints::operator zwp_pointer_constraints_v1*() const { return d->pointerconstraints; } bool PointerConstraints::isValid() const { return d->pointerconstraints.isValid(); } void PointerConstraints::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PointerConstraints::eventQueue() { return d->queue; } LockedPointer *PointerConstraints::lockPointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent) { Q_ASSERT(isValid()); auto p = new LockedPointer(parent); zwp_pointer_constraints_v1_lifetime lf; switch (lifetime) { case LifeTime::OneShot: lf = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; break; case LifeTime::Persistent: lf = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT; break; default: Q_UNREACHABLE(); break; } wl_region *wr = nullptr; if (region) { wr = *region; } auto w = zwp_pointer_constraints_v1_lock_pointer(d->pointerconstraints, *surface, *pointer, wr, lf); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } ConfinedPointer *PointerConstraints::confinePointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent) { Q_ASSERT(isValid()); auto p = new ConfinedPointer(parent); zwp_pointer_constraints_v1_lifetime lf; switch (lifetime) { case LifeTime::OneShot: lf = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; break; case LifeTime::Persistent: lf = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT; break; default: Q_UNREACHABLE(); break; } wl_region *wr = nullptr; if (region) { wr = *region; } auto w = zwp_pointer_constraints_v1_confine_pointer(d->pointerconstraints, *surface, *pointer, wr, lf); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN LockedPointer::Private { public: Private(LockedPointer *q); void setup(zwp_locked_pointer_v1 *arg); WaylandPointer lockedpointer; private: LockedPointer *q; private: static void lockedCallback(void *data, zwp_locked_pointer_v1 *zwp_locked_pointer_v1); static void unlockedCallback(void *data, zwp_locked_pointer_v1 *zwp_locked_pointer_v1); static const zwp_locked_pointer_v1_listener s_listener; }; const zwp_locked_pointer_v1_listener LockedPointer::Private::s_listener = { lockedCallback, unlockedCallback }; void LockedPointer::Private::lockedCallback(void *data, zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { auto p = reinterpret_cast(data); Q_ASSERT(p->lockedpointer == zwp_locked_pointer_v1); emit p->q->locked(); } void LockedPointer::Private::unlockedCallback(void *data, zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { auto p = reinterpret_cast(data); Q_ASSERT(p->lockedpointer == zwp_locked_pointer_v1); emit p->q->unlocked(); } LockedPointer::Private::Private(LockedPointer *q) : q(q) { } LockedPointer::LockedPointer(QObject *parent) : QObject(parent) , d(new Private(this)) { } void LockedPointer::Private::setup(zwp_locked_pointer_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!lockedpointer); lockedpointer.setup(arg); zwp_locked_pointer_v1_add_listener(lockedpointer, &s_listener, this); } LockedPointer::~LockedPointer() { release(); } void LockedPointer::setup(zwp_locked_pointer_v1 *lockedpointer) { d->setup(lockedpointer); } void LockedPointer::release() { d->lockedpointer.release(); } void LockedPointer::destroy() { d->lockedpointer.destroy(); } LockedPointer::operator zwp_locked_pointer_v1*() { return d->lockedpointer; } LockedPointer::operator zwp_locked_pointer_v1*() const { return d->lockedpointer; } bool LockedPointer::isValid() const { return d->lockedpointer.isValid(); } void LockedPointer::setCursorPositionHint(const QPointF &surfaceLocal) { Q_ASSERT(isValid()); zwp_locked_pointer_v1_set_cursor_position_hint(d->lockedpointer, wl_fixed_from_double(surfaceLocal.x()), wl_fixed_from_double(surfaceLocal.y())); } void LockedPointer::setRegion(Region *region) { Q_ASSERT(isValid()); wl_region *wr = nullptr; if (region) { wr = *region; } zwp_locked_pointer_v1_set_region(d->lockedpointer, wr); } class Q_DECL_HIDDEN ConfinedPointer::Private { public: Private(ConfinedPointer *q); void setup(zwp_confined_pointer_v1 *arg); WaylandPointer confinedpointer; private: ConfinedPointer *q; private: static void confinedCallback(void *data, zwp_confined_pointer_v1 *zwp_confined_pointer_v1); static void unconfinedCallback(void *data, zwp_confined_pointer_v1 *zwp_confined_pointer_v1); static const zwp_confined_pointer_v1_listener s_listener; }; const zwp_confined_pointer_v1_listener ConfinedPointer::Private::s_listener = { confinedCallback, unconfinedCallback }; void ConfinedPointer::Private::confinedCallback(void *data, zwp_confined_pointer_v1 *zwp_confined_pointer_v1) { auto p = reinterpret_cast(data); Q_ASSERT(p->confinedpointer == zwp_confined_pointer_v1); emit p->q->confined(); } void ConfinedPointer::Private::unconfinedCallback(void *data, zwp_confined_pointer_v1 *zwp_confined_pointer_v1) { auto p = reinterpret_cast(data); Q_ASSERT(p->confinedpointer == zwp_confined_pointer_v1); emit p->q->unconfined(); } ConfinedPointer::Private::Private(ConfinedPointer *q) : q(q) { } ConfinedPointer::ConfinedPointer(QObject *parent) : QObject(parent) , d(new Private(this)) { } void ConfinedPointer::Private::setup(zwp_confined_pointer_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!confinedpointer); confinedpointer.setup(arg); zwp_confined_pointer_v1_add_listener(confinedpointer, &s_listener, this); } ConfinedPointer::~ConfinedPointer() { release(); } void ConfinedPointer::setup(zwp_confined_pointer_v1 *confinedpointer) { d->setup(confinedpointer); } void ConfinedPointer::release() { d->confinedpointer.release(); } void ConfinedPointer::destroy() { d->confinedpointer.destroy(); } ConfinedPointer::operator zwp_confined_pointer_v1*() { return d->confinedpointer; } ConfinedPointer::operator zwp_confined_pointer_v1*() const { return d->confinedpointer; } bool ConfinedPointer::isValid() const { return d->confinedpointer.isValid(); } void ConfinedPointer::setRegion(Region *region) { Q_ASSERT(isValid()); wl_region *wr = nullptr; if (region) { wr = *region; } zwp_confined_pointer_v1_set_region(d->confinedpointer, wr); } } } diff --git a/src/client/pointerconstraints.h b/src/client/pointerconstraints.h index 9c76d02..e8e294f 100644 --- a/src/client/pointerconstraints.h +++ b/src/client/pointerconstraints.h @@ -1,471 +1,457 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_POINTERCONSTRAINTS_H #define KWAYLAND_CLIENT_POINTERCONSTRAINTS_H #include #include struct zwp_pointer_constraints_v1; struct zwp_locked_pointer_v1; struct zwp_confined_pointer_v1; class QPointF; namespace KWayland { namespace Client { class EventQueue; class LockedPointer; class Surface; class Region; class ConfinedPointer; class Pointer; /** * @short Wrapper for the zwp_pointer_constraints_v1 interface. * * This class provides a convenient wrapper for the zwp_pointer_constraints_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the PointerConstraints interface: * @code * PointerConstraints *c = registry->createPointerConstraints(name, version); * @endcode * * This creates the PointerConstraints and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PointerConstraints *c = new PointerConstraints; * c->setup(registry->bindPointerConstraints(name, version)); * @endcode * * The PointerConstraints can be used as a drop-in replacement for any zwp_pointer_constraints_v1 * pointer as it provides matching cast operators. * * @see Registry * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerConstraints : public QObject { Q_OBJECT public: /** * Creates a new PointerConstraints. * Note: after constructing the PointerConstraints it is not yet valid and one needs * to call setup. In order to get a ready to use PointerConstraints prefer using * Registry::createPointerConstraints. **/ explicit PointerConstraints(QObject *parent = nullptr); virtual ~PointerConstraints(); /** * Setup this PointerConstraints to manage the @p pointerconstraints. * When using Registry::createPointerConstraints there is no need to call this * method. **/ void setup(zwp_pointer_constraints_v1 *pointerconstraints); /** * @returns @c true if managing a zwp_pointer_constraints_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_constraints_v1 interface. * After the interface has been released the PointerConstraints instance is no * longer valid and can be setup with another zwp_pointer_constraints_v1 interface. **/ void release(); /** * Destroys the data held by this PointerConstraints. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_pointer_constraints_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerconstraints, &PointerConstraints::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this PointerConstraints. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this PointerConstraints. **/ EventQueue *eventQueue(); /** * These values represent different lifetime semantics. They are passed * as arguments to the factory requests to specify how the constraint * lifetimes should be managed. * @see lockPointer * @see confinePointer **/ enum class LifeTime { /** * A OneShot pointer constraint will never reactivate once it has been * deactivated. **/ OneShot, /** * A persistent pointer constraint may again reactivate once it has * been deactivated. **/ Persistent }; /** * This factory method creates a LockedPointer. * * A LockedPointer lets the client request to disable movements of * the virtual pointer (i.e. the cursor), effectively locking the pointer * to a position. * * Creating a LockedPointer does not lock the pointer immediately; in the * future, when the compositor deems implementation-specific constraints * are satisfied, the pointer lock will be activated and the compositor * sends a locked event, reported by {@link LockedPointer::locked}. * * The protocol provides no guarantee that the constraints are ever * satisfied, and does not require the compositor to send an error if the * constraints cannot ever be satisfied. It is thus possible to request a * lock that will never activate. * * There may not be another pointer constraint of any kind requested or * active on the @p surface for any of the Pointer objects of the Seat of * the passed @p pointer when requesting a lock. If there is, an error will be * raised. * * The intersection of the @p region passed with this request and the input * region of the @p surface is used to determine where the pointer must be * in order for the lock to activate. It is up to the compositor whether to * warp the pointer or require some kind of user interaction for the lock * to activate. If the @p region is null the surface input region is used. * * A Surface may receive pointer focus without the lock being activated. * * Note that while a pointer is locked, the Pointer objects of the * corresponding seat will not emit any {@link Pointer::motion} signals, but * relative motion events will still be emitted via {@link RelativePointer::relativeMotion}. * Pointer axis and button events are unaffected. * * @param surface The Surface which should be constrained in pointer motion * @param pointer The Pointer object for which this LockedPointer should be created * @param region Region where to lock the pointer, if @c null the input region of the Surface is used * @param lifetime Whether the LockedPointer becomes invalid on unlocked * @param parent The parent object for the LockedPointer * @returns The factored LockedPointer **/ LockedPointer *lockPointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent = nullptr); /** * This factory method creates a ConfinedPointer. * * A ConfinedPointer lets the client request to confine the * pointer cursor to a given @p region. Creating a ConfinedPointer * does not take effect immediately; in the future, when the compositor * deems implementation-specific constraints are satisfied, the pointer * confinement will be activated and the compositor sends a confined event, * which is reported through the {@link ConfinedPointer::confined} signal. * * The intersection of the @p region passed and the input region of the * @p surface is used to determine where the pointer must be * in order for the confinement to activate. It is up to the compositor * whether to warp the pointer or require some kind of user interaction for * the confinement to activate. If the @p region is @c null the @p surface input * region is used. * * @param surface The Surface which should be constrained in pointer motion * @param pointer The Pointer object for which this LockedPointer should be created * @param region Region where to confine the pointer, if @c null the input region of the Surface is used * @param lifetime Whether the ConfinedPointer becomes invalid on unconfined * @param parent The parent object for the ConfinedPointer * @returns The factored ConfinedPointer **/ ConfinedPointer *confinePointer(Surface *surface, Pointer *pointer, Region *region, LifeTime lifetime, QObject *parent = nullptr); operator zwp_pointer_constraints_v1*(); operator zwp_pointer_constraints_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the PointerConstraints got created by * Registry::createPointerConstraints **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the zwp_locked_pointer_v1 interface. * * The LockedPointer represents a locked pointer state. * * While the lock of this object is active, the Pointer objects of the * associated seat will not emit any {@link Pointer::motion} events. * * This object will send the signal locked when the lock is activated. * Whenever the lock is activated, it is guaranteed that the locked surface * will already have received pointer focus and that the pointer will be * within the region passed to the request creating this object. * * To unlock the pointer, delete the object. * * If the compositor decides to unlock the pointer the unlocked signal is * emitted. * * When unlocking, the compositor may warp the cursor position to the set * cursor position hint. If it does, it will not result in any relative * motion events emitted via {@link RelativePointer::relativeMotion}. * * If the Surface the lock was requested on is destroyed and the lock is not * yet activated, the LockedPointer object is now defunct and must be * deleted. * * @see PointerConstraints::lockedPointer * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT LockedPointer : public QObject { Q_OBJECT public: virtual ~LockedPointer(); /** * Setup this LockedPointer to manage the @p lockedpointer. * When using PointerConstraints::createLockedPointer there is no need to call this * method. **/ void setup(zwp_locked_pointer_v1 *lockedpointer); /** * @returns @c true if managing a zwp_locked_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_locked_pointer_v1 interface. * After the interface has been released the LockedPointer instance is no * longer valid and can be setup with another zwp_locked_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this LockedPointer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_locked_pointer_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, lockedpointer, &LockedPointer::destroy); * @endcode * * @see release **/ void destroy(); /** * Set the cursor position hint relative to the top left corner of the Surface. * * If the client is drawing its own cursor, it should update the position * hint to the position of its own cursor. A compositor may use this * information to warp the pointer upon unlock in order to avoid pointer * jumps. * * The cursor position hint is double buffered. The new hint will only take * effect when the associated surface gets it pending state applied. * See {@link Surface::commit} for details. * * @param surfaceLocal The new position hint in surface local coordinates * @see Surface::commit **/ void setCursorPositionHint(const QPointF &surfaceLocal); /** * Set a new region used to lock the pointer. * * The new lock region is double-buffered. The new lock region will * only take effect when the associated Surface gets its pending state * applied. See {@link Surface::commit} for details. * * @param region The new lock region. * @see Surface::commit * @see PointerConstraints::lockPointer **/ void setRegion(Region *region); operator zwp_locked_pointer_v1*(); operator zwp_locked_pointer_v1*() const; Q_SIGNALS: /** * Notification that the pointer lock of the seat's pointer is activated. * @see unlocked **/ void locked(); /** * Notification that the pointer lock of the seat's pointer is no longer * active. If this is a oneshot pointer lock (see * wp_pointer_constraints.lifetime) this object is now defunct and should * be destroyed. If this is a persistent pointer lock (see * wp_pointer_constraints.lifetime) this pointer lock may again * reactivate in the future. * @see locked **/ void unlocked(); private: friend class PointerConstraints; explicit LockedPointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; /** * @short Wrapper for zwp_confined_pointer_v1 protocol * The confine pointer interface represents a confined pointer state. * * This object will send the signal 'confined' when the confinement is * activated. Whenever the confinement is activated, it is guaranteed that * the surface the pointer is confined to will already have received pointer * focus and that the pointer will be within the region passed to the request * creating this object. It is up to the compositor to decide whether this * requires some user interaction and if the pointer will warp to within the * passed region if outside. * * To unconfine the pointer, delete the object. * * If the compositor decides to unconfine the pointer the unconfined signal is * emitted. The ConfinedPointer object is at this point defunct and should * be deleted. * @see PointerConstraints::confinePointer * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT ConfinedPointer : public QObject { Q_OBJECT public: virtual ~ConfinedPointer(); /** * Setup this ConfinedPointer to manage the @p confinedpointer. * When using PointerConstraints::createConfinedPointer there is no need to call this * method. **/ void setup(zwp_confined_pointer_v1 *confinedpointer); /** * @returns @c true if managing a zwp_confined_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_confined_pointer_v1 interface. * After the interface has been released the ConfinedPointer instance is no * longer valid and can be setup with another zwp_confined_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this ConfinedPointer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_confined_pointer_v1 interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * PointerConstraints gets destroyed. * * @see release **/ void destroy(); /** * Set a new region used to confine the pointer. * * The new confine region is double-buffered. The new confine region will * only take effect when the associated Surface gets its pending state * applied. See {@link Surface::commit} for details. * * If the confinement is active when the new confinement region is applied * and the pointer ends up outside of newly applied region, the pointer may * warped to a position within the new confinement region. If warped, a * {@link Pointer::motion} signal will be emitted, but no * {@link RelativePointer::relativeMotion} signal. * * The compositor may also, instead of using the new region, unconfine the * pointer. * * @param region The new confine region. * @see Surface::commit * @see PointerConstraints::confinePointer **/ void setRegion(Region *region); operator zwp_confined_pointer_v1*(); operator zwp_confined_pointer_v1*() const; Q_SIGNALS: /** * Notification that the pointer confinement of the seat's pointer is activated. * @see unconfined **/ void confined(); /** * Notification that the pointer confinement of the seat's pointer is no * longer active. If this is a oneshot pointer confinement (see * wp_pointer_constraints.lifetime) this object is now defunct and should * be destroyed. If this is a persistent pointer confinement (see * wp_pointer_constraints.lifetime) this pointer confinement may again * reactivate in the future. * @see confined **/ void unconfined(); private: friend class PointerConstraints; explicit ConfinedPointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/pointergestures.cpp b/src/client/pointergestures.cpp index a876175..a6c2b10 100644 --- a/src/client/pointergestures.cpp +++ b/src/client/pointergestures.cpp @@ -1,354 +1,340 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointergestures.h" #include "pointer.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PointerGestures::Private { public: Private() = default; WaylandPointer pointergestures; EventQueue *queue = nullptr; }; PointerGestures::PointerGestures(QObject *parent) : QObject(parent) , d(new Private) { } PointerGestures::~PointerGestures() { release(); } void PointerGestures::setup(zwp_pointer_gestures_v1 *pointergestures) { Q_ASSERT(pointergestures); Q_ASSERT(!d->pointergestures); d->pointergestures.setup(pointergestures); } void PointerGestures::release() { d->pointergestures.release(); } void PointerGestures::destroy() { d->pointergestures.destroy(); } PointerGestures::operator zwp_pointer_gestures_v1*() { return d->pointergestures; } PointerGestures::operator zwp_pointer_gestures_v1*() const { return d->pointergestures; } bool PointerGestures::isValid() const { return d->pointergestures.isValid(); } void PointerGestures::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PointerGestures::eventQueue() { return d->queue; } PointerSwipeGesture *PointerGestures::createSwipeGesture(Pointer *pointer, QObject *parent) { Q_ASSERT(isValid()); PointerSwipeGesture *p = new PointerSwipeGesture(parent); auto w = zwp_pointer_gestures_v1_get_swipe_gesture(d->pointergestures, *pointer); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } PointerPinchGesture *PointerGestures::createPinchGesture(Pointer *pointer, QObject *parent) { Q_ASSERT(isValid()); PointerPinchGesture *p = new PointerPinchGesture(parent); auto w = zwp_pointer_gestures_v1_get_pinch_gesture(d->pointergestures, *pointer); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN PointerSwipeGesture::Private { public: Private(PointerSwipeGesture *q); void setup(zwp_pointer_gesture_swipe_v1 *pg); WaylandPointer pointerswipegesture; quint32 fingerCount = 0; QPointer surface; private: static void beginCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers); static void updateCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy); static void endCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, int32_t cancelled); PointerSwipeGesture *q; static const zwp_pointer_gesture_swipe_v1_listener s_listener; }; const zwp_pointer_gesture_swipe_v1_listener PointerSwipeGesture::Private::s_listener = { beginCallback, updateCallback, endCallback }; void PointerSwipeGesture::Private::beginCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); p->fingerCount = fingers; p->surface = QPointer(Surface::get(surface)); emit p->q->started(serial, time); } void PointerSwipeGesture::Private::updateCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); emit p->q->updated(QSizeF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)), time); } void PointerSwipeGesture::Private::endCallback(void *data, zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, int32_t cancelled) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerswipegesture == zwp_pointer_gesture_swipe_v1); if (cancelled) { emit p->q->cancelled(serial, time); } else { emit p->q->ended(serial, time); } p->fingerCount = 0; p->surface.clear(); } PointerSwipeGesture::Private::Private(PointerSwipeGesture *q) : q(q) { } PointerSwipeGesture::PointerSwipeGesture(QObject *parent) : QObject(parent) , d(new Private(this)) { } PointerSwipeGesture::~PointerSwipeGesture() { release(); } quint32 PointerSwipeGesture::fingerCount() const { return d->fingerCount; } QPointer PointerSwipeGesture::surface() const { return d->surface; } void PointerSwipeGesture::Private::setup(zwp_pointer_gesture_swipe_v1 *pg) { Q_ASSERT(pg); Q_ASSERT(!pointerswipegesture); pointerswipegesture.setup(pg); zwp_pointer_gesture_swipe_v1_add_listener(pointerswipegesture, &s_listener, this); } void PointerSwipeGesture::setup(zwp_pointer_gesture_swipe_v1 *pointerswipegesture) { d->setup(pointerswipegesture); } void PointerSwipeGesture::release() { d->pointerswipegesture.release(); } void PointerSwipeGesture::destroy() { d->pointerswipegesture.destroy(); } PointerSwipeGesture::operator zwp_pointer_gesture_swipe_v1*() { return d->pointerswipegesture; } PointerSwipeGesture::operator zwp_pointer_gesture_swipe_v1*() const { return d->pointerswipegesture; } bool PointerSwipeGesture::isValid() const { return d->pointerswipegesture.isValid(); } class Q_DECL_HIDDEN PointerPinchGesture::Private { public: Private(PointerPinchGesture *q); void setup(zwp_pointer_gesture_pinch_v1 *pg); WaylandPointer pointerpinchgesture; quint32 fingerCount = 0; QPointer surface; private: static void beginCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers); static void updateCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation); static void endCallback(void *data, zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled); PointerPinchGesture *q; static const zwp_pointer_gesture_pinch_v1_listener s_listener; }; const zwp_pointer_gesture_pinch_v1_listener PointerPinchGesture::Private::s_listener = { beginCallback, updateCallback, endCallback }; void PointerPinchGesture::Private::beginCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t serial, uint32_t time, wl_surface *surface, uint32_t fingers) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerpinchgesture == pg); p->fingerCount = fingers; p->surface = QPointer(Surface::get(surface)); emit p->q->started(serial, time); } void PointerPinchGesture::Private::updateCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerpinchgesture == pg); emit p->q->updated(QSizeF(wl_fixed_to_double(dx), wl_fixed_to_double(dy)), wl_fixed_to_double(scale), wl_fixed_to_double(rotation), time); } void PointerPinchGesture::Private::endCallback(void *data, zwp_pointer_gesture_pinch_v1 *pg, uint32_t serial, uint32_t time, int32_t cancelled) { auto p = reinterpret_cast(data); Q_ASSERT(p->pointerpinchgesture == pg); if (cancelled) { emit p->q->cancelled(serial, time); } else { emit p->q->ended(serial, time); } p->fingerCount = 0; p->surface.clear(); } PointerPinchGesture::Private::Private(PointerPinchGesture *q) : q(q) { } PointerPinchGesture::PointerPinchGesture(QObject *parent) : QObject(parent) , d(new Private(this)) { } PointerPinchGesture::~PointerPinchGesture() { release(); } void PointerPinchGesture::Private::setup(zwp_pointer_gesture_pinch_v1 *pg) { Q_ASSERT(pg); Q_ASSERT(!pointerpinchgesture); pointerpinchgesture.setup(pg); zwp_pointer_gesture_pinch_v1_add_listener(pointerpinchgesture, &s_listener, this); } void PointerPinchGesture::setup(zwp_pointer_gesture_pinch_v1 *pointerpinchgesture) { d->setup(pointerpinchgesture); } void PointerPinchGesture::release() { d->pointerpinchgesture.release(); } void PointerPinchGesture::destroy() { d->pointerpinchgesture.destroy(); } PointerPinchGesture::operator zwp_pointer_gesture_pinch_v1*() { return d->pointerpinchgesture; } PointerPinchGesture::operator zwp_pointer_gesture_pinch_v1*() const { return d->pointerpinchgesture; } bool PointerPinchGesture::isValid() const { return d->pointerpinchgesture.isValid(); } quint32 PointerPinchGesture::fingerCount() const { return d->fingerCount; } QPointer PointerPinchGesture::surface() const { return d->surface; } } } diff --git a/src/client/pointergestures.h b/src/client/pointergestures.h index d99f2c4..32f78d5 100644 --- a/src/client/pointergestures.h +++ b/src/client/pointergestures.h @@ -1,415 +1,401 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_POINTERGESTURES_H #define KWAYLAND_CLIENT_POINTERGESTURES_H #include #include #include struct zwp_pointer_gestures_v1; struct zwp_pointer_gesture_swipe_v1; struct zwp_pointer_gesture_pinch_v1; class QSizeF; namespace KWayland { namespace Client { class EventQueue; class PointerPinchGesture; class Pointer; class Surface; class PointerSwipeGesture; /** * @short Wrapper for the zwp_pointer_gestures_v1 interface. * * This class provides a convenient wrapper for the zwp_pointer_gestures_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the PointerGestures interface: * @code * PointerGestures *c = registry->createPointerGestures(name, version); * @endcode * * This creates the PointerGestures and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PointerGestures *c = new PointerGestures; * c->setup(registry->bindPointerGestures(name, version)); * @endcode * * The PointerGestures can be used as a drop-in replacement for any zwp_pointer_gestures_v1 * pointer as it provides matching cast operators. * * @see Registry * @see PointerSwipeGesture * @see PointerPinchGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerGestures : public QObject { Q_OBJECT public: /** * Creates a new PointerGestures. * Note: after constructing the PointerGestures it is not yet valid and one needs * to call setup. In order to get a ready to use PointerGestures prefer using * Registry::createPointerGestures. **/ explicit PointerGestures(QObject *parent = nullptr); virtual ~PointerGestures(); /** * Setup this PointerGestures to manage the @p pointergestures. * When using Registry::createPointerGestures there is no need to call this * method. **/ void setup(zwp_pointer_gestures_v1 *pointergestures); /** * @returns @c true if managing a zwp_pointer_gestures_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gestures_v1 interface. * After the interface has been released the PointerGestures instance is no * longer valid and can be setup with another zwp_pointer_gestures_v1 interface. **/ void release(); /** * Destroys the data held by this PointerGestures. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_pointer_gestures_v1 interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * PointerGestures gets destroyed. * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this PointerGestures. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this PointerGestures. **/ EventQueue *eventQueue(); /** * Creates a PointerSwipeGesture for the given @p pointer with the @p parent. **/ PointerSwipeGesture *createSwipeGesture(Pointer *pointer, QObject *parent = nullptr); /** * Creates a PointerPinchGesture for the given @p pointer with the @p parent. **/ PointerPinchGesture *createPinchGesture(Pointer *pointer, QObject *parent = nullptr); operator zwp_pointer_gestures_v1*(); operator zwp_pointer_gestures_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the PointerGestures got created by * Registry::createPointerGestures **/ void removed(); private: class Private; QScopedPointer d; }; /** * This class is a wrapper for the zwp_pointer_gesture_swipe_v1 protocol. * * A PointerSwipeGesture object notifies a client about a multi-finger swipe * gesture detected on an indirect input device such as a touchpad. * The gesture is usually initiated by multiple fingers moving in the * same direction but once initiated the direction may change. * The precise conditions of when such a gesture is detected are * implementation-dependent. * * A gesture consists of three stages: begin, update (optional) and end. * There cannot be multiple simultaneous pinch or swipe gestures on the * same pointer/seat, how compositors prevent these situations is * implementation-dependent. * * A gesture may be cancelled by the compositor or the hardware. * Clients should not consider performing permanent or irreversible * actions until the end of a gesture has been received. * * @see PointerGestures * @see PointerPinchGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerSwipeGesture : public QObject { Q_OBJECT public: virtual ~PointerSwipeGesture(); /** * Setup this PointerSwipeGesture to manage the @p pointerswipegesture. * When using PointerGestures::createPointerSwipeGesture there is no need to call this * method. **/ void setup(zwp_pointer_gesture_swipe_v1 *pointerswipegesture); /** * @returns @c true if managing a zwp_pointer_gesture_swipe_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gesture_swipe_v1 interface. * After the interface has been released the PointerSwipeGesture instance is no * longer valid and can be setup with another zwp_pointer_gesture_swipe_v1 interface. **/ void release(); /** * Destroys the data held by this PointerSwipeGesture. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_pointer_gesture_swipe_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerswipegesture, &PointerSwipeGesture::destroy); * @endcode * * @see release **/ void destroy(); /** * The number of fingers taking part in this gesture. * If no gesture is in progress @c 0 is returned. **/ quint32 fingerCount() const; /** * The Surface on which this gesture is performed. * If no gesture is in progress the returned pointer is null. **/ QPointer surface() const; operator zwp_pointer_gesture_swipe_v1*(); operator zwp_pointer_gesture_swipe_v1*() const; Q_SIGNALS: /** * A gesture got started. * @param serial Unique serial for this start gesture event. * @param time Timestamp in milliseconds granularity * @see updated * @see ended * @see cancelled **/ void started(quint32 serial, quint32 time); /** * A gesture got updated. * @param delta relative coordinates of the logical center of the gesture compared to the previous event * @param time Timestamp in milliseconds granularity * @see started * @see ended * @see cancelled **/ void updated(const QSizeF &delta, quint32 time); /** * A gesture ended. * * @param serial Unique serial for this end gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see cancelled **/ void ended(quint32 serial, quint32 time); /** * A gesture got cancelled by the Wayland compositor. * * @param serial Unique serial for this cancel gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see ended **/ void cancelled(quint32 serial, quint32 time); private: friend class PointerGestures; explicit PointerSwipeGesture(QObject *parent = nullptr); class Private; QScopedPointer d; }; /** * This class is a wrapper for the zwp_pointer_gesture_pinch_v1 protocol. * * A PointerPinchGesture object notifies a client about a multi-finger pinch * gesture detected on an indirect input device such as a touchpad. * The gesture is usually initiated by multiple fingers moving towards * each other or away from each other, or by two or more fingers rotating * around a logical center of gravity. The precise conditions of when * such a gesture is detected are implementation-dependent. * * A gesture consists of three stages: begin, update (optional) and end. * There cannot be multiple simultaneous pinch or swipe gestures on the * same pointer/seat, how compositors prevent these situations is * implementation-dependent. * * A gesture may be cancelled by the compositor or the hardware. * Clients should not consider performing permanent or irreversible * actions until the end of a gesture has been received. * * @see PointerGestures * @see PointerSwipeGesture * @since 5.29 **/ class KWAYLANDCLIENT_EXPORT PointerPinchGesture : public QObject { Q_OBJECT public: virtual ~PointerPinchGesture(); /** * Setup this PointerPinchGesture to manage the @p pointerpinchgesture. * When using PointerGestures::createPointerPinchGesture there is no need to call this * method. **/ void setup(zwp_pointer_gesture_pinch_v1 *pointerpinchgesture); /** * @returns @c true if managing a zwp_pointer_gesture_pinch_v1. **/ bool isValid() const; /** * Releases the zwp_pointer_gesture_pinch_v1 interface. * After the interface has been released the PointerPinchGesture instance is no * longer valid and can be setup with another zwp_pointer_gesture_pinch_v1 interface. **/ void release(); /** * Destroys the data held by this PointerPinchGesture. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_pointer_gesture_pinch_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, pointerpinchgesture, &PointerPinchGesture::destroy); * @endcode * * @see release **/ void destroy(); /** * The number of fingers taking part in this gesture. * If no gesture is in progress @c 0 is returned. **/ quint32 fingerCount() const; /** * The Surface on which this gesture is performed. * If no gesture is in progress the returned pointer is null. **/ QPointer surface() const; operator zwp_pointer_gesture_pinch_v1*(); operator zwp_pointer_gesture_pinch_v1*() const; Q_SIGNALS: /** * A gesture got started. * @param serial Unique serial for this start gesture event. * @param time Timestamp in milliseconds granularity * @see updated * @see ended * @see cancelled **/ void started(quint32 serial, quint32 time); /** * A gesture got updated. * @param delta relative coordinates of the logical center of the gesture compared to the previous event * @param scale an absolute scale compared to the start * @param rotation relative angle in degrees clockwise compared to the previous start or update event. * @param time Timestamp in milliseconds granularity * @see started * @see ended * @see cancelled **/ void updated(const QSizeF &delta, qreal scale, qreal rotation, quint32 time); /** * A gesture ended. * * @param serial Unique serial for this end gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see cancelled **/ void ended(quint32 serial, quint32 time); /** * A gesture got cancelled by the Wayland compositor. * * @param serial Unique serial for this cancel gesture event. * @param time Timestamp in milliseconds granularity * @see started * @see updated * @see ended **/ void cancelled(quint32 serial, quint32 time); private: friend class PointerGestures; explicit PointerPinchGesture(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/protocols/appmenu.xml b/src/client/protocols/appmenu.xml index 2483f72..6e8d33b 100644 --- a/src/client/protocols/appmenu.xml +++ b/src/client/protocols/appmenu.xml @@ -1,47 +1,36 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface allows a client to link a window (or wl_surface) to an com.canonical.dbusmenu interface registered on DBus. The DBus service name and object path where the appmenu interface is present The object should be registered on the session bus before sending this request. If not applicable, clients should remove this object. Set or update the service name and object path. Strings should be formatted in Latin-1 matching the relevant DBus specifications. diff --git a/src/client/protocols/blur.xml b/src/client/protocols/blur.xml index de72f64..477bd72 100644 --- a/src/client/protocols/blur.xml +++ b/src/client/protocols/blur.xml @@ -1,39 +1,28 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> diff --git a/src/client/protocols/contrast.xml b/src/client/protocols/contrast.xml index 7ede4b6..c1794d1 100644 --- a/src/client/protocols/contrast.xml +++ b/src/client/protocols/contrast.xml @@ -1,48 +1,37 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> diff --git a/src/client/protocols/dpms.xml b/src/client/protocols/dpms.xml index c93069d..6839c07 100644 --- a/src/client/protocols/dpms.xml +++ b/src/client/protocols/dpms.xml @@ -1,93 +1,82 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> The Dpms manager allows to get a org_kde_kwin_dpms for a given wl_output. The org_kde_kwin_dpms provides the currently used VESA Display Power Management Signaling state (see https://en.wikipedia.org/wiki/VESA_Display_Power_Management_Signaling ). In addition it allows to request a state change. A compositor is not obliged to honor it and will normally automatically switch back to on state. Factory request to get the org_kde_kwin_dpms for a given wl_output. This interface provides information about the VESA DPMS state for a wl_output. It gets created through the request get on the org_kde_kwin_dpms_manager interface. On creating the resource the server will push whether DPSM is supported for the output, the currently used DPMS state and notifies the client through the done event once all states are pushed. Whenever a state changes the set of changes is committed with the done event. This event gets pushed on binding the resource and indicates whether the wl_output supports DPMS. There are operation modes of a Wayland server where DPMS might not make sense (e.g. nested compositors). This mode gets pushed on binding the resource and provides the currently used DPMS mode. It also gets pushed if DPMS is not supported for the wl_output, in that case the value will be On. The event is also pushed whenever the state changes. This event gets pushed on binding the resource once all other states are pushed. In addition it gets pushed whenever a state changes to tell the client that all state changes have been pushed. Requests that the compositor puts the wl_output into the passed mode. The compositor is not obliged to change the state. In addition the compositor might leave the mode whenever it seems suitable. E.g. the compositor might return to On state on user input. The client should not assume that the mode changed after requesting a new mode. Instead the client should listen for the mode event. diff --git a/src/client/protocols/fake-input.xml b/src/client/protocols/fake-input.xml index 0fea76a..1053383 100644 --- a/src/client/protocols/fake-input.xml +++ b/src/client/protocols/fake-input.xml @@ -1,95 +1,84 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface allows other processes to provide fake input events. Purpose is on the one hand side to provide testing facilities like XTest on X11. But also to support use case like kdeconnect's mouse pad interface. A compositor should not trust the input received from this interface. Clients should not expect that the compositor honors the requests from this interface. A client should use this request to tell the compositor why it wants to use this interface. The compositor might use the information to decide whether it wants to grant the request. The data might also be passed to the user to decide whether the application should get granted access to this very privileged interface. A client should use this request to send touch down event at specific co-ordinates. A client should use this request to send touch motion to specific position. A client should use this request to send touch up event. A client should use this request to cancel the current touch event. A client should use this request to send touch frame event. diff --git a/src/client/protocols/idle.xml b/src/client/protocols/idle.xml index 8a8007f..aa23914 100644 --- a/src/client/protocols/idle.xml +++ b/src/client/protocols/idle.xml @@ -1,49 +1,38 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface allows to monitor user idle time on a given seat. The interface allows to register timers which trigger after no user activity was registered on the seat for a given interval. It notifies when user activity resumes. This is useful for applications wanting to perform actions when the user is not interacting with the system, e.g. chat applications setting the user as away, power management features to dim screen, etc.. diff --git a/src/client/protocols/keystate.xml b/src/client/protocols/keystate.xml index cb855ea..fc7969d 100644 --- a/src/client/protocols/keystate.xml +++ b/src/client/protocols/keystate.xml @@ -1,43 +1,32 @@ + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez - This program 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 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> Keeps track of the states of the different keys that have a state attached to it. diff --git a/src/client/protocols/output-management.xml b/src/client/protocols/output-management.xml index 3b3bd70..a216eca 100644 --- a/src/client/protocols/output-management.xml +++ b/src/client/protocols/output-management.xml @@ -1,195 +1,176 @@ - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. + SPDX-FileCopyrightText: 2008-2011 Kristian Høgsberg + SPDX-FileCopyrightText: 2010-2011 Intel Corporation + SPDX-FileCopyrightText: 2012-2013 Collabora, Ltd. + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: MIT-CMU ]]> This interface enables clients to set properties of output devices for screen configuration purposes via the server. To this end output devices are referenced by global org_kde_kwin_outputdevice objects. outputmanagement (wl_global) -------------------------- request: * create_configuration -> outputconfiguration (wl_resource) outputconfiguration (wl_resource) -------------------------- requests: * enable(outputdevice, bool) * mode(outputdevice, mode_id) * transformation(outputdevice, flag) * position(outputdevice, x, y) * apply events: * applied * failed The server registers one outputmanagement object as a global object. In order to configure outputs a client requests create_configuration, which provides a resource referencing an outputconfiguration for one-time configuration. That way the server knows which requests belong together and can group them by that. On the outputconfiguration object the client calls for each output whether the output should be enabled, which mode should be set (by referencing the mode from the list of announced modes) and the output's global position. Once all outputs are configured that way, the client calls apply. At that point and not earlier the server should try to apply the configuration. If this succeeds the server emits the applied signal, otherwise the failed signal, such that the configuring client is noticed about the success of its configuration request. Through this design the interface enables atomic output configuration changes if internally supported by the server. Request an outputconfiguration object through which the client can configure output devices. outputconfiguration is a client-specific resource that can be used to ask the server to apply changes to available output devices. The client receives a list of output devices from the registry. When it wants to apply new settings, it creates a configuration object from the outputmanagement global, writes changes through this object's enable, scale, transform and mode calls. It then asks the server to apply these settings in an atomic fashion, for example through Linux' DRM interface. The server signals back whether the new settings have applied successfully or failed to apply. outputdevice objects are updated after the changes have been applied to the hardware and before the server side sends the applied event. Mark the output as enabled or disabled. Sets the mode for a given output by its mode size (width and height) and refresh rate. Sets the transformation for a given output. Sets the position for this output device. (x,y) describe the top-left corner of the output in global space, whereby the origin (0,0) of the global space has to be aligned with the top-left corner of the most left and in case this does not define a single one the top output. There may be no gaps or overlaps between outputs, i.e. the outputs are stacked horizontally, vertically, or both on each other. Sets the scaling factor for this output device. Asks the server to apply property changes requested through this outputconfiguration object to all outputs on the server side. Sent after the server has successfully applied the changes. . Sent if the server rejects the changes or failed to apply them. Sets the scaling factor for this output device. Sending both scale and scalef is undefined. Set color curves of output devices through RGB color ramps. Allows color correction of output device from user space. These are the raw values. A compositor might opt to adjust these values internally, for example to shift color temperature at night. diff --git a/src/client/protocols/outputdevice.xml b/src/client/protocols/outputdevice.xml index 21f7cb5..d2836fa 100644 --- a/src/client/protocols/outputdevice.xml +++ b/src/client/protocols/outputdevice.xml @@ -1,295 +1,276 @@ - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. + SPDX-FileCopyrightText: 2008-2011 Kristian Høgsberg + SPDX-FileCopyrightText: 2010-2011 Intel Corporation + SPDX-FileCopyrightText: 2012-2013 Collabora, Ltd. + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: MIT-CMU ]]> An outputdevice describes a display device available to the compositor. outputdevice is similar to wl_output, but focuses on output configuration management. A client can query all global outputdevice objects to enlist all available display devices, even those that may currently not be represented by the compositor as a wl_output. The client sends configuration changes to the server through the outputconfiguration interface, and the server applies the configuration changes to the hardware and signals changes to the outputdevices accordingly. This object is published as global during start up for every available display devices, or when one later becomes available, for example by being hotplugged via a physical connector. This enumeration describes how the physical pixels on an output are laid out. This describes the transform, that a compositor will apply to a surface to compensate for the rotation or mirroring of an output device. The flipped values correspond to an initial flip around a vertical axis followed by rotation. The purpose is mainly to allow clients to render accordingly and tell the compositor, so that for fullscreen surfaces, the compositor is still able to scan out directly client surfaces. The geometry event describes geometric properties of the output. The event is sent when binding to the output object and whenever any of the properties change. These flags describe properties of an output mode. They are used in the flags bitfield of the mode event. The mode event describes an available mode for the output. When the client binds to the outputdevice object, the server sends this event once for every available mode the outputdevice can be operated by. There will always be at least one event sent out on initial binding, which represents the current mode. Later on if an output changes its mode the event is sent again, whereby this event represents the mode that has now become current. In other words, the current mode is always represented by the latest event sent with the current flag set. The size of a mode is given in physical hardware units of the output device. This is not necessarily the same as the output size in the global compositor space. For instance, the output may be scaled, as described in org_kde_kwin_outputdevice.scale, or transformed, as described in org_kde_kwin_outputdevice.transform. The id can be used to refer to a mode when calling set_mode on an org_kde_kwin_outputconfiguration object. This event is sent after all other properties have been sent on binding to the output object as well as after any other output property change have been applied later on. This allows to see changes to the output properties as atomic, even if multiple events successively announce them. This event contains scaling geometry information that is not in the geometry event. It may be sent after binding the output object or if the output scale changes later. If it is not sent, the client should assume a scale of 1. A scale larger than 1 means that the compositor will automatically scale surface buffers by this amount when rendering. This is used for high resolution displays where applications rendering at the native resolution would be too small to be legible. It is intended that scaling aware clients track the current output of a surface, and if it is on a scaled output it should use wl_surface.set_buffer_scale with the scale of the output. That way the compositor can avoid scaling the surface, and the client can supply a higher detail image. The edid event encapsulates the EDID data for the outputdevice. The event is sent when binding to the output object. The EDID data may be empty, in which case this event is sent anyway. If the EDID information is empty, you can fall back to the name et al. properties of the outputdevice. Describes whether a device is enabled, i.e. device is used to display content by the compositor. This wraps a boolean around an int to avoid a boolean trap. The enabled event notifies whether this output is currently enabled and used for displaying content by the server. The event is sent when binding to the output object and whenever later on an output changes its state by becoming enabled or disabled. The uuid can be used to identify the output. It's controlled by the server entirely. The server should make sure the uuid is persistent across restarts. An empty uuid is considered invalid. This event contains scaling geometry information that is not in the geometry event. It may be sent after binding the output object or if the output scale changes later. If it is not sent, the client should assume a scale of 1. A scale larger than 1 means that the compositor will automatically scale surface buffers by this amount when rendering. This is used for high resolution displays where applications rendering at the native resolution would be too small to be legible. It is intended that scaling aware clients track the current output of a surface, and if it is on a scaled output it should use wl_surface.set_buffer_scale with the scale of the output. That way the compositor can avoid scaling the surface, and the client can supply a higher detail image. wl_output will keep the output scale as an integer. In every situation except configuring the window manager you want to use that. Describes the color intensity profile of the output. Commonly used for gamma/color correction. The array contains all color ramp values of the output. For example on 8bit screens there are 256 of them. The array elements are unsigned 16bit integers. Serial ID of the monitor, sent on startup before the first done event. EISA ID of the monitor, sent on startup before the first done event. diff --git a/src/client/protocols/plasma-effects.xml b/src/client/protocols/plasma-effects.xml index 0fb758b..5af3862 100644 --- a/src/client/protocols/plasma-effects.xml +++ b/src/client/protocols/plasma-effects.xml @@ -1,75 +1,64 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> Ask the compositor to move the surface from a location to another with a slide animation. The from argument provides a clue about where the slide animation begins, destination coordinates are specified with x and y. This request sets the region of the surface that will allow to see through with a blur effect. Pass a null region to disable blur behind. This request sets the region of the surface with a different contrast. Pass a null region to disable this effect. When a null region is passed the contrast, intensity and saturation arguments are not taken into account. The contrast, intensity and saturation parameters are in the 0-255 range. diff --git a/src/client/protocols/plasma-shell.xml b/src/client/protocols/plasma-shell.xml index e6e0612..2bffbd9 100644 --- a/src/client/protocols/plasma-shell.xml +++ b/src/client/protocols/plasma-shell.xml @@ -1,396 +1,385 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface is used by KF5 powered Wayland shells to communicate with the compositor and can only be bound one time. Create a shell surface for an existing surface. Only one shell surface can be associated with a given surface. An interface that may be implemented by a wl_surface, for implementations that provide the shell user interface. It provides requests to set surface roles, assign an output or set the position in output coordinates. On the server side the object is automatically destroyed when the related wl_surface is destroyed. On client side, org_kde_plasma_surface.destroy() must be called before destroying the wl_surface object. The org_kde_plasma_surface interface is removed from the wl_surface object that was turned into a shell surface with the org_kde_plasma_shell.get_surface request. The shell surface role is lost and wl_surface is unmapped. Assign an output to this shell surface. The compositor will use this information to set the position when org_kde_plasma_surface.set_position request is called. Move the surface to new coordinates. Coordinates are global, for example 50,50 for a 1920,0+1920x1080 output is 1970,50 in global coordinates space. Use org_kde_plasma_surface.set_output to assign an output to this surface. Assign a role to a shell surface. The compositor handles surfaces depending on their role. See the explanation below. This request fails if the surface already has a role, this means the surface role may be assigned only once. == Surfaces with splash role == Splash surfaces are placed above every other surface during the shell startup phase. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. These surfaces are meant to hide the desktop during the startup phase so that the user will always see a ready to work desktop. A shell might not create splash surfaces if the compositor reveals the desktop in an alternative fashion, for example with a fade in effect. That depends on how much time the desktop usually need to prepare the workspace or specific design decisions. This specification doesn't impose any particular design. When the startup phase is finished, the shell will send the org_kde_plasma.desktop_ready request to the compositor. == Surfaces with desktop role == Desktop surfaces are placed below all other surfaces and are used to show the actual desktop view with icons, search results or controls the user will interact with. What to show depends on the shell implementation. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. Only one surface per output can have the desktop role. == Surfaces with dashboard role == Dashboard surfaces are placed above desktop surfaces and are used to show additional widgets and controls. The surfaces are placed according to the output coordinates. No size is imposed to those surfaces, the shell has to resize them according to output size. Only one surface per output can have the dashboard role. == Surfaces with config role == A configuration surface is shown when the user wants to configure panel or desktop views. Only one surface per output can have the config role. TODO: This should grab the input like popup menus, right? == Surfaces with overlay role == Overlays are special surfaces that shows for a limited amount of time. Such surfaces are useful to display things like volume, brightness and status changes. Compositors may decide to show those surfaces in a layer above all surfaces, even full screen ones if so is desired. == Surfaces with notification role == Notification surfaces display informative content for a limited amount of time. The compositor may decide to show them in a corner depending on the configuration. These surfaces are shown in a layer above all other surfaces except for full screen ones. == Surfaces with lock role == The lock surface is shown by the compositor when the session is locked, users interact with it to unlock the session. Compositors should move lock surfaces to 0,0 in output coordinates space and hide all other surfaces for security sake. For the same reason it is recommended that clients make the lock surface as big as the screen. Only one surface per output can have the lock role. The panel is on top of other surfaces, windows cannot cover (full screen windows excluded). The panel is hidden automatically and restored when the mouse is over. Windows can cover the panel. Maximized windows take the whole screen space but the panel is above the windows. Set flags bitmask as described by the flag enum. Pass 0 to unset any flag, the surface will adjust its behavior to the default. Setting this bit to the window, will make it say it prefers to not be listed in the taskbar. Taskbar implementations may or may not follow this hint. A panel surface with panel_behavior auto_hide can perform this request to hide the panel on a screen edge without unmapping it. The compositor informs the client about the panel being hidden with the event auto_hidden_panel_hidden. The compositor will restore the visibility state of the surface when the pointer touches the screen edge the panel borders. Once the compositor restores the visibility the event auto_hidden_panel_shown will be sent. This event will also be sent if the compositor is unable to hide the panel. The client can also request to show the panel again with the request panel_auto_hide_show. A panel surface with panel_behavior auto_hide can perform this request to show the panel again which got hidden with panel_auto_hide_hide. By default various org_kde_plasma_surface roles do not take focus and cannot be activated. With this request the compositor can be instructed to pass focus also to this org_kde_plasma_surface. An auto-hiding panel got hidden by the compositor. An auto-hiding panel got shown by the compositor. Setting this bit will indicate that the window prefers not to be listed in a switcher. diff --git a/src/client/protocols/plasma-virtual-desktop.xml b/src/client/protocols/plasma-virtual-desktop.xml index 1496d31..ea9f93a 100644 --- a/src/client/protocols/plasma-virtual-desktop.xml +++ b/src/client/protocols/plasma-virtual-desktop.xml @@ -1,121 +1,110 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id; Ask the server to create a new virtual desktop, and position it at a specified position. If the position is zero or less, it will be positioned at the beginning, if the cosition is the count or more, it will be positioned at the end. Ask the server to get rid of a virtual desktop, the server may or may not acconsent to the request. This event is sent after all other properties has been sent after binding to the desktop manager object and after any other property changes done after that. This allows changes to the org_kde_plasma_virtual_desktop_management properties to be seen as atomic, even if they happen via multiple events. Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system. The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation. The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen. Windows associated to this virtual desktop will be shown. Windows that were associated only to this desktop will be hidden. This event is sent after all other properties has been sent after binding to the desktop object and after any other property changes done after that. This allows changes to the org_kde_plasma_virtual_desktop properties to be seen as atomic, even if they happen via multiple events. This virtual desktop has just been removed by the server: All windows will lose the association to this desktop. diff --git a/src/client/protocols/plasma-window-management.xml b/src/client/protocols/plasma-window-management.xml index 684b1ea..332bc04 100644 --- a/src/client/protocols/plasma-window-management.xml +++ b/src/client/protocols/plasma-window-management.xml @@ -1,324 +1,313 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface manages application windows. It provides requests to show and hide the desktop and emits an event every time a window is created so that the client can use it to manage the window. Only one client can bind this interface at a time. Tell the compositor to show/hide the desktop. This event will be sent whenever the show desktop mode changes. E.g. when it is entered or left. On binding the interface the current state is sent. This event will be sent immediately after a window is mapped. Manages and control an application window. Only one client can bind this interface at a time. Set window state. Values for state argument are described by org_kde_plasma_window_management.state and can be used together in a bitfield. The flags bitfield describes which flags are supposed to be set, the state bitfield the value for the set flags Deprecated: use enter_virtual_desktop Maps the window to a different virtual desktop. To show the window on all virtual desktops, call the org_kde_plasma_window.set_state request and specify a on_all_desktops state in the bitfield. Sets the geometry of the taskbar entry for this window. The geometry is relative to a panel in particular. Remove the task geometry information for a particular panel. Close this window. Request an interactive move for this window. Request an interactive resize for this window. Removes the resource bound for this org_kde_plasma_window. The compositor will write the window icon into the provided file descriptor. The data is a serialized QIcon with QDataStream. This event will be sent as soon as the window title is changed. This event will be sent as soon as the application identifier is changed. This event will be sent as soon as the window state changes. Values for state argument are described by org_kde_plasma_window_management.state. DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead This event will be sent when a window is moved to another virtual desktop. It is not sent if it becomes visible on all virtual desktops though. This event will be sent whenever the themed icon name changes. May be null. This event will be sent immediately after the window is closed and its surface is unmapped. This event will be sent immediately after all initial state been sent to the client. If the Plasma window is already unmapped, the unmapped event will be sent before the initial_state event. This event will be sent whenever the parent window of this org_kde_plasma_window changes. The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a transient window to the parent window. If the parent argument is null, this org_kde_plasma_window does not have a parent window. This event will be sent whenever the window geometry of this org_kde_plasma_window changes. The coordinates are in absolute coordinates of the windowing system. This event will be sent whenever the icon of the window changes, but there is no themed icon name. Common examples are Xwayland windows which have a pixmap based icon. The client can request the icon using get_icon. This event will be sent when the compositor has set the process id this window belongs to. This should be set once before the initial_state is sent. Make the window enter a virtual desktop. A window can enter more than one virtual desktop. if the id is empty or invalid, no action will be performed. RFC: do this with an empty id to request_enter_virtual_desktop? Make the window enter a new virtual desktop. If the server consents the request, it will create a new virtual desktop and assign the window to it. Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops. This event will be sent after the application menu for the window has changed. diff --git a/src/client/protocols/remote-access.xml b/src/client/protocols/remote-access.xml index 98adb3e..146d3a9 100644 --- a/src/client/protocols/remote-access.xml +++ b/src/client/protocols/remote-access.xml @@ -1,49 +1,38 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> diff --git a/src/client/protocols/server-decoration-palette.xml b/src/client/protocols/server-decoration-palette.xml index 26e50ac..2f29f16 100644 --- a/src/client/protocols/server-decoration-palette.xml +++ b/src/client/protocols/server-decoration-palette.xml @@ -1,44 +1,33 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> l This interface allows a client to alter the palette of a server side decoration. This interface allows a client to alter the palette of a server side decoration. Color scheme that should be applied to the window decoration. Absolute file path, or name of palette in the user's config directory. The server may choose not to follow the requested style. diff --git a/src/client/protocols/server-decoration.xml b/src/client/protocols/server-decoration.xml index 7ea135a..b70bec1 100644 --- a/src/client/protocols/server-decoration.xml +++ b/src/client/protocols/server-decoration.xml @@ -1,96 +1,85 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> This interface allows to coordinate whether the server should create a server-side window decoration around a wl_surface representing a shell surface (wl_shell_surface or similar). By announcing support for this interface the server indicates that it supports server side decorations. Use in conjunction with zxdg_decoration_manager_v1 is undefined. When a client creates a server-side decoration object it indicates that it supports the protocol. The client is supposed to tell the server whether it wants server-side decorations or will provide client-side decorations. If the client does not create a server-side decoration object for a surface the server interprets this as lack of support for this protocol and considers it as client-side decorated. Nevertheless a client-side decorated surface should use this protocol to indicate to the server that it does not want a server-side deco. This event is emitted directly after binding the interface. It contains the default mode for the decoration. When a new server decoration object is created this new object will be in the default mode until the first request_mode is requested. The server may change the default mode at any time. This event is emitted directly after the decoration is created and represents the base decoration policy by the server. E.g. a server which wants all surfaces to be client-side decorated will send Client, a server which wants server-side decoration will send Server. The client can request a different mode through the decoration request. The server will acknowledge this by another event with the same mode. So even if a server prefers server-side decoration it's possible to force a client-side decoration. The server may emit this event at any time. In this case the client can again request a different mode. It's the responsibility of the server to prevent a feedback loop. diff --git a/src/client/protocols/shadow.xml b/src/client/protocols/shadow.xml index dfa9aa2..0efcb80 100644 --- a/src/client/protocols/shadow.xml +++ b/src/client/protocols/shadow.xml @@ -1,81 +1,70 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> Destroy the org_kde_kwin_shadow_manager object. Destroy the org_kde_kwin_shadow object. If the org_kde_kwin_shadow is still set on a wl_surface the shadow will be immediately removed. Prefer to first call the request unset on the org_kde_kwin_shadow_manager and commit the wl_surface to apply the change. diff --git a/src/client/protocols/slide.xml b/src/client/protocols/slide.xml index fe12b37..d5d35fb 100644 --- a/src/client/protocols/slide.xml +++ b/src/client/protocols/slide.xml @@ -1,55 +1,44 @@ . + SPDX-License-Identifier: LGPL-2.1-or-later ]]> Ask the compositor to move the surface from a location to another with a slide animation. The from argument provides a clue about where the slide animation begins, offset is the distance from screen edge to begin the animation. diff --git a/src/client/protocols/surface-extension.xml b/src/client/protocols/surface-extension.xml index 680a6ac..4a420d9 100644 --- a/src/client/protocols/surface-extension.xml +++ b/src/client/protocols/surface-extension.xml @@ -1,95 +1,65 @@ - Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). - Contact: http://www.qt-project.org/legal + This file is part of the plugins of the Qt Toolkit. + SPDX-FileCopyrightText: 2012 Digia Plc and/or its subsidiary(-ies). + Contact: http://www.qt-project.org/legal - This file is part of the plugins of the Qt Toolkit. - - $QT_BEGIN_LICENSE:BSD$ - You may use this file under the terms of the BSD license as follows: - - "Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names - of its contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - - $QT_END_LICENSE$ + SPDX-License-Identifier: BSD-3-Clause diff --git a/src/client/protocols/text-input-unstable-v2.xml b/src/client/protocols/text-input-unstable-v2.xml index dbc7abd..ecec5f1 100644 --- a/src/client/protocols/text-input-unstable-v2.xml +++ b/src/client/protocols/text-input-unstable-v2.xml @@ -1,478 +1,459 @@ - Copyright © 2012, 2013 Intel Corporation - Copyright © 2015, 2016 Jan Arne Petersen - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. + SPDX-FileCopyrightText: 2012, 2013 Intel Corporation + SPDX-FileCopyrightText: 2015, 2016 Jan Arne Petersen + + SPDX-License-Identifier: MIT-CMU The zwp_text_input_v2 interface represents text input and input methods associated with a seat. It provides enter/leave events to follow the text input focus for a seat. Requests are used to enable/disable the text-input object and set state information like surrounding and selected text or the content type. The information about the entered text is sent to the text-input object via the pre-edit and commit events. Using this interface removes the need for applications to directly process hardware key events and compose text out of them. Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices have to always point to the first byte of an UTF-8 encoded code point. Lengths are not allowed to contain just a part of an UTF-8 encoded code point. State is sent by the state requests (set_surrounding_text, set_content_type, set_cursor_rectangle and set_preferred_language) and an update_state request. After an enter or an input_method_change event all state information is invalidated and needs to be resent from the client. A reset or entering a new widget on client side also invalidates all current state information. Destroy the wp_text_input object. Also disables all surfaces enabled through this wp_text_input object Enable text input in a surface (usually when a text entry inside of it has focus). This can be called before or after a surface gets text (or keyboard) focus via the enter event. Text input to a surface is only active when it has the current text (or keyboard) focus and is enabled. Disable text input in a surface (typically when there is no focus on any text entry inside the surface). Requests input panels (virtual keyboard) to show. This should be used for example to show a virtual keyboard again (with a tap) after it was closed by pressing on a close button on the keyboard. Requests input panels (virtual keyboard) to hide. Sets the plain surrounding text around the input position. Text is UTF-8 encoded. Cursor is the byte offset within the surrounding text. Anchor is the byte offset of the selection anchor within the surrounding text. If there is no selected text, anchor is the same as cursor. Make sure to always send some text before and after the cursor except when the cursor is at the beginning or end of text. When there was a configure_surrounding_text event take the before_cursor and after_cursor arguments into account for picking how much surrounding text to send. There is a maximum length of wayland messages so text can not be longer than 4000 bytes. Content hint is a bitmask to allow to modify the behavior of the text input. The content purpose allows to specify the primary purpose of a text input. This allows an input method to show special purpose input panels with extra characters or to disallow some characters. Sets the content purpose and content hint. While the purpose is the basic purpose of an input field, the hint flags allow to modify some of the behavior. When no content type is explicitly set, a normal content purpose with none hint should be assumed. Sets the cursor outline as a x, y, width, height rectangle in surface local coordinates. Allows the compositor to put a window with word suggestions near the cursor. Sets a specific language. This allows for example a virtual keyboard to show a language specific layout. The "language" argument is a RFC-3066 format language tag. It could be used for example in a word processor to indicate language of currently edited document or in an instant message application which tracks languages of contacts. Defines the reason for sending an updated state. Allows to atomically send state updates from client. This request should follow after a batch of state updating requests like set_surrounding_text, set_content_type, set_cursor_rectangle and set_preferred_language. The flags field indicates why an updated state is sent to the input method. Reset should be used by an editor widget after the text was changed outside of the normal input method flow. For "change" it is enough to send the changed state, else the full state should be send. Serial should be set to the serial from the last enter or input_method_changed event. To make sure to not receive outdated input method events after a reset or switching to a new widget wl_display_sync() should be used after update_state in these cases. Notification that this seat's text-input focus is on a certain surface. When the seat has the keyboard capability the text-input focus follows the keyboard focus. Notification that this seat's text-input focus is no longer on a certain surface. The leave notification is sent before the enter notification for the new focus. When the seat has the keyboard capabillity the text-input focus follows the keyboard focus. Notification that the visibility of the input panel (virtual keyboard) changed. The rectangle x, y, width, height defines the area overlapped by the input panel (virtual keyboard) on the surface having the text focus in surface local coordinates. That can be used to make sure widgets are visible and not covered by a virtual keyboard. Notify when a new composing text (pre-edit) should be set around the current cursor position. Any previously set composing text should be removed. The commit text can be used to replace the composing text in some cases (for example when losing focus). The text input should also handle all preedit_style and preedit_cursor events occurring directly before preedit_string. Sets styling information on composing text. The style is applied for length bytes from index relative to the beginning of the composing text (as byte offset). Multiple styles can be applied to a composing text by sending multiple preedit_styling events. This event is handled as part of a following preedit_string event. Sets the cursor position inside the composing text (as byte offset) relative to the start of the composing text. When index is a negative number no cursor is shown. When no preedit_cursor event is sent the cursor will be at the end of the composing text by default. This event is handled as part of a following preedit_string event. Notify when text should be inserted into the editor widget. The text to commit could be either just a single character after a key press or the result of some composing (pre-edit). It could be also an empty text when some text should be removed (see delete_surrounding_text) or when the input cursor should be moved (see cursor_position). Any previously set composing text should be removed. Notify when the cursor or anchor position should be modified. This event should be handled as part of a following commit_string event. The text between anchor and index should be selected. Notify when the text around the current cursor position should be deleted. BeforeLength and afterLength is the length (in bytes) of text before and after the current cursor position (excluding the selection) to delete. This event should be handled as part of a following commit_string or preedit_string event. Transfer an array of 0-terminated modifiers names. The position in the array is the index of the modifier as used in the modifiers bitmask in the keysym event. Notify when a key event was sent. Key events should not be used for normal text input operations, which should be done with commit_string, delete_surrounding_text, etc. The key event follows the wl_keyboard key event convention. Sym is a XKB keysym, state a wl_keyboard key_state. Modifiers are a mask for effective modifiers (where the modifier indices are set by the modifiers_map event) Sets the language of the input text. The "language" argument is a RFC-3066 format language tag. Sets the text direction of input text. It is mainly needed for showing input cursor on correct side of the editor when there is no input yet done and making sure neutral direction text is laid out properly. Configure what amount of surrounding text is expected by the input method. The surrounding text will be sent in the set_surrounding_text request on the following state information updates. The input method changed on compositor side, which invalidates all current state information. New state information should be sent from the client via state requests (set_surrounding_text, set_content_hint, ...) and update_state. A factory for text-input objects. This object is a global singleton. Destroy the wp_text_input_manager object. Creates a new text-input object for a given seat. diff --git a/src/client/protocols/text-input.xml b/src/client/protocols/text-input.xml index 1b5284d..93c6296 100644 --- a/src/client/protocols/text-input.xml +++ b/src/client/protocols/text-input.xml @@ -1,346 +1,327 @@ - Copyright © 2012, 2013 Intel Corporation + SPDX-FileCopyrightText: 2012, 2013 Intel Corporation - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that copyright notice and this permission - notice appear in supporting documentation, and that the name of - the copyright holders not be used in advertising or publicity - pertaining to distribution of the software without specific, - written prior permission. The copyright holders make no - representations about the suitability of this software for any - purpose. It is provided "as is" without express or implied - warranty. - - THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN - AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF - THIS SOFTWARE. + SPDX-License-Identifier: MIT-CMU - An object used for text input. Adds support for text input and input + An object used for text input. Adds support for text input and input methods to applications. A text-input object is created from a wl_text_input_manager and corresponds typically to a text entry in an application. Requests are used to activate/deactivate the text-input object and set state information like surrounding and selected text or the content type. The information about entered text is sent to the text-input object via the pre-edit and commit events. Using this interface removes the need for applications to directly process hardware key events and compose text out of them. Text is generally UTF-8 encoded, indices and lengths are in bytes. Serials are used to synchronize the state between the text input and an input method. New serials are sent by the text input in the commit_state request and are used by the input method to indicate the known text input state in events like preedit_string, commit_string, and keysym. The text input can then ignore events from the input method which are based on an outdated state (for example after a reset). - Requests the text-input object to be activated (typically when the + Requests the text-input object to be activated (typically when the text entry gets focus). The seat argument is a wl_seat which maintains the focus for this activation. The surface argument is a wl_surface assigned to the text-input object and tracked for focus lost. The enter event is emitted on successful activation. Requests the text-input object to be deactivated (typically when the text entry lost focus). The seat argument is a wl_seat which was used for activation. Requests input panels (virtual keyboard) to show. Requests input panels (virtual keyboard) to hide. Should be called by an editor widget when the input state should be reset, for example after the text was changed outside of the normal input method flow. Sets the plain surrounding text around the input position. Text is UTF-8 encoded. Cursor is the byte offset within the surrounding text. Anchor is the byte offset of the selection anchor within the surrounding text. If there is no selected text anchor is the same as cursor. Content hint is a bitmask to allow to modify the behavior of the text input. The content purpose allows to specify the primary purpose of a text input. This allows an input method to show special purpose input panels with extra characters or to disallow some characters. Sets the content purpose and content hint. While the purpose is the basic purpose of an input field, the hint flags allow to modify some of the behavior. When no content type is explicitly set, a normal content purpose with default hints (auto completion, auto correction, auto capitalization) should be assumed. Sets a specific language. This allows for example a virtual keyboard to - show a language specific layout. The "language" argument is a RFC-3066 + show a language specific layout. The "language" argument is a RFC-3066 format language tag. It could be used for example in a word processor to indicate language of currently edited document or in an instant message application which tracks languages of contacts. Notify the text-input object when it received focus. Typically in response to an activate request. Notify the text-input object when it lost focus. Either in response to a deactivate request or when the assigned surface lost focus or was destroyed. Transfer an array of 0-terminated modifiers names. The position in the array is the index of the modifier as used in the modifiers bitmask in the keysym event. Notify when the visibility state of the input panel changed. Notify when a new composing text (pre-edit) should be set around the current cursor position. Any previously set composing text should be removed. The commit text can be used to replace the preedit text on reset (for example on unfocus). The text input should also handle all preedit_style and preedit_cursor events occuring directly before preedit_string. Sets styling information on composing text. The style is applied for - length bytes from index relative to the beginning of the composing + length bytes from index relative to the beginning of the composing text (as byte offset). Multiple styles can be applied to a composing text by sending multiple preedit_styling events. This event is handled as part of a following preedit_string event. Sets the cursor position inside the composing text (as byte offset) relative to the start of the composing text. When index is a negative number no cursor is shown. This event is handled as part of a following preedit_string event. Notify when text should be inserted into the editor widget. The text to commit could be either just a single character after a key press or the result of some composing (pre-edit). It could be also an empty text when some text should be removed (see delete_surrounding_text) or when the input cursor should be moved (see cursor_position). Any previously set composing text should be removed. Notify when the cursor or anchor position should be modified. This event should be handled as part of a following commit_string event. Notify when the text around the current cursor position should be deleted. Index is relative to the current cursor (in bytes). Length is the length of deleted text (in bytes). This event should be handled as part of a following commit_string event. Notify when a key event was sent. Key events should not be used for normal text input operations, which should be done with commit_string, delete_surrounding_text, etc. The key event follows the wl_keyboard key event convention. Sym is a XKB keysym, state a wl_keyboard key_state. Modifiers are a mask for effective modifiers (where the modifier indices are set by the modifiers_map event) - Sets the language of the input text. The "language" argument is a RFC-3066 + Sets the language of the input text. The "language" argument is a RFC-3066 format language tag. Sets the text direction of input text. It is mainly needed for showing input cursor on correct side of the editor when there is no input yet done and making sure neutral direction text is laid out properly. A factory for text-input objects. This object is a global singleton. Creates a new text-input object. diff --git a/src/client/protocols/wayland-eglstream-controller.xml b/src/client/protocols/wayland-eglstream-controller.xml index 2c3a635..31050ab 100644 --- a/src/client/protocols/wayland-eglstream-controller.xml +++ b/src/client/protocols/wayland-eglstream-controller.xml @@ -1,87 +1,71 @@ - Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved. + SPDX-FileCopyrightText: 2017-2018, NVIDIA CORPORATION. All rights reserved. - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. + SPDX-License-Identifier: MIT - dont_care: Using this enum will tell the server to make its own decisions regarding present mode. - fifo: Tells the server to use a fifo present mode. The decision to use fifo synchronous is left up to the server. - mailbox: Tells the server to use a mailbox present mode. - present_mode: Must be one of wl_eglstream_controller_present_mode. Tells the server the desired present mode that should be used. - fifo_length: Only valid when the present_mode attrib is provided and its value is specified as fifo. Tells the server the desired fifo length to be used when the desired present_mode is fifo. Creates the corresponding server side EGLStream from the given wl_buffer and attaches a consumer to it. Creates the corresponding server side EGLStream from the given wl_buffer and attaches a consumer to it using the given attributes. It contains key-value pairs compatible with intptr_t type. A key must be one of wl_eglstream_controller_attrib enumeration values. What a value represents is attribute-specific. diff --git a/src/client/region.cpp b/src/client/region.cpp index bda5cb2..f5afd9d 100644 --- a/src/client/region.cpp +++ b/src/client/region.cpp @@ -1,154 +1,140 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "region.h" #include "wayland_pointer_p.h" // Qt #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Region::Private { public: Private(const QRegion ®ion); void installRegion(const QRect &rect); void installRegion(const QRegion ®ion); void uninstallRegion(const QRect &rect); void uninstallRegion(const QRegion ®ion); WaylandPointer region; QRegion qtRegion; }; Region::Private::Private(const QRegion ®ion) : qtRegion(region) { } void Region::Private::installRegion(const QRect &rect) { if (!region.isValid()) { return; } wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height()); } void Region::Private::installRegion(const QRegion ®ion) { for (const QRect &rect : region) { installRegion(rect); } } void Region::Private::uninstallRegion(const QRect &rect) { if (!region.isValid()) { return; } wl_region_subtract(region, rect.x(), rect.y(), rect.width(), rect.height()); } void Region::Private::uninstallRegion(const QRegion ®ion) { for (const QRect &rect : region) { uninstallRegion(rect); } } Region::Region(const QRegion ®ion, QObject *parent) : QObject(parent) , d(new Private(region)) { } Region::~Region() { release(); } void Region::release() { d->region.release(); } void Region::destroy() { d->region.destroy(); } void Region::setup(wl_region *region) { Q_ASSERT(region); d->region.setup(region); d->installRegion(d->qtRegion); } bool Region::isValid() const { return d->region.isValid(); } void Region::add(const QRect &rect) { d->qtRegion = d->qtRegion.united(rect); d->installRegion(rect); } void Region::add(const QRegion ®ion) { d->qtRegion = d->qtRegion.united(region); d->installRegion(region); } void Region::subtract(const QRect &rect) { d->qtRegion = d->qtRegion.subtracted(rect); d->uninstallRegion(rect); } void Region::subtract(const QRegion ®ion) { d->qtRegion = d->qtRegion.subtracted(region); d->uninstallRegion(region); } QRegion Region::region() const { return d->qtRegion; } Region::operator wl_region*() const { return d->region; } Region::operator wl_region*() { return d->region; } } } diff --git a/src/client/region.h b/src/client/region.h index 4365ee7..b7ad9d7 100644 --- a/src/client/region.h +++ b/src/client/region.h @@ -1,120 +1,106 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_REGION_H #define WAYLAND_REGION_H #include #include struct wl_region; namespace KWayland { namespace Client { /** * @short Wrapper for the wl_region interface. * * This class is a convenient wrapper for the wl_region interface. * To create a Region call Compositor::createRegion. * * The main purpose of this class is to provide regions which can be * used to e.g. set the input region on a Surface. * * @see Compositor * @see Surface **/ class KWAYLANDCLIENT_EXPORT Region : public QObject { Q_OBJECT public: explicit Region(const QRegion ®ion, QObject *parent = nullptr); virtual ~Region(); /** * Setup this Surface to manage the @p region. * When using Compositor::createRegion there is no need to call this * method. **/ void setup(wl_region *region); /** * Releases the wl_region interface. * After the interface has been released the Region instance is no * longer valid and can be setup with another wl_region interface. **/ void release(); /** * Destroys the data held by this Region. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_region interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, region, &Region::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_region. **/ bool isValid() const; /** * Adds the @p rect to this Region. **/ void add(const QRect &rect); /** * Adds the @p region to this Rregion. **/ void add(const QRegion ®ion); /** * Subtracts @p rect from this Region. **/ void subtract(const QRect &rect); /** * Subtracts @p region from this Region. **/ void subtract(const QRegion ®ion); /** * The geometry of this Region. **/ QRegion region() const; operator wl_region*(); operator wl_region*() const; private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/registry.cpp b/src/client/registry.cpp index 5e7603d..f5c0371 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -1,911 +1,897 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "registry.h" #include "compositor.h" #include "connection_thread.h" #include "datadevicemanager.h" #include "dpms.h" #include "event_queue.h" #include "fakeinput.h" #include "fullscreen_shell.h" #include "idle.h" #include "idleinhibit.h" #include "keystate.h" #include "remote_access.h" #include "logging.h" #include "outputconfiguration.h" #include "outputmanagement.h" #include "outputdevice.h" #include "output.h" #include "plasmashell.h" #include "plasmavirtualdesktop.h" #include "plasmawindowmanagement.h" #include "pointerconstraints.h" #include "pointergestures.h" #include "seat.h" #include "shadow.h" #include "blur.h" #include "contrast.h" #include "relativepointer.h" #include "server_decoration.h" #include "slide.h" #include "shell.h" #include "shm_pool.h" #include "subcompositor.h" #include "textinput_p.h" #include "xdgshell.h" #include "xdgshell_p.h" #include "wayland_pointer_p.h" #include "xdgforeign_v2.h" #include "appmenu.h" #include "server_decoration_palette.h" #include "xdgoutput.h" #include "xdgdecoration.h" // Qt #include // wayland #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../compat/wayland-xdg-shell-v5-client-protocol.h" #include #include #include #include #include #include #include #include #include #include #include /***** * How to add another interface: * * define a new enum value in Registry::Interface * * define the bind method * * define the create method * * define the Announced signal * * define the Removed signal * * add a block to s_interfaces * * add the BIND macro for the new bind * * add the CREATE macro for the new create * * extend registry unit test to verify that it works ****/ namespace KWayland { namespace Client { namespace { struct SuppertedInterfaceData { quint32 maxVersion; QByteArray name; const wl_interface *interface; void (Registry::*announcedSignal)(quint32, quint32); void (Registry::*removedSignal)(quint32); }; static const QMap s_interfaces = { {Registry::Interface::Compositor, { 4, QByteArrayLiteral("wl_compositor"), &wl_compositor_interface, &Registry::compositorAnnounced, &Registry::compositorRemoved }}, {Registry::Interface::DataDeviceManager, { 3, QByteArrayLiteral("wl_data_device_manager"), &wl_data_device_manager_interface, &Registry::dataDeviceManagerAnnounced, &Registry::dataDeviceManagerRemoved }}, {Registry::Interface::Output, { 3, QByteArrayLiteral("wl_output"), &wl_output_interface, &Registry::outputAnnounced, &Registry::outputRemoved }}, {Registry::Interface::Shm, { 1, QByteArrayLiteral("wl_shm"), &wl_shm_interface, &Registry::shmAnnounced, &Registry::shmRemoved }}, {Registry::Interface::Seat, { 5, QByteArrayLiteral("wl_seat"), &wl_seat_interface, &Registry::seatAnnounced, &Registry::seatRemoved }}, {Registry::Interface::Shell, { 1, QByteArrayLiteral("wl_shell"), &wl_shell_interface, &Registry::shellAnnounced, &Registry::shellRemoved }}, {Registry::Interface::SubCompositor, { 1, QByteArrayLiteral("wl_subcompositor"), &wl_subcompositor_interface, &Registry::subCompositorAnnounced, &Registry::subCompositorRemoved }}, {Registry::Interface::PlasmaShell, { 6, QByteArrayLiteral("org_kde_plasma_shell"), &org_kde_plasma_shell_interface, &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, {Registry::Interface::PlasmaVirtualDesktopManagement, { 2, QByteArrayLiteral("org_kde_plasma_virtual_desktop_management"), &org_kde_plasma_virtual_desktop_management_interface, &Registry::plasmaVirtualDesktopManagementAnnounced, &Registry::plasmaVirtualDesktopManagementRemoved }}, {Registry::Interface::PlasmaWindowManagement, { 10, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, &Registry::plasmaWindowManagementRemoved }}, {Registry::Interface::Idle, { 1, QByteArrayLiteral("org_kde_kwin_idle"), &org_kde_kwin_idle_interface, &Registry::idleAnnounced, &Registry::idleRemoved }}, {Registry::Interface::RemoteAccessManager, { 1, QByteArrayLiteral("org_kde_kwin_remote_access_manager"), &org_kde_kwin_remote_access_manager_interface, &Registry::remoteAccessManagerAnnounced, &Registry::remoteAccessManagerRemoved }}, {Registry::Interface::FakeInput, { 4, QByteArrayLiteral("org_kde_kwin_fake_input"), &org_kde_kwin_fake_input_interface, &Registry::fakeInputAnnounced, &Registry::fakeInputRemoved }}, {Registry::Interface::OutputManagement, { 2, QByteArrayLiteral("org_kde_kwin_outputmanagement"), &org_kde_kwin_outputmanagement_interface, &Registry::outputManagementAnnounced, &Registry::outputManagementRemoved }}, {Registry::Interface::OutputDevice, { 2, QByteArrayLiteral("org_kde_kwin_outputdevice"), &org_kde_kwin_outputdevice_interface, &Registry::outputDeviceAnnounced, &Registry::outputDeviceRemoved }}, {Registry::Interface::Shadow, { 2, QByteArrayLiteral("org_kde_kwin_shadow_manager"), &org_kde_kwin_shadow_manager_interface, &Registry::shadowAnnounced, &Registry::shadowRemoved }}, {Registry::Interface::Blur, { 1, QByteArrayLiteral("org_kde_kwin_blur_manager"), &org_kde_kwin_blur_manager_interface, &Registry::blurAnnounced, &Registry::blurRemoved }}, {Registry::Interface::Contrast, { 1, QByteArrayLiteral("org_kde_kwin_contrast_manager"), &org_kde_kwin_contrast_manager_interface, &Registry::contrastAnnounced, &Registry::contrastRemoved }}, {Registry::Interface::Slide, { 1, QByteArrayLiteral("org_kde_kwin_slide_manager"), &org_kde_kwin_slide_manager_interface, &Registry::slideAnnounced, &Registry::slideRemoved }}, {Registry::Interface::FullscreenShell, { 1, QByteArrayLiteral("_wl_fullscreen_shell"), &_wl_fullscreen_shell_interface, &Registry::fullscreenShellAnnounced, &Registry::fullscreenShellRemoved }}, {Registry::Interface::Dpms, { 1, QByteArrayLiteral("org_kde_kwin_dpms_manager"), &org_kde_kwin_dpms_manager_interface, &Registry::dpmsAnnounced, &Registry::dpmsRemoved }}, {Registry::Interface::ServerSideDecorationManager, { 1, QByteArrayLiteral("org_kde_kwin_server_decoration_manager"), &org_kde_kwin_server_decoration_manager_interface, &Registry::serverSideDecorationManagerAnnounced, &Registry::serverSideDecorationManagerRemoved }}, {Registry::Interface::TextInputManagerUnstableV0, { 1, QByteArrayLiteral("wl_text_input_manager"), &wl_text_input_manager_interface, &Registry::textInputManagerUnstableV0Announced, &Registry::textInputManagerUnstableV0Removed }}, {Registry::Interface::TextInputManagerUnstableV2, { 1, QByteArrayLiteral("zwp_text_input_manager_v2"), &zwp_text_input_manager_v2_interface, &Registry::textInputManagerUnstableV2Announced, &Registry::textInputManagerUnstableV2Removed }}, {Registry::Interface::XdgShellUnstableV5, { 1, QByteArrayLiteral("xdg_shell"), &zxdg_shell_v5_interface, &Registry::xdgShellUnstableV5Announced, &Registry::xdgShellUnstableV5Removed }}, {Registry::Interface::RelativePointerManagerUnstableV1, { 1, QByteArrayLiteral("zwp_relative_pointer_manager_v1"), &zwp_relative_pointer_manager_v1_interface, &Registry::relativePointerManagerUnstableV1Announced, &Registry::relativePointerManagerUnstableV1Removed }}, {Registry::Interface::PointerGesturesUnstableV1, { 1, QByteArrayLiteral("zwp_pointer_gestures_v1"), &zwp_pointer_gestures_v1_interface, &Registry::pointerGesturesUnstableV1Announced, &Registry::pointerGesturesUnstableV1Removed }}, {Registry::Interface::PointerConstraintsUnstableV1, { 1, QByteArrayLiteral("zwp_pointer_constraints_v1"), &zwp_pointer_constraints_v1_interface, &Registry::pointerConstraintsUnstableV1Announced, &Registry::pointerConstraintsUnstableV1Removed }}, {Registry::Interface::XdgExporterUnstableV2, { 1, QByteArrayLiteral("zxdg_exporter_v2"), &zxdg_exporter_v2_interface, &Registry::exporterUnstableV2Announced, &Registry::exporterUnstableV2Removed }}, {Registry::Interface::XdgImporterUnstableV2, { 1, QByteArrayLiteral("zxdg_importer_v2"), &zxdg_importer_v2_interface, &Registry::importerUnstableV2Announced, &Registry::importerUnstableV2Removed }}, {Registry::Interface::XdgShellUnstableV6, { 1, QByteArrayLiteral("zxdg_shell_v6"), &zxdg_shell_v6_interface, &Registry::xdgShellUnstableV6Announced, &Registry::xdgShellUnstableV6Removed }}, {Registry::Interface::IdleInhibitManagerUnstableV1, { 1, QByteArrayLiteral("zwp_idle_inhibit_manager_v1"), &zwp_idle_inhibit_manager_v1_interface, &Registry::idleInhibitManagerUnstableV1Announced, &Registry::idleInhibitManagerUnstableV1Removed }}, {Registry::Interface::AppMenu, { 1, QByteArrayLiteral("org_kde_kwin_appmenu_manager"), &org_kde_kwin_appmenu_manager_interface, &Registry::appMenuAnnounced, &Registry::appMenuRemoved }}, {Registry::Interface::ServerSideDecorationPalette, { 1, QByteArrayLiteral("org_kde_kwin_server_decoration_palette_manager"), &org_kde_kwin_server_decoration_palette_manager_interface, &Registry::serverSideDecorationPaletteManagerAnnounced, &Registry::serverSideDecorationPaletteManagerRemoved }}, {Registry::Interface::XdgOutputUnstableV1, { 1, QByteArrayLiteral("zxdg_output_manager_v1"), &zxdg_output_manager_v1_interface, &Registry::xdgOutputAnnounced, &Registry::xdgOutputRemoved }}, {Registry::Interface::XdgShellStable, { 1, QByteArrayLiteral("xdg_wm_base"), &xdg_wm_base_interface, &Registry::xdgShellStableAnnounced, &Registry::xdgShellStableRemoved }}, {Registry::Interface::XdgDecorationUnstableV1, { 1, QByteArrayLiteral("zxdg_decoration_manager_v1"), &zxdg_decoration_manager_v1_interface, &Registry::xdgDecorationAnnounced, &Registry::xdgDecorationRemoved }}, {Registry::Interface::Keystate, { 1, QByteArrayLiteral("org_kde_kwin_keystate"), &org_kde_kwin_keystate_interface, &Registry::keystateAnnounced, &Registry::keystateRemoved }} }; static quint32 maxVersion(const Registry::Interface &interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().maxVersion; } return 0; } } class Q_DECL_HIDDEN Registry::Private { public: Private(Registry *q); void setup(); bool hasInterface(Interface interface) const; AnnouncedInterface interface(Interface interface) const; QVector interfaces(Interface interface) const; Interface interfaceForName(quint32 name) const; template T *bind(Interface interface, uint32_t name, uint32_t version) const; template T *create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const); WaylandPointer registry; static const struct wl_callback_listener s_callbackListener; WaylandPointer callback; EventQueue *queue = nullptr; private: void handleAnnounce(uint32_t name, const char *interface, uint32_t version); void handleRemove(uint32_t name); void handleGlobalSync(); static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void globalRemove(void *data, struct wl_registry *registry, uint32_t name); static void globalSync(void *data, struct wl_callback *callback, uint32_t serial); Registry *q; struct InterfaceData { Interface interface; uint32_t name; uint32_t version; }; QList m_interfaces; static const struct wl_registry_listener s_registryListener; }; Registry::Private::Private(Registry *q) : q(q) { } void Registry::Private::setup() { wl_registry_add_listener(registry, &s_registryListener, this); wl_callback_add_listener(callback, &s_callbackListener, this); } Registry::Registry(QObject *parent) : QObject(parent) , d(new Private(this)) { } Registry::~Registry() { release(); } void Registry::release() { d->registry.release(); d->callback.release(); } void Registry::destroy() { emit registryDestroyed(); d->registry.destroy(); d->callback.destroy(); } void Registry::create(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!isValid()); d->registry.setup(wl_display_get_registry(display)); d->callback.setup(wl_display_sync(display)); if (d->queue) { d->queue->addProxy(d->registry); d->queue->addProxy(d->callback); } } void Registry::create(ConnectionThread *connection) { create(connection->display()); connect(connection, &ConnectionThread::connectionDied, this, &Registry::destroy); } void Registry::setup() { Q_ASSERT(isValid()); d->setup(); } void Registry::setEventQueue(EventQueue *queue) { d->queue = queue; if (!queue) { return; } if (d->registry) { d->queue->addProxy(d->registry); } if (d->callback) { d->queue->addProxy(d->callback); } } EventQueue *Registry::eventQueue() { return d->queue; } #ifndef K_DOXYGEN const struct wl_registry_listener Registry::Private::s_registryListener = { globalAnnounce, globalRemove }; const struct wl_callback_listener Registry::Private::s_callbackListener = { globalSync }; #endif void Registry::Private::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleAnnounce(name, interface, version); } void Registry::Private::globalRemove(void *data, wl_registry *registry, uint32_t name) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleRemove(name); } void Registry::Private::globalSync(void* data, wl_callback* callback, uint32_t serial) { Q_UNUSED(serial) auto r = reinterpret_cast(data); Q_ASSERT(r->callback == callback); r->handleGlobalSync(); r->callback.release(); } void Registry::Private::handleGlobalSync() { emit q->interfacesAnnounced(); } namespace { static Registry::Interface nameToInterface(const char *interface) { for (auto it = s_interfaces.constBegin(); it != s_interfaces.constEnd(); ++it) { if (qstrcmp(interface, it.value().name) == 0) { return it.key(); } } return Registry::Interface::Unknown; } } void Registry::Private::handleAnnounce(uint32_t name, const char *interface, uint32_t version) { Interface i = nameToInterface(interface); emit q->interfaceAnnounced(QByteArray(interface), name, version); if (i == Interface::Unknown) { qCDebug(KWAYLAND_CLIENT) << "Unknown interface announced: " << interface << "/" << name << "/" << version; return; } qCDebug(KWAYLAND_CLIENT) << "Wayland Interface: " << interface << "/" << name << "/" << version; m_interfaces.append({i, name, version}); auto it = s_interfaces.constFind(i); if (it != s_interfaces.end()) { emit (q->*it.value().announcedSignal)(name, version); } } void Registry::Private::handleRemove(uint32_t name) { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [name](const InterfaceData &data) { return data.name == name; } ); if (it != m_interfaces.end()) { InterfaceData data = *(it); m_interfaces.erase(it); auto sit = s_interfaces.find(data.interface); if (sit != s_interfaces.end()) { emit (q->*sit.value().removedSignal)(data.name); } } emit q->interfaceRemoved(name); } bool Registry::Private::hasInterface(Registry::Interface interface) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [interface](const InterfaceData &data) { return data.interface == interface; } ); return it != m_interfaces.constEnd(); } QVector Registry::Private::interfaces(Interface interface) const { QVector retVal; for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { const auto &data = *it; if (data.interface == interface) { retVal << AnnouncedInterface{data.name, data.version}; } } return retVal; } Registry::AnnouncedInterface Registry::Private::interface(Interface interface) const { const auto all = interfaces(interface); if (!all.isEmpty()) { return all.last(); } return AnnouncedInterface{0, 0}; } Registry::Interface Registry::Private::interfaceForName(quint32 name) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [name] (const InterfaceData &data) { return data.name == name; }); if (it == m_interfaces.constEnd()) { return Interface::Unknown; } return (*it).interface; } bool Registry::hasInterface(Registry::Interface interface) const { return d->hasInterface(interface); } QVector Registry::interfaces(Interface interface) const { return d->interfaces(interface); } Registry::AnnouncedInterface Registry::interface(Interface interface) const { return d->interface(interface); } #define BIND2(__NAME__, __INAME__, __WL__) \ __WL__ *Registry::bind##__NAME__(uint32_t name, uint32_t version) const \ { \ return d->bind<__WL__>(Interface::__INAME__, name, qMin(maxVersion(Interface::__INAME__), version)); \ } #define BIND(__NAME__, __WL__) BIND2(__NAME__, __NAME__, __WL__) BIND(Compositor, wl_compositor) BIND(Output, wl_output) BIND(Seat, wl_seat) BIND(Shell, wl_shell) BIND(Shm, wl_shm) BIND(SubCompositor, wl_subcompositor) BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) BIND(PlasmaVirtualDesktopManagement, org_kde_plasma_virtual_desktop_management) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) BIND(FakeInput, org_kde_kwin_fake_input) BIND(OutputManagement, org_kde_kwin_outputmanagement) BIND(OutputDevice, org_kde_kwin_outputdevice) BIND(ServerSideDecorationManager, org_kde_kwin_server_decoration_manager) BIND(TextInputManagerUnstableV0, wl_text_input_manager) BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND(XdgShellUnstableV5, xdg_shell) BIND(XdgShellUnstableV6, zxdg_shell_v6) BIND(XdgShellStable, xdg_wm_base) BIND(RelativePointerManagerUnstableV1, zwp_relative_pointer_manager_v1) BIND(PointerGesturesUnstableV1, zwp_pointer_gestures_v1) BIND(PointerConstraintsUnstableV1, zwp_pointer_constraints_v1) BIND(XdgExporterUnstableV2, zxdg_exporter_v2) BIND(XdgImporterUnstableV2, zxdg_importer_v2) BIND(IdleInhibitManagerUnstableV1, zwp_idle_inhibit_manager_v1) BIND(Keystate, org_kde_kwin_keystate) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) BIND2(SlideManager, Slide, org_kde_kwin_slide_manager) BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager) BIND2(AppMenuManager, AppMenu, org_kde_kwin_appmenu_manager) BIND2(ServerSideDecorationPaletteManager, ServerSideDecorationPalette, org_kde_kwin_server_decoration_palette_manager) BIND(XdgOutputUnstableV1, zxdg_output_manager_v1) BIND(XdgDecorationUnstableV1, zxdg_decoration_manager_v1) #undef BIND #undef BIND2 template T *Registry::Private::create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const) { T *t = new T(parent); t->setEventQueue(queue); t->setup((q->*bindMethod)(name, version)); QObject::connect(q, &Registry::interfaceRemoved, t, [t, name] (quint32 removed) { if (name == removed) { emit t->removed(); } } ); QObject::connect(q, &Registry::registryDestroyed, t, &T::destroy); return t; } #define CREATE2(__NAME__, __BINDNAME__) \ __NAME__ *Registry::create##__NAME__(quint32 name, quint32 version, QObject *parent) \ { \ return d->create<__NAME__>(name, version, parent, &Registry::bind##__BINDNAME__); \ } #define CREATE(__NAME__) CREATE2(__NAME__, __NAME__) CREATE(Compositor) CREATE(Seat) CREATE(Shell) CREATE(SubCompositor) CREATE(FullscreenShell) CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) CREATE(PlasmaVirtualDesktopManagement) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(RemoteAccessManager) CREATE(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) CREATE(ShadowManager) CREATE(BlurManager) CREATE(ContrastManager) CREATE(SlideManager) CREATE(DpmsManager) CREATE(ServerSideDecorationManager) CREATE2(ShmPool, Shm) CREATE(AppMenuManager) CREATE(Keystate) CREATE(ServerSideDecorationPaletteManager) #undef CREATE #undef CREATE2 XdgExporter *Registry::createXdgExporter(quint32 name, quint32 version, QObject *parent) { //only V1 supported for now return d->create(name, version, parent, &Registry::bindXdgExporterUnstableV2); } XdgImporter *Registry::createXdgImporter(quint32 name, quint32 version, QObject *parent) { //only V1 supported for now return d->create(name, version, parent, &Registry::bindXdgImporterUnstableV2); } TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::TextInputManagerUnstableV0: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV0); case Interface::TextInputManagerUnstableV2: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV2); default: return nullptr; } } XdgShell *Registry::createXdgShell(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::XdgShellUnstableV5: return d->create(name, version, parent, &Registry::bindXdgShellUnstableV5); case Interface::XdgShellUnstableV6: return d->create(name, version, parent, &Registry::bindXdgShellUnstableV6); case Interface::XdgShellStable: return d->create(name, version, parent, &Registry::bindXdgShellStable); default: return nullptr; } } RelativePointerManager *Registry::createRelativePointerManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::RelativePointerManagerUnstableV1: return d->create(name, version, parent, &Registry::bindRelativePointerManagerUnstableV1); default: return nullptr; } } PointerGestures *Registry::createPointerGestures(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::PointerGesturesUnstableV1: return d->create(name, version, parent, &Registry::bindPointerGesturesUnstableV1); default: return nullptr; } } PointerConstraints *Registry::createPointerConstraints(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::PointerConstraintsUnstableV1: return d->create(name, version, parent, &Registry::bindPointerConstraintsUnstableV1); default: return nullptr; } } IdleInhibitManager *Registry::createIdleInhibitManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::IdleInhibitManagerUnstableV1: return d->create(name, version, parent, &Registry::bindIdleInhibitManagerUnstableV1); default: return nullptr; } } XdgOutputManager *Registry::createXdgOutputManager(quint32 name, quint32 version, QObject *parent) { switch(d->interfaceForName(name)) { case Interface::XdgOutputUnstableV1: return d->create(name, version, parent, &Registry::bindXdgOutputUnstableV1); default: return nullptr; } } XdgDecorationManager *Registry::createXdgDecorationManager(quint32 name, quint32 version, QObject *parent) { switch(d->interfaceForName(name)) { case Interface::XdgDecorationUnstableV1: return d->create(name, version, parent, &Registry::bindXdgDecorationUnstableV1); default: return nullptr; } } namespace { static const wl_interface *wlInterface(Registry::Interface interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().interface; } return nullptr; } } template T *Registry::Private::bind(Registry::Interface interface, uint32_t name, uint32_t version) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [=](const InterfaceData &data) { return data.interface == interface && data.name == name && data.version >= version; }); if (it == m_interfaces.constEnd()) { qCDebug(KWAYLAND_CLIENT) << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version; return nullptr; } auto t = reinterpret_cast(wl_registry_bind(registry, name, wlInterface(interface), version)); if (queue) { queue->addProxy(t); } return t; } bool Registry::isValid() const { return d->registry.isValid(); } wl_registry *Registry::registry() { return d->registry; } Registry::operator wl_registry*() const { return d->registry; } Registry::operator wl_registry*() { return d->registry; } } } diff --git a/src/client/registry.h b/src/client/registry.h index b64ba53..ea07deb 100644 --- a/src/client/registry.h +++ b/src/client/registry.h @@ -1,1809 +1,1795 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2018 David Edmundson +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2018 David Edmundson -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_REGISTRY_H #define WAYLAND_REGISTRY_H #include #include #include struct wl_compositor; struct wl_data_device_manager; struct wl_display; struct wl_output; struct wl_registry; struct wl_seat; struct wl_shell; struct wl_shm; struct wl_subcompositor; struct wl_text_input_manager; struct zwp_text_input_manager_v2; struct _wl_fullscreen_shell; struct org_kde_kwin_appmenu_manager; struct org_kde_kwin_outputmanagement; struct org_kde_kwin_outputdevice; struct org_kde_kwin_fake_input; struct org_kde_kwin_idle; struct org_kde_kwin_keystate; struct org_kde_kwin_remote_access_manager; struct org_kde_kwin_dpms_manager; struct org_kde_kwin_shadow_manager; struct org_kde_kwin_blur_manager; struct org_kde_kwin_contrast_manager; struct org_kde_kwin_slide_manager; struct org_kde_plasma_shell; struct org_kde_plasma_virtual_desktop_management; struct org_kde_plasma_window_management; struct org_kde_kwin_server_decoration_manager; struct org_kde_kwin_server_decoration_palette_manager; struct xdg_shell; struct zxdg_shell_v6; struct xdg_wm_base; struct zwp_relative_pointer_manager_v1; struct zwp_pointer_gestures_v1; struct zwp_pointer_constraints_v1; struct zxdg_exporter_v2; struct zxdg_importer_v2; struct zwp_idle_inhibit_manager_v1; struct zxdg_output_manager_v1; struct zxdg_decoration_manager_v1; namespace KWayland { namespace Client { class AppMenuManager; class Compositor; class ConnectionThread; class DataDeviceManager; class DpmsManager; class EventQueue; class FakeInput; class FullscreenShell; class OutputManagement; class OutputDevice; class Idle; class IdleInhibitManager; class Keystate; class RemoteAccessManager; class Output; class PlasmaShell; class PlasmaVirtualDesktopManagement; class PlasmaWindowManagement; class PointerConstraints; class PointerGestures; class Seat; class ShadowManager; class BlurManager; class ContrastManager; class SlideManager; class Shell; class ShmPool; class ServerSideDecorationManager; class ServerSideDecorationPaletteManager; class SubCompositor; class TextInputManager; class TextInputManagerUnstableV0; class TextInputManagerUnstableV2; class XdgShell; class RelativePointerManager; class XdgExporterUnstableV2; class XdgImporterUnstableV2; class XdgExporter; class XdgImporter; class XdgOutputManager; class XdgDecorationManager; /** * @short Wrapper for the wl_registry interface. * * The purpose of this class is to manage the wl_registry interface. * This class supports some well-known interfaces and can create a * wrapper class for those. * * The main purpose is to emit signals whenever a new interface is * added or an existing interface is removed. For the well known interfaces * dedicated signals are emitted allowing a user to connect directly to the * signal announcing the interface it is interested in. * * To create and setup the Registry one needs to call create with either a * wl_display from an existing Wayland connection or a ConnectionThread instance: * * @code * ConnectionThread *connection; // existing connection * Registry registry; * registry.create(connection); * registry.setup(); * @endcode * * The interfaces are announced in an asynchronous way by the Wayland server. * To initiate the announcing of the interfaces one needs to call setup. **/ class KWAYLANDCLIENT_EXPORT Registry : public QObject { Q_OBJECT public: /** * The well-known interfaces this Registry supports. * For each of the enum values the Registry is able to create a Wrapper * object. **/ enum class Interface { Unknown, ///< Refers to an Unknown interface Compositor, ///< Refers to the wl_compositor interface Shell, ///< Refers to the wl_shell interface Seat, ///< Refers to the wl_seat interface Shm, ///< Refers to the wl_shm interface Output, ///< Refers to the wl_output interface FullscreenShell, ///< Refers to the _wl_fullscreen_shell interface SubCompositor, ///< Refers to the wl_subcompositor interface; DataDeviceManager, ///< Refers to the wl_data_device_manager interface PlasmaShell, ///< Refers to org_kde_plasma_shell interface PlasmaWindowManagement, ///< Refers to org_kde_plasma_window_management interface Idle, ///< Refers to org_kde_kwin_idle_interface interface FakeInput, ///< Refers to org_kde_kwin_fake_input interface Shadow, ///< Refers to org_kde_kwin_shadow_manager interface Blur, ///< refers to org_kde_kwin_blur_manager interface Contrast, ///< refers to org_kde_kwin_contrast_manager interface Slide, ///< refers to org_kde_kwin_slide_manager Dpms, ///< Refers to org_kde_kwin_dpms_manager interface OutputManagement, ///< Refers to the wl_data_device_manager interface OutputDevice, ///< Refers to the org_kde_kwin_outputdevice interface ServerSideDecorationManager, ///< Refers to org_kde_kwin_server_decoration_manager TextInputManagerUnstableV0, ///< Refers to wl_text_input_manager, @since 5.23 TextInputManagerUnstableV2, ///< Refers to zwp_text_input_manager_v2, @since 5.23 XdgShellUnstableV5, ///< Refers to xdg_shell (unstable version 5), @since 5.25 RelativePointerManagerUnstableV1, ///< Refers to zwp_relative_pointer_manager_v1, @since 5.28 PointerGesturesUnstableV1, ///< Refers to zwp_pointer_gestures_v1, @since 5.29 PointerConstraintsUnstableV1, ///< Refers to zwp_pointer_constraints_v1, @since 5.29 XdgExporterUnstableV2, ///< refers to zxdg_exporter_v2, @since 5.40 XdgImporterUnstableV2, ///< refers to zxdg_importer_v2, @since 5.40 XdgShellUnstableV6, ///< Refers to zxdg_shell_v6 (unstable version 6), @since 5.39 IdleInhibitManagerUnstableV1, ///< Refers to zwp_idle_inhibit_manager_v1 (unstable version 1), @since 5.41 AppMenu, ///Refers to org_kde_kwin_appmenu @since 5.42 ServerSideDecorationPalette, ///Refers to org_kde_kwin_server_decoration_palette_manager @since 5.42 RemoteAccessManager, ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.45 PlasmaVirtualDesktopManagement, ///< Refers to org_kde_plasma_virtual_desktop_management interface @since 5.52 XdgOutputUnstableV1, ///refers to zxdg_output_v1, @since 5.47 XdgShellStable, ///refers to xdg_wm_base @since 5.48 XdgDecorationUnstableV1, ///refers to zxdg_decoration_manager_v1, @since 5.54 Keystate,/// interfaces(Interface interface) const; /** * @name Low-level bind methods for global interfaces. **/ ///@{ /** * Binds the wl_compositor with @p name and @p version. * If the @p name does not exist or is not for the compositor interface, * @c null will be returned. * * Prefer using createCompositor instead. * @see createCompositor **/ wl_compositor *bindCompositor(uint32_t name, uint32_t version) const; /** * Binds the wl_shell with @p name and @p version. * If the @p name does not exist or is not for the shell interface, * @c null will be returned. * * Prefer using createShell instead. * @see createShell **/ wl_shell *bindShell(uint32_t name, uint32_t version) const; /** * Binds the wl_seat with @p name and @p version. * If the @p name does not exist or is not for the seat interface, * @c null will be returned. * * Prefer using createSeat instead. * @see createSeat **/ wl_seat *bindSeat(uint32_t name, uint32_t version) const; /** * Binds the wl_shm with @p name and @p version. * If the @p name does not exist or is not for the shm interface, * @c null will be returned. * * Prefer using createShmPool instead. * @see createShmPool **/ wl_shm *bindShm(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_outputmanagement with @p name and @p version. * If the @p name does not exist or is not for the outputmanagement interface, * @c null will be returned. * * Prefer using createOutputManagement instead. * @see createOutputManagement **/ org_kde_kwin_outputmanagement *bindOutputManagement(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_outputdevice with @p name and @p version. * If the @p name does not exist or is not for the outputdevice interface, * @c null will be returned. * * Prefer using createOutputDevice instead. * @see createOutputDevice **/ wl_output *bindOutput(uint32_t name, uint32_t version) const; /** * Binds the wl_subcompositor with @p name and @p version. * If the @p name does not exist or is not for the subcompositor interface, * @c null will be returned. * * Prefer using createSubCompositor instead. * @see createSubCompositor **/ wl_subcompositor *bindSubCompositor(uint32_t name, uint32_t version) const; /** * Binds the wl_output with @p name and @p version. * If the @p name does not exist or is not for the output interface, * @c null will be returned. * * Prefer using createOutput instead. * @see createOutput * @since 5.5 **/ org_kde_kwin_outputdevice *bindOutputDevice(uint32_t name, uint32_t version) const; /** * Binds the _wl_fullscreen_shell with @p name and @p version. * If the @p name does not exist or is not for the fullscreen shell interface, * @c null will be returned. * * Prefer using createFullscreenShell instead. * @see createFullscreenShell **/ _wl_fullscreen_shell *bindFullscreenShell(uint32_t name, uint32_t version) const; /** * Binds the wl_data_device_manager with @p name and @p version. * If the @p name does not exist or is not for the data device manager interface, * @c null will be returned. * * Prefer using createDataDeviceManager instead. * @see createDataDeviceManager **/ wl_data_device_manager *bindDataDeviceManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_plasma_shell with @p name and @p version. * If the @p name does not exist or is not for the Plasma shell interface, * @c null will be returned. * * Prefer using createPlasmaShell instead. * @see createPlasmaShell * @since 5.4 **/ org_kde_plasma_shell *bindPlasmaShell(uint32_t name, uint32_t version) const; /** * Binds the org_kde_plasma_virtual_desktop_management with @p name and @p version. * If the @p name does not exist or is not for the Plasma Virtual desktop interface, * @c null will be returned. * * Prefer using createPlasmaShell instead. * @see createPlasmaShell * @since 5.52 **/ org_kde_plasma_virtual_desktop_management *bindPlasmaVirtualDesktopManagement(uint32_t name, uint32_t version) const; /** * Binds the org_kde_plasma_window_management with @p name and @p version. * If the @p name does not exist or is not for the Plasma window management interface, * @c null will be returned. * * Prefer using createPlasmaWindowManagement instead. * @see createPlasmaWindowManagement * @since 5.46 **/ org_kde_plasma_window_management *bindPlasmaWindowManagement(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_idle with @p name and @p version. * If the @p name does not exist or is not for the idle interface, * @c null will be returned. * * Prefer using createIdle instead. * @see createIdle * @since 5.4 **/ org_kde_kwin_idle *bindIdle(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_keystate with @p name and @p version. * If the @p name does not exist or is not for the keystate interface, * @c null will be returned. * * Prefer using createIdle instead. * @see createIdle * @since 5.4 **/ org_kde_kwin_keystate *bindKeystate(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_remote_access_manager with @p name and @p version. * If the @p name does not exist or is not for the idle interface, * @c null will be returned. * * Prefer using createRemoteAccessManager instead. * @see createRemoteAccessManager * @since 5.45 **/ org_kde_kwin_remote_access_manager *bindRemoteAccessManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_fake_input with @p name and @p version. * If the @p name does not exist or is not for the fake input interface, * @c null will be returned. * * Prefer using createFakeInput instead. * @see createFakeInput * @since 5.4 **/ org_kde_kwin_fake_input *bindFakeInput(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_shadow_manager with @p name and @p version. * If the @p name does not exist or is not for the shadow manager interface, * @c null will be returned. * * Prefer using createShadowManager instead. * @see createShadowManager * @since 5.4 **/ org_kde_kwin_shadow_manager *bindShadowManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_blur_manager with @p name and @p version. * If the @p name does not exist or is not for the blur manager interface, * @c null will be returned. * * Prefer using createBlurManager instead. * @see createBlurManager * @since 5.5 **/ org_kde_kwin_blur_manager *bindBlurManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_contrast_manager with @p name and @p version. * If the @p name does not exist or is not for the contrast manager interface, * @c null will be returned. * * Prefer using createContrastManager instead. * @see createContrastManager * @since 5.5 **/ org_kde_kwin_contrast_manager *bindContrastManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_slide_manager with @p name and @p version. * If the @p name does not exist or is not for the slide manager interface, * @c null will be returned. * * Prefer using createSlideManager instead. * @see createSlideManager * @since 5.5 **/ org_kde_kwin_slide_manager * bindSlideManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_dpms_manager with @p name and @p version. * If the @p name does not exist or is not for the dpms manager interface, * @c null will be returned. * * Prefer using createDpmsManager instead. * @see createDpmsManager * @since 5.5 **/ org_kde_kwin_dpms_manager *bindDpmsManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_server_decoration_manager with @p name and @p version. * If the @p name does not exist or is not for the server side decoration manager interface, * @c null will be returned. * * Prefer using createServerSideDecorationManager instead. * @see createServerSideDecorationManager * @since 5.6 **/ org_kde_kwin_server_decoration_manager *bindServerSideDecorationManager(uint32_t name, uint32_t version) const; /** * Binds the wl_text_input_manager with @p name and @p version. * If the @p name does not exist or is not for the text input interface in unstable version 0, * @c null will be returned. * * Prefer using createTextInputManager instead. * @see createTextInputManager * @since 5.23 **/ wl_text_input_manager *bindTextInputManagerUnstableV0(uint32_t name, uint32_t version) const; /** * Binds the zwp_text_input_manager_v2 with @p name and @p version. * If the @p name does not exist or is not for the text input interface in unstable version 2, * @c null will be returned. * * Prefer using createTextInputManager instead. * @see createTextInputManager * @since 5.23 **/ zwp_text_input_manager_v2 *bindTextInputManagerUnstableV2(uint32_t name, uint32_t version) const; /** * Binds the xdg_shell (unstable version 5) with @p name and @p version. * If the @p name does not exist or is not for the xdg shell interface in unstable version 5, * @c null will be returned. * * Prefer using createXdgShell instead. * @see createXdgShell * @since 5.25 **/ xdg_shell *bindXdgShellUnstableV5(uint32_t name, uint32_t version) const; /** * Binds the zxdg_shell_v6 (unstable version 6) with @p name and @p version. * If the @p name does not exist or is not for the xdg shell interface in unstable version 5, * @c null will be returned. * * Prefer using createXdgShell instead. * @see createXdgShell * @since 5.39 **/ zxdg_shell_v6 *bindXdgShellUnstableV6(uint32_t name, uint32_t version) const; /** * Binds the zxdg_shell_v6 (unstable version 6) with @p name and @p version. * If the @p name does not exist or is not for the xdg shell interface in unstable version 5, * @c null will be returned. * * Prefer using createXdgShell instead. * @see createXdgShell * @since 5.39 **/ xdg_wm_base *bindXdgShellStable(uint32_t name, uint32_t version) const; /** * Binds the zwp_relative_pointer_manager_v1 with @p name and @p version. * If the @p name does not exist or is not for the relative pointer interface in unstable version 1, * @c null will be returned. * * Prefer using createRelativePointerManager instead. * @see createRelativePointerManager * @since 5.28 **/ zwp_relative_pointer_manager_v1 *bindRelativePointerManagerUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zwp_pointer_gestures_v1 with @p name and @p version. * If the @p name does not exist or is not for the pointer gestures interface in unstable version 1, * @c null will be returned. * * Prefer using createPointerGestures instead. * @see createPointerGestures * @since 5.29 **/ zwp_pointer_gestures_v1 *bindPointerGesturesUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zwp_pointer_constraints_v1 with @p name and @p version. * If the @p name does not exist or is not for the pointer constraints interface in unstable version 1, * @c null will be returned. * * Prefer using createPointerConstraints instead. * @see createPointerConstraints * @since 5.29 **/ zwp_pointer_constraints_v1 *bindPointerConstraintsUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zxdg_exporter_v2 with @p name and @p version. * If the @p name does not exists or isnot for the exporter * extension in unstable version 1, * @c null will be returned. * * Prefer using createXdgExporter * @since 5.40 */ zxdg_exporter_v2 *bindXdgExporterUnstableV2(uint32_t name, uint32_t version) const; /** * Binds the zxdg_importer_v2 with @p name and @p version. * If the @p name does not exists or isnot for the importer * extension in unstable version 1, * @c null will be returned. * * Prefer using createXdgImporter * @since 5.40 */ zxdg_importer_v2 *bindXdgImporterUnstableV2(uint32_t name, uint32_t version) const; /** * Binds the zwp_idle_inhibit_manager_v1 with @p name and @p version. * If the @p name does not exists or is not for the idle inhibit manager in unstable version 1, * @c null will be returned. * * Prefer using createIdleInhibitManager * @since 5.41 */ zwp_idle_inhibit_manager_v1 *bindIdleInhibitManagerUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_appmenu_manager with @p name and @p version. * If the @p name does not exist or is not for the appmenu manager interface, * @c null will be returned. * * Prefer using createAppMenuManager instead. * @see createAppMenuManager * @since 5.42 **/ org_kde_kwin_appmenu_manager *bindAppMenuManager(uint32_t name, uint32_t version) const; /** * Binds the org_kde_kwin_server_decoration_palette_manager with @p name and @p version. * If the @p name does not exist or is not for the server side decoration palette manager interface, * @c null will be returned. * * Prefer using createServerSideDecorationPaletteManager instead. * @see createServerSideDecorationPaletteManager * @since 5.42 **/ org_kde_kwin_server_decoration_palette_manager *bindServerSideDecorationPaletteManager(uint32_t name, uint32_t version) const; /** * Binds the zxdg_output_v1 with @p name and @p version. * If the @p name does not exist, * @c null will be returned. * * Prefer using createXdgOutputManager instead. * @see createXdgOutputManager * @since 5.47 **/ zxdg_output_manager_v1 *bindXdgOutputUnstableV1(uint32_t name, uint32_t version) const; /** * Binds the zxdg_decoration_manager_v1 with @p name and @p version. * If the @p name does not exist, * @c null will be returned. * * Prefer using createXdgDecorationManager instead. * @see createXdgDecorationManager * @since 5.54 **/ zxdg_decoration_manager_v1 *bindXdgDecorationUnstableV1(uint32_t name, uint32_t version) const; ///@} /** * @name Convenient factory methods for global objects. **/ ///@{ /** * Creates a Compositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_compositor interface, * the returned Compositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_compositor interface to bind * @param version The version or the wl_compositor interface to use * @param parent The parent for Compositor * * @returns The created Compositor. **/ Compositor *createCompositor(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a Seat and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_seat interface, * the returned Seat will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_seat interface to bind * @param version The version or the wl_seat interface to use * @param parent The parent for Seat * * @returns The created Seat. **/ Shell *createShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a Compositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_compositor interface, * the returned Compositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_compositor interface to bind * @param version The version or the wl_compositor interface to use * @param parent The parent for Compositor * * @returns The created Compositor. **/ Seat *createSeat(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ShmPool and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_shm interface, * the returned ShmPool will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_shm interface to bind * @param version The version or the wl_shm interface to use * @param parent The parent for ShmPool * * @returns The created ShmPool. **/ ShmPool *createShmPool(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a SubCompositor and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_subcompositor interface, * the returned SubCompositor will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_subcompositor interface to bind * @param version The version or the wl_subcompositor interface to use * @param parent The parent for SubCompositor * * @returns The created SubCompositor. **/ SubCompositor *createSubCompositor(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an Output and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_output interface, * the returned Output will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_output interface to bind * @param version The version or the wl_output interface to use * @param parent The parent for Output * * @returns The created Output. **/ Output *createOutput(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an KWinOutputManagement and sets it up to manage the interface identified * by @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_output interface, * the returned KWinConnectors will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_outputmanagement interface to bind * @param version The version or the org_kde_kwin_outputmanagement interface to use * @param parent The parent for KWinOutputManagement * * @returns The created KWinOutputManagement. * @since 5.5 **/ OutputManagement *createOutputManagement(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an OutputDevice and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_outputdevice interface, * the returned OutputDevice will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_outputdevice interface to bind * @param version The version or the org_kde_kwin_outputdevice interface to use * @param parent The parent for OutputDevice * * @returns The created Output. * @since 5.5 **/ OutputDevice *createOutputDevice(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a FullscreenShell and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the _wl_fullscreen_shell interface, * the returned FullscreenShell will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the _wl_fullscreen_shell interface to bind * @param version The version or the _wl_fullscreen_shell interface to use * @param parent The parent for FullscreenShell * * @returns The created FullscreenShell. * @since 5.5 **/ FullscreenShell *createFullscreenShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a DataDeviceManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the wl_data_device_manager interface, * the returned DataDeviceManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the wl_data_device_manager interface to bind * @param version The version or the wl_data_device_manager interface to use * @param parent The parent for DataDeviceManager * * @returns The created DataDeviceManager. **/ DataDeviceManager *createDataDeviceManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PlasmaShell and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_plasma_shell interface, * the returned PlasmaShell will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_plasma_shell interface to bind * @param version The version or the org_kde_plasma_shell interface to use * @param parent The parent for PlasmaShell * * @returns The created PlasmaShell. * @since 5.4 **/ PlasmaShell *createPlasmaShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PlasmaVirtualDesktopManagement and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_plasma_virtual_desktop_management interface, * the returned VirtualDesktop will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_plasma_virtual_desktop_management interface to bind * @param version The version or the org_kde_plasma_virtual_desktop_management interface to use * @param parent The parent for PlasmaShell * * @returns The created PlasmaShell. * @since 5.52 **/ PlasmaVirtualDesktopManagement *createPlasmaVirtualDesktopManagement(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PlasmaWindowManagement and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_plasma_window_management interface, * the returned PlasmaWindowManagement will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_plasma_window_management interface to bind * @param version The version or the org_kde_plasma_window_management interface to use * @param parent The parent for PlasmaWindowManagement * * @returns The created PlasmaWindowManagement. * @since 5.4 **/ PlasmaWindowManagement *createPlasmaWindowManagement(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an Idle and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_idle interface, * the returned Idle will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_idle interface to bind * @param version The version or the org_kde_kwin_idle interface to use * @param parent The parent for Idle * * @returns The created Idle. * @since 5.4 **/ Idle *createIdle(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a KEystate and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_keystate interface, * the returned Idle will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_keystate interface to bind * @param version The version or the org_kde_kwin_keystate interface to use * @param parent The parent for Idle * * @returns The created Idle. * @since 5.4 **/ Keystate *createKeystate(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a RemoteAccessManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_remote_access_manager interface, * the returned RemoteAccessManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_remote_access_manager interface to bind * @param version The version or the org_kde_kwin_remote_access_manager interface to use * @param parent The parent for RemoteAccessManager * * @returns The created RemoteAccessManager. * @since 5.45 **/ RemoteAccessManager *createRemoteAccessManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a FakeInput and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_fake_input interface, * the returned FakeInput will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_fake_input interface to bind * @param version The version or the org_kde_kwin_fake_input interface to use * @param parent The parent for FakeInput * * @returns The created FakeInput. * @since 5.4 **/ FakeInput *createFakeInput(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ShadowManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_shadow_manager interface, * the returned ShadowManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_shadow_manager interface to bind * @param version The version or the org_kde_kwin_shadow_manager interface to use * @param parent The parent for ShadowManager * * @returns The created ShadowManager. * @since 5.4 **/ ShadowManager *createShadowManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a BlurManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_blur_manager interface, * the returned BlurManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_blur_manager interface to bind * @param version The version or the org_kde_kwin_blur_manager interface to use * @param parent The parent for BlurManager * * @returns The created BlurManager. * @since 5.5 **/ BlurManager *createBlurManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ContrastManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_contrast_manager interface, * the returned ContrastManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_contrast_manager interface to bind * @param version The version or the org_kde_kwin_contrast_manager interface to use * @param parent The parent for ContrastManager * * @returns The created ContrastManager. * @since 5.5 **/ ContrastManager *createContrastManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a SlideManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_slide_manager interface, * the returned SlideManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_slide_manager interface to bind * @param version The version or the org_kde_kwin_slide_manager interface to use * @param parent The parent for SlideManager * * @returns The created SlideManager. * @since 5.5 **/ SlideManager *createSlideManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a DpmsManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_dpms_manager interface, * the returned DpmsManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_dpms_manager interface to bind * @param version The version or the org_kde_kwin_dpms_manager interface to use * @param parent The parent for DpmsManager * * @returns The created DpmsManager. * @since 5.5 **/ DpmsManager *createDpmsManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ServerSideDecorationManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_server_decoration_manager interface, * the returned ServerSideDecorationManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_server_decoration_manager interface to bind * @param version The version or the org_kde_kwin_server_decoration_manager interface to use * @param parent The parent for ServerSideDecorationManager * * @returns The created ServerSideDecorationManager. * @since 5.6 **/ ServerSideDecorationManager *createServerSideDecorationManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a TextInputManager and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li wl_text_input_manager * @li zwp_text_input_manager_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the TextInputManager * * @returns The created TextInputManager * @since 5.23 **/ TextInputManager *createTextInputManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgShell and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li xdg_shell (Unstable version 5) * * If @p name is for one of the supported interfaces the corresponding shell will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the XdgShell * * @returns The created XdgShell * @since 5.25 **/ XdgShell *createXdgShell(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a RelativePointerManager and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_relative_pointer_manager_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the RelativePointerManager * * @returns The created RelativePointerManager * @since 5.28 **/ RelativePointerManager *createRelativePointerManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PointerGestures and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_pointer_gestures_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the PointerGestures * * @returns The created PointerGestures * @since 5.29 **/ PointerGestures *createPointerGestures(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a PointerConstraints and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_pointer_constraints_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @param name The name of the interface to bind * @param version The version of the interface to use * @param parent The parent for the PointerConstraints * * @returns The created PointerConstraints * @since 5.29 **/ PointerConstraints *createPointerConstraints(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgExporter and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zxdg_exporter_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @returns The created XdgExporter * @since 5.40 */ XdgExporter *createXdgExporter(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgImporter and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zxdg_importer_v2 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @returns The created XdgImporter * @since 5.40 */ XdgImporter *createXdgImporter(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an IdleInhibitManager and sets it up to manage the interface identified by * @p name and @p version. * * This factory method supports the following interfaces: * @li zwp_idle_inhibit_manager_v1 * * If @p name is for one of the supported interfaces the corresponding manager will be created, * otherwise @c null will be returned. * * @returns The created IdleInhibitManager * @since 5.41 */ IdleInhibitManager *createIdleInhibitManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a AppMenuManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_appmenu_manager interface, * the returned AppMenuManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_appmenu_manager interface to bind * @param version The version or the org_kde_kwin_appmenu_manager interface to use * @param parent The parent for AppMenuManager * * @returns The created AppMenuManager. * @since 5.42 **/ AppMenuManager *createAppMenuManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates a ServerSideDecorationPaletteManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the org_kde_kwin_appmenu_manager interface, * the returned ServerSideDecorationPaletteManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the org_kde_kwin_server_decoration_palette_manager interface to bind * @param version The version or the org_kde_kwin_server_decoration_palette_manager interface to use * @param parent The parent for ServerSideDecorationPaletteManager * * @returns The created ServerSideDecorationPaletteManager. * @since 5.42 **/ ServerSideDecorationPaletteManager *createServerSideDecorationPaletteManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgOutputManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the zxdg_output_manager_v1 interface, * the returned XdgOutputManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the zxdg_output_manager_v1 interface to bind * @param version The version or the zxdg_output_manager_v1 interface to use * @param parent The parent for XdgOuptutManager * * @returns The created XdgOuptutManager. * @since 5.47 **/ XdgOutputManager *createXdgOutputManager(quint32 name, quint32 version, QObject *parent = nullptr); /** * Creates an XdgDecorationManager and sets it up to manage the interface identified by * @p name and @p version. * * Note: in case @p name is invalid or isn't for the zxdg_decoration_manager_v1 interface, * the returned XdgDecorationManager will not be valid. Therefore it's recommended to call * isValid on the created instance. * * @param name The name of the zxdg_decoration_manager_v1 interface to bind * @param version The version or the zxdg_decoration_manager_v1 interface to use * @param parent The parent for XdgDecorationManager * * @returns The created XdgDecorationManager. * @since 5.54 **/ XdgDecorationManager *createXdgDecorationManager(quint32 name, quint32 version, QObject *parent = nullptr); ///@} /** * cast operator to the low-level Wayland @c wl_registry **/ operator wl_registry*(); /** * cast operator to the low-level Wayland @c wl_registry **/ operator wl_registry*() const; /** * @returns access to the low-level Wayland @c wl_registry **/ wl_registry *registry(); Q_SIGNALS: /** * @name Interface announced signals. **/ ///@{ /** * Emitted whenever a wl_compositor interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void compositorAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void shellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_seat interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void seatAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_shm interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void shmAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_subcompositor interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void subCompositorAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_output interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void outputAnnounced(quint32 name, quint32 version); /** * Emitted whenever a _wl_fullscreen_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void fullscreenShellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_data_device_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void dataDeviceManagerAnnounced(quint32 name, quint32 version); void outputManagementAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_outputdevice interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void outputDeviceAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_plasma_shell interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void plasmaShellAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_plasma_virtual_desktop_management interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.52 **/ void plasmaVirtualDesktopManagementAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_plasma_window_management interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void plasmaWindowManagementAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_idle interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void idleAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_remote_access_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.45 **/ void remoteAccessManagerAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_fake_input interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void fakeInputAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_shadow_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.4 **/ void shadowAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_blur_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void blurAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_contrast_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void contrastAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_slide_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void slideAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_dpms_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.5 **/ void dpmsAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_server_decoration_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.6 **/ void serverSideDecorationManagerAnnounced(quint32 name, quint32 version); /** * Emitted whenever a wl_text_input_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.23 **/ void textInputManagerUnstableV0Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_text_input_manager_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.23 **/ void textInputManagerUnstableV2Announced(quint32 name, quint32 version); /** * Emitted whenever a xdg_shell (unstable version 5) interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.25 **/ void xdgShellUnstableV5Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_shell_v6 (unstable version 6) interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.25 **/ void xdgShellUnstableV6Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_relative_pointer_manager_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.28 **/ void relativePointerManagerUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_pointer_gestures_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.29 **/ void pointerGesturesUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_pointer_constraints_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.29 **/ void pointerConstraintsUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_exporter_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.40 */ void exporterUnstableV2Announced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_importer_v2 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.40 */ void importerUnstableV2Announced(quint32 name, quint32 version); /** * Emitted whenever a zwp_idle_inhibit_manager_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.41 */ void idleInhibitManagerUnstableV1Announced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_appmenu_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.42 */ void appMenuAnnounced(quint32 name, quint32 version); /** * Emitted whenever a org_kde_kwin_server_decoration_palette_manager interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.42 */ void serverSideDecorationPaletteManagerAnnounced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_output_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.47 */ void xdgOutputAnnounced(quint32 name, quint32 version); /** * Emitted whenever a xdg_wm_base (stable xdg shell) interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.48 **/ void xdgShellStableAnnounced(quint32 name, quint32 version); /** * Emitted whenever a zxdg_decoration_manager_v1 interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface * @since 5.54 **/ void xdgDecorationAnnounced(quint32 name, quint32 version); ///@} /** * @name Interface removed signals. **/ ///@{ /** * Emitted whenever a wl_compositor interface gets removed. * @param name The name for the removed interface **/ void compositorRemoved(quint32 name); /** * Emitted whenever a wl_shell interface gets removed. * @param name The name for the removed interface **/ void shellRemoved(quint32 name); /** * Emitted whenever a wl_seat interface gets removed. * @param name The name for the removed interface **/ void seatRemoved(quint32 name); /** * Emitted whenever a wl_shm interface gets removed. * @param name The name for the removed interface **/ void shmRemoved(quint32 name); /** * Emitted whenever a wl_subcompositor interface gets removed. * @param name The name for the removed interface **/ void subCompositorRemoved(quint32 name); /** * Emitted whenever a wl_output interface gets removed. * @param name The name for the removed interface **/ void outputRemoved(quint32 name); /** * Emitted whenever a _wl_fullscreen_shell interface gets removed. * @param name The name for the removed interface **/ void fullscreenShellRemoved(quint32 name); /** * Emitted whenever a wl_data_device_manager interface gets removed. * @param name The name for the removed interface **/ void dataDeviceManagerRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_outputmanagement interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void outputManagementRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_outputdevice interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void outputDeviceRemoved(quint32 name); /** * Emitted whenever a org_kde_plasma_shell interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void plasmaShellRemoved(quint32 name); /** * Emitted whenever a org_kde_plasma_virtual_desktop_management interface gets removed. * @param name The name for the removed interface * @since 5.52 **/ void plasmaVirtualDesktopManagementRemoved(quint32 name); /** * Emitted whenever a org_kde_plasma_window_management interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void plasmaWindowManagementRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_idle interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void idleRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_remote_access_manager interface gets removed. * @param name The name for the removed interface * @since 5.45 **/ void remoteAccessManagerRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_fake_input interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void fakeInputRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_shadow_manager interface gets removed. * @param name The name for the removed interface * @since 5.4 **/ void shadowRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_blur_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void blurRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_contrast_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void contrastRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_slide_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void slideRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_dpms_manager interface gets removed. * @param name The name for the removed interface * @since 5.5 **/ void dpmsRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_server_decoration_manager interface gets removed. * @param name The name for the removed interface * @since 5.6 **/ void serverSideDecorationManagerRemoved(quint32 name); /** * Emitted whenever a wl_text_input_manager interface gets removed. * @param name The name for the removed interface * @since 5.23 **/ void textInputManagerUnstableV0Removed(quint32 name); /** * Emitted whenever a zwp_text_input_manager_v2 interface gets removed. * @param name The name for the removed interface * @since 5.23 **/ void textInputManagerUnstableV2Removed(quint32 name); /** * Emitted whenever an xdg_shell (unstable version 5) interface gets removed. * @param name The name for the removed interface * @since 5.25 **/ void xdgShellUnstableV5Removed(quint32 name); /** * Emitted whenever an xdg_shell (unstable version 5) interface gets removed. * @param name The name for the removed interface * @since 5.25 **/ void xdgShellUnstableV6Removed(quint32 name); /** * Emitted whenever a zwp_relative_pointer_manager_v1 interface gets removed. * @param name The name for the removed interface * @since 5.28 **/ void relativePointerManagerUnstableV1Removed(quint32 name); /** * Emitted whenever a zwp_pointer_gestures_v1 interface gets removed. * @param name The name for the removed interface * @since 5.29 **/ void pointerGesturesUnstableV1Removed(quint32 name); /** * Emitted whenever a zwp_pointer_constraints_v1 interface gets removed. * @param name The name for the removed interface * @since 5.29 **/ void pointerConstraintsUnstableV1Removed(quint32 name); /** * Emitted whenever a zxdg_exporter_v2 interface gets removed. * @param name The name for the removed interface * @since 5.40 **/ void exporterUnstableV2Removed(quint32 name); /** * Emitted whenever a zxdg_importer_v2 interface gets removed. * @param name The name for the removed interface * @since 5.40 **/ void importerUnstableV2Removed(quint32 name); /** * Emitted whenever a zwp_idle_inhibit_manager_v1 interface gets removed. * @param name The name of the removed interface * @since 5.41 **/ void idleInhibitManagerUnstableV1Removed(quint32 name); /** * Emitted whenever a org_kde_kwin_appmenu_manager gets removed. * @param name The name of the removed interface * @since 5.42 **/ void appMenuRemoved(quint32 name); /** * Emitted whenever a org_kde_kwin_server_decoration_palette_manager gets removed. * @param name The name of the removed interface * @since 5.42 **/ void serverSideDecorationPaletteManagerRemoved(quint32 name); /** * Emitted whenever a zxdg_output_v1 gets removed. * @param name The name of the removed interface * @since 5.47 **/ void xdgOutputRemoved(quint32 name); /** * Emitted whenever an xdg_wm_base (stable xdgshell) interface gets removed. * @param name The name for the removed interface * @since 5.48 **/ void xdgShellStableRemoved(quint32 name); /** * Emitted whenever a zxdg_decoration_manager_v1 gets removed. * @param name The name of the removed interface * @since 5.54 **/ void xdgDecorationRemoved(quint32 name); void keystateAnnounced(quint32 name, quint32 version); void keystateRemoved(quint32 name); ///@} /** * Generic announced signal which gets emitted whenever an interface gets * announced. * * This signal is emitted before the dedicated signals are handled. If one * wants to know about one of the well-known interfaces use the dedicated * signals instead. Especially the bind methods might fail before the dedicated * signals are emitted. * * @param interface The interface (e.g. wl_compositor) which is announced * @param name The name for the announced interface * @param version The maximum supported version of the announced interface **/ void interfaceAnnounced(QByteArray interface, quint32 name, quint32 version); /** * Generic removal signal which gets emitted whenever an interface gets removed. * * This signal is emitted after the dedicated signals are handled. * * @param name The name for the removed interface **/ void interfaceRemoved(quint32 name); /** * Emitted when the Wayland display is done flushing the initial interface * callbacks, announcing wl_display properties. This can be used to compress * events. Note that this signal is emitted only after announcing interfaces, * such as outputs, but not after receiving callbacks of interface properties, * such as the output's geometry, modes, etc.. * This signal is emitted from the wl_display_sync callback. **/ void interfacesAnnounced(); Q_SIGNALS: /* * Emitted when the registry has been destroyed rather than released */ void registryDestroyed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/relativepointer.cpp b/src/client/relativepointer.cpp index ce3c354..dbd5f00 100644 --- a/src/client/relativepointer.cpp +++ b/src/client/relativepointer.cpp @@ -1,194 +1,180 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "relativepointer.h" #include "event_queue.h" #include "pointer.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN RelativePointerManager::Private { public: Private() = default; WaylandPointer relativepointermanagerunstablev1; EventQueue *queue = nullptr; }; RelativePointerManager::RelativePointerManager(QObject *parent) : QObject(parent) , d(new Private) { } RelativePointerManager::~RelativePointerManager() { release(); } void RelativePointerManager::setup(zwp_relative_pointer_manager_v1 *relativepointermanagerunstablev1) { Q_ASSERT(relativepointermanagerunstablev1); Q_ASSERT(!d->relativepointermanagerunstablev1); d->relativepointermanagerunstablev1.setup(relativepointermanagerunstablev1); } void RelativePointerManager::release() { d->relativepointermanagerunstablev1.release(); } void RelativePointerManager::destroy() { d->relativepointermanagerunstablev1.destroy(); } void RelativePointerManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *RelativePointerManager::eventQueue() { return d->queue; } RelativePointerManager::operator zwp_relative_pointer_manager_v1*() { return d->relativepointermanagerunstablev1; } RelativePointerManager::operator zwp_relative_pointer_manager_v1*() const { return d->relativepointermanagerunstablev1; } bool RelativePointerManager::isValid() const { return d->relativepointermanagerunstablev1.isValid(); } RelativePointer *RelativePointerManager::createRelativePointer(Pointer *pointer, QObject *parent) { Q_ASSERT(isValid()); RelativePointer *p = new RelativePointer(parent); auto w = zwp_relative_pointer_manager_v1_get_relative_pointer(d->relativepointermanagerunstablev1, *pointer); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN RelativePointer::Private { public: Private(RelativePointer *q); void setup(zwp_relative_pointer_v1 *relativepointerunstablev1); WaylandPointer relativepointerunstablev1; private: static void relativeMotionCallback(void *data, zwp_relative_pointer_v1 *zwp_relative_pointer_v1, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel); RelativePointer *q; static const zwp_relative_pointer_v1_listener s_listener; }; RelativePointer::Private::Private(RelativePointer *q) : q(q) { } const zwp_relative_pointer_v1_listener RelativePointer::Private::s_listener = { relativeMotionCallback }; void RelativePointer::Private::relativeMotionCallback(void *data, zwp_relative_pointer_v1 *zwp_relative_pointer_v1, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { auto p = reinterpret_cast(data); Q_ASSERT(p->relativepointerunstablev1 == zwp_relative_pointer_v1); const QSizeF delta(wl_fixed_to_double(dx), wl_fixed_to_double(dy)); const QSizeF deltaNonAccel(wl_fixed_to_double(dx_unaccel), wl_fixed_to_double(dy_unaccel)); const quint64 timestamp = quint64(utime_lo) | (quint64(utime_hi) << 32); emit p->q->relativeMotion(delta, deltaNonAccel, timestamp); } void RelativePointer::Private::setup(zwp_relative_pointer_v1 *v1) { Q_ASSERT(v1); Q_ASSERT(!relativepointerunstablev1); relativepointerunstablev1.setup(v1); zwp_relative_pointer_v1_add_listener(relativepointerunstablev1, &s_listener, this); } RelativePointer::RelativePointer(QObject *parent) : QObject(parent) , d(new Private(this)) { } RelativePointer::~RelativePointer() { release(); } void RelativePointer::setup(zwp_relative_pointer_v1 *relativepointerunstablev1) { d->setup(relativepointerunstablev1); } void RelativePointer::release() { d->relativepointerunstablev1.release(); } void RelativePointer::destroy() { d->relativepointerunstablev1.destroy(); } RelativePointer::operator zwp_relative_pointer_v1*() { return d->relativepointerunstablev1; } RelativePointer::operator zwp_relative_pointer_v1*() const { return d->relativepointerunstablev1; } bool RelativePointer::isValid() const { return d->relativepointerunstablev1.isValid(); } } } diff --git a/src/client/relativepointer.h b/src/client/relativepointer.h index f596555..be7b3bb 100644 --- a/src/client/relativepointer.h +++ b/src/client/relativepointer.h @@ -1,236 +1,222 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_RELATIVEPOINTER_H #define KWAYLAND_CLIENT_RELATIVEPOINTER_H #include #include struct zwp_relative_pointer_manager_v1; struct zwp_relative_pointer_v1; namespace KWayland { namespace Client { class EventQueue; class Pointer; class RelativePointer; /** * @short Wrapper for the zwp_relative_pointer_manager_v1 interface. * * This class provides a convenient wrapper for the zwp_relative_pointer_manager_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the RelativePointerManager interface: * @code * RelativePointerManager *c = registry->createRelativePointerManagerUnstableV1(name, version); * @endcode * * This creates the RelativePointerManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * RelativePointerManager *c = new RelativePointerManager; * c->setup(registry->RelativePointerManager(name, version)); * @endcode * * The RelativePointerManager can be used as a drop-in replacement for any zwp_relative_pointer_manager_v1 * pointer as it provides matching cast operators. * * @see Registry * @since 5.28 **/ class KWAYLANDCLIENT_EXPORT RelativePointerManager : public QObject { Q_OBJECT public: /** * Creates a new RelativePointerManager. * Note: after constructing the RelativePointerManager it is not yet valid and one needs * to call setup. In order to get a ready to use RelativePointerManager prefer using * Registry::createRelativePointerManagerUnstableV1. **/ explicit RelativePointerManager(QObject *parent = nullptr); virtual ~RelativePointerManager(); /** * Setup this RelativePointerManagerUnstableV1 to manage the @p relativepointermanagerunstablev1. * When using Registry::createRelativePointerManagerUnstableV1 there is no need to call this * method. **/ void setup(zwp_relative_pointer_manager_v1 *relativepointermanagerunstablev1); /** * @returns @c true if managing a zwp_relative_pointer_manager_v1. **/ bool isValid() const; /** * Releases the zwp_relative_pointer_manager_v1 interface. * After the interface has been released the RelativePointerManagerUnstableV1 instance is no * longer valid and can be setup with another zwp_relative_pointer_manager_v1 interface. **/ void release(); /** * Destroys the data held by this RelativePointerManagerUnstableV1. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_relative_pointer_manager_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, relativepointermanagerunstablev1, &RelativePointerManagerUnstableV1::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this RelativePointerManagerUnstableV1. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this RelativePointerManagerUnstableV1. **/ EventQueue *eventQueue(); /** * Creates a RelativePointer for the given @p pointer. **/ RelativePointer *createRelativePointer(Pointer *pointer, QObject *parent = nullptr); operator zwp_relative_pointer_manager_v1*(); operator zwp_relative_pointer_manager_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the RelativePointerManagerUnstableV1 got created by * Registry::createRelativePointerManagerUnstableV1 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the zwp_relative_pointer_v1 interface. * * The RelativePointer is an extension to the Pointer used for emitting * relative pointer events. It shares the same focus as Pointer of the same Seat * and will only emit events when it has focus. * * @since 5.28 **/ class KWAYLANDCLIENT_EXPORT RelativePointer : public QObject { Q_OBJECT public: virtual ~RelativePointer(); /** * Setup this RelativePointerUnstableV1 to manage the @p relativepointerunstablev1. * When using RelativePointerManagerUnstableV1::createRelativePointerUnstableV1 there is no need to call this * method. **/ void setup(zwp_relative_pointer_v1 *relativepointerunstablev1); /** * @returns @c true if managing a zwp_relative_pointer_v1. **/ bool isValid() const; /** * Releases the zwp_relative_pointer_v1 interface. * After the interface has been released the RelativePointerUnstableV1 instance is no * longer valid and can be setup with another zwp_relative_pointer_v1 interface. **/ void release(); /** * Destroys the data held by this RelativePointerUnstableV1. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_relative_pointer_v1 interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * RelativePointer gets destroyed. * * @see release **/ void destroy(); operator zwp_relative_pointer_v1*(); operator zwp_relative_pointer_v1*() const; Q_SIGNALS: /** * A relative motion event. * * A relative motion is in the same dimension as regular motion events, * except they do not represent an absolute position. For example, * moving a pointer from (x, y) to (x', y') would have the equivalent * relative motion (x' - x, y' - y). If a pointer motion caused the * absolute pointer position to be clipped by for example the edge of the * monitor, the relative motion is unaffected by the clipping and will * represent the unclipped motion. * * This signal also contains non-accelerated motion deltas (@p deltaNonAccelerated). * The non-accelerated delta is, when applicable, the regular pointer motion * delta as it was before having applied motion acceleration and other * transformations such as normalization. * * Note that the non-accelerated delta does not represent 'raw' events as * they were read from some device. Pointer motion acceleration is device- * and configuration-specific and non-accelerated deltas and accelerated * deltas may have the same value on some devices. * * Relative motions are not coupled to Pointer motion events, * and can be sent in combination with such events, but also independently. There may * also be scenarios where Pointer motion is sent, but there is no * relative motion. The order of an absolute and relative motion event * originating from the same physical motion is not guaranteed. * * @param delta Motion vector * @param deltaNonAccelerated non-accelerated motion vector * @param microseconds timestamp with microseconds granularity **/ void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp); private: friend class RelativePointerManager; explicit RelativePointer(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/remote_access.cpp b/src/client/remote_access.cpp index 5a89088..5cee126 100644 --- a/src/client/remote_access.cpp +++ b/src/client/remote_access.cpp @@ -1,246 +1,232 @@ -/**************************************************************************** -Copyright 2016 Oleg Chernovskiy - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "remote_access.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "logging.h" // Wayland #include namespace KWayland { namespace Client { class RemoteAccessManager::Private { public: explicit Private(RemoteAccessManager *ram); void setup(org_kde_kwin_remote_access_manager *k); WaylandPointer ram; EventQueue *queue = nullptr; private: static const struct org_kde_kwin_remote_access_manager_listener s_listener; static void bufferReadyCallback(void *data, org_kde_kwin_remote_access_manager *interface, qint32 buffer_id, wl_output *output); RemoteAccessManager *q; }; RemoteAccessManager::Private::Private(RemoteAccessManager *q) : q(q) { } const org_kde_kwin_remote_access_manager_listener RemoteAccessManager::Private::s_listener = { bufferReadyCallback }; void RemoteAccessManager::Private::bufferReadyCallback(void *data, org_kde_kwin_remote_access_manager *interface, qint32 buffer_id, wl_output *output) { auto ramp = reinterpret_cast(data); Q_ASSERT(ramp->ram == interface); // handle it fully internally, get the buffer immediately auto requested = org_kde_kwin_remote_access_manager_get_buffer(ramp->ram, buffer_id); auto rbuf = new RemoteBuffer(ramp->q); rbuf->setup(requested); qCDebug(KWAYLAND_CLIENT) << "Got buffer, server fd:" << buffer_id; emit ramp->q->bufferReady(output, rbuf); } void RemoteAccessManager::Private::setup(org_kde_kwin_remote_access_manager *k) { Q_ASSERT(k); Q_ASSERT(!ram); ram.setup(k); org_kde_kwin_remote_access_manager_add_listener(k, &s_listener, this); } RemoteAccessManager::RemoteAccessManager(QObject *parent) : QObject(parent) , d(new Private(this)) { } RemoteAccessManager::~RemoteAccessManager() { release(); } void RemoteAccessManager::setup(org_kde_kwin_remote_access_manager *ram) { d->setup(ram); } void RemoteAccessManager::release() { d->ram.release(); } void RemoteAccessManager::destroy() { d->ram.destroy(); } void RemoteAccessManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *RemoteAccessManager::eventQueue() { return d->queue; } RemoteAccessManager::operator org_kde_kwin_remote_access_manager*() { return d->ram; } RemoteAccessManager::operator org_kde_kwin_remote_access_manager*() const { return d->ram; } bool RemoteAccessManager::isValid() const { return d->ram.isValid(); } class RemoteBuffer::Private { public: Private(RemoteBuffer *q); void setup(org_kde_kwin_remote_buffer *buffer); static struct org_kde_kwin_remote_buffer_listener s_listener; static void paramsCallback(void *data, org_kde_kwin_remote_buffer *rbuf, qint32 fd, quint32 width, quint32 height, quint32 stride, quint32 format); WaylandPointer remotebuffer; RemoteBuffer *q; - + qint32 fd = 0; quint32 width = 0; quint32 height = 0; quint32 stride = 0; quint32 format = 0; }; RemoteBuffer::Private::Private(RemoteBuffer *q) : q(q) { } void RemoteBuffer::Private::paramsCallback(void *data, org_kde_kwin_remote_buffer *rbuf, qint32 fd, quint32 width, quint32 height, quint32 stride, quint32 format) { Q_UNUSED(rbuf) Private *p = reinterpret_cast(data); p->fd = fd; p->width = width; p->height = height; p->stride = stride; p->format = format; emit p->q->parametersObtained(); } #ifndef K_DOXYGEN org_kde_kwin_remote_buffer_listener RemoteBuffer::Private::s_listener = { paramsCallback }; #endif void RemoteBuffer::Private::setup(org_kde_kwin_remote_buffer *rbuffer) { remotebuffer.setup(rbuffer); org_kde_kwin_remote_buffer_add_listener(rbuffer, &s_listener, this); } RemoteBuffer::RemoteBuffer(QObject *parent) : QObject(parent) , d(new Private(this)) { } RemoteBuffer::~RemoteBuffer() { release(); qCDebug(KWAYLAND_CLIENT) << "Buffer released"; } void RemoteBuffer::setup(org_kde_kwin_remote_buffer *remotebuffer) { Q_ASSERT(remotebuffer); Q_ASSERT(!d->remotebuffer); d->setup(remotebuffer); } void RemoteBuffer::release() { d->remotebuffer.release(); } void RemoteBuffer::destroy() { d->remotebuffer.destroy(); } RemoteBuffer::operator org_kde_kwin_remote_buffer*() { return d->remotebuffer; } RemoteBuffer::operator org_kde_kwin_remote_buffer*() const { return d->remotebuffer; } bool RemoteBuffer::isValid() const { return d->remotebuffer.isValid(); } qint32 RemoteBuffer::fd() const { return d->fd; } quint32 RemoteBuffer::width() const { return d->width; } quint32 RemoteBuffer::height() const { return d->height; } quint32 RemoteBuffer::stride() const { return d->stride; } quint32 RemoteBuffer::format() const { return d->format; } } } diff --git a/src/client/remote_access.h b/src/client/remote_access.h index 874de4a..4bce779 100644 --- a/src/client/remote_access.h +++ b/src/client/remote_access.h @@ -1,208 +1,194 @@ -/**************************************************************************** -Copyright 2016 Oleg Chernovskiy - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_REMOTE_ACCESS_H #define KWAYLAND_CLIENT_REMOTE_ACCESS_H #include #include struct org_kde_kwin_remote_access_manager; struct org_kde_kwin_remote_buffer; struct wl_output; namespace KWayland { namespace Client { class EventQueue; class RemoteBuffer; /** * @short Wrapper for the org_kde_kwin_remote_access_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_remote_access_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the RemoteAccessManager interface: * @code * RemoteAccessManager *c = registry->createRemoteAccessManager(name, version); * @endcode * * This creates the RemoteAccessManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * RemoteAccessManager *c = new RemoteAccessManager; * c->setup(registry->bindRemoteAccessManager(name, version)); * @endcode * * The RemoteAccessManager can be used as a drop-in replacement for any org_kde_kwin_remote_access_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT RemoteAccessManager : public QObject { Q_OBJECT public: /** * Creates a new RemoteAccessManager. * Note: after constructing the RemoteAccessManager it is not yet valid and one needs * to call setup. In order to get a ready to use RemoteAccessManager prefer using * Registry::createRemoteAccessManager. **/ explicit RemoteAccessManager(QObject *parent = nullptr); virtual ~RemoteAccessManager(); /** * Setup this RemoteAccessManager to manage the @p remoteaccessmanager. * When using Registry::createRemoteAccessManager there is no need to call this * method. **/ void setup(org_kde_kwin_remote_access_manager *remoteaccessmanager); /** * @returns @c true if managing a org_kde_kwin_remote_access_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_remote_access_manager interface. * After the interface has been released the RemoteAccessManager instance is no * longer valid and can be setup with another org_kde_kwin_remote_access_manager interface. **/ void release(); /** * Destroys the data held by this RemoteAccessManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_remote_access_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, remoteaccessmanager, &RemoteAccessManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this RemoteAccessManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this RemoteAccessManager. **/ EventQueue *eventQueue(); operator org_kde_kwin_remote_access_manager*(); operator org_kde_kwin_remote_access_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the RemoteAccessManager got created by * Registry::createRemoteAccessManager **/ void removed(); /** * Buffer from server is ready to be delivered to this client * @param buffer_id internal buffer id to be created **/ void bufferReady(const void* output, const RemoteBuffer *rbuf); private: class Private; QScopedPointer d; }; /** * @short Wrapper for org_kde_kwin_remote_buffer interface. * The instances of this class are created by parent RemoteAccessManager. * Deletion (by noLongerNeeded call) is in responsibility of underlying system. */ class KWAYLANDCLIENT_EXPORT RemoteBuffer : public QObject { Q_OBJECT public: virtual ~RemoteBuffer(); /** * Setup this RemoteBuffer to manage the @p remotebuffer. **/ void setup(org_kde_kwin_remote_buffer *remotebuffer); /** * @returns @c true if managing a org_kde_kwin_remote_buffer. **/ bool isValid() const; /** * Releases the org_kde_kwin_remote_buffer interface. * After the interface has been released the RemoteBuffer instance is no * longer valid and can be setup with another org_kde_kwin_remote_buffer interface. **/ void release(); /** * Destroys the data held by this RemoteBuffer. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_remote_buffer interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, remotebuffer, &RemoteBuffer::destroy); * @endcode * * @see release **/ void destroy(); operator org_kde_kwin_remote_buffer*(); operator org_kde_kwin_remote_buffer*() const; - + qint32 fd() const; quint32 width() const; quint32 height() const; quint32 stride() const; quint32 format() const; Q_SIGNALS: void parametersObtained(); private: friend class RemoteAccessManager; explicit RemoteBuffer(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/seat.cpp b/src/client/seat.cpp index d093d58..c01a469 100644 --- a/src/client/seat.cpp +++ b/src/client/seat.cpp @@ -1,271 +1,257 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "seat.h" #include "event_queue.h" #include "keyboard.h" #include "pointer.h" #include "touch.h" #include "wayland_pointer_p.h" // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Seat::Private { public: Private(Seat *q); void resetSeat(); void setup(wl_seat *seat); WaylandPointer seat; EventQueue *queue = nullptr; bool capabilityKeyboard = false; bool capabilityPointer = false; bool capabilityTouch = false; QString name; private: void setHasKeyboard(bool has); void setHasPointer(bool has); void setHasTouch(bool has); void capabilitiesChanged(uint32_t capabilities); void setName(const QString &name); static void capabilitiesCallback(void *data, wl_seat *seat, uint32_t capabilities); static void nameCallback(void *data, wl_seat *wl_seat, const char *name); Seat *q; static const wl_seat_listener s_listener; }; Seat::Private::Private(Seat *q) : q(q) { } void Seat::Private::setup(wl_seat *s) { Q_ASSERT(s); Q_ASSERT(!seat); seat.setup(s); wl_seat_add_listener(seat, &s_listener, this); } const wl_seat_listener Seat::Private::s_listener = { capabilitiesCallback, nameCallback }; Seat::Seat(QObject *parent) : QObject(parent) , d(new Private(this)) { } Seat::~Seat() { release(); } void Seat::release() { if (!d->seat) { return; } emit interfaceAboutToBeReleased(); d->seat.release(); d->resetSeat(); } void Seat::destroy() { if (!d->seat) { return; } emit interfaceAboutToBeDestroyed(); d->seat.destroy(); d->resetSeat(); } void Seat::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *Seat::eventQueue() { return d->queue; } void Seat::Private::resetSeat() { setHasKeyboard(false); setHasPointer(false); setHasTouch(false); setName(QString()); } void Seat::Private::setHasKeyboard(bool has) { if (capabilityKeyboard == has) { return; } capabilityKeyboard = has; emit q->hasKeyboardChanged(capabilityKeyboard); } void Seat::Private::setHasPointer(bool has) { if (capabilityPointer == has) { return; } capabilityPointer = has; emit q->hasPointerChanged(capabilityPointer); } void Seat::Private::setHasTouch(bool has) { if (capabilityTouch == has) { return; } capabilityTouch = has; emit q->hasTouchChanged(capabilityTouch); } void Seat::setup(wl_seat *seat) { d->setup(seat); } void Seat::Private::capabilitiesCallback(void *data, wl_seat *seat, uint32_t capabilities) { auto s = reinterpret_cast(data); Q_ASSERT(s->seat == seat); s->capabilitiesChanged(capabilities); } void Seat::Private::nameCallback(void *data, wl_seat *seat, const char *name) { auto s = reinterpret_cast(data); Q_ASSERT(s->seat == seat); s->setName(QString::fromUtf8(name)); } void Seat::Private::capabilitiesChanged(uint32_t capabilities) { setHasKeyboard(capabilities & WL_SEAT_CAPABILITY_KEYBOARD); setHasPointer(capabilities & WL_SEAT_CAPABILITY_POINTER); setHasTouch(capabilities & WL_SEAT_CAPABILITY_TOUCH); } Keyboard *Seat::createKeyboard(QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(d->capabilityKeyboard); Keyboard *k = new Keyboard(parent); connect(this, &Seat::interfaceAboutToBeReleased, k, &Keyboard::release); connect(this, &Seat::interfaceAboutToBeDestroyed, k, &Keyboard::destroy); auto w = wl_seat_get_keyboard(d->seat); if (d->queue) { d->queue->addProxy(w); } k->setup(w); return k; } Pointer *Seat::createPointer(QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(d->capabilityPointer); Pointer *p = new Pointer(parent); connect(this, &Seat::interfaceAboutToBeReleased, p, &Pointer::release); connect(this, &Seat::interfaceAboutToBeDestroyed, p, &Pointer::destroy); auto w = wl_seat_get_pointer(d->seat); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } Touch *Seat::createTouch(QObject *parent) { Q_ASSERT(isValid()); Q_ASSERT(d->capabilityTouch); Touch *t = new Touch(parent); connect(this, &Seat::interfaceAboutToBeReleased, t, &Touch::release); connect(this, &Seat::interfaceAboutToBeDestroyed, t, &Touch::destroy); auto w = wl_seat_get_touch(d->seat); if (d->queue) { d->queue->addProxy(w); } t->setup(w); return t; } void Seat::Private::setName(const QString &n) { if (name == n) { return; } name = n; emit q->nameChanged(name); } bool Seat::isValid() const { return d->seat.isValid(); } bool Seat::hasKeyboard() const { return d->capabilityKeyboard; } bool Seat::hasPointer() const { return d->capabilityPointer; } bool Seat::hasTouch() const { return d->capabilityTouch; } QString Seat::name() const { return d->name; } Seat::operator wl_seat*() { return d->seat; } Seat::operator wl_seat*() const { return d->seat; } } } diff --git a/src/client/seat.h b/src/client/seat.h index 62265c2..66cc66a 100644 --- a/src/client/seat.h +++ b/src/client/seat.h @@ -1,206 +1,192 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SEAT_H #define WAYLAND_SEAT_H #include #include struct wl_seat; struct wl_touch; namespace KWayland { namespace Client { class EventQueue; class Keyboard; class Pointer; class Touch; /** * @short Wrapper for the wl_seat interface. * * This class provides a convenient wrapper for the wl_seat interface. * It's main purpose is to provide the interfaces for Keyboard, Pointer and Touch. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Seat interface: * @code * Seat *s = registry->createSeat(name, version); * @endcode * * This creates the Seat and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Seat *s = new Seat; * s->setup(registry->bindSeat(name, version)); * @endcode * * The Seat can be used as a drop-in replacement for any wl_seat * pointer as it provides matching cast operators. * * @see Registry * @see Keyboard * @see Pointer **/ class KWAYLANDCLIENT_EXPORT Seat : public QObject { Q_OBJECT /** * The seat has pointer devices. Default value is @c false. **/ Q_PROPERTY(bool keyboard READ hasKeyboard NOTIFY hasKeyboardChanged) /** * The seat has pointer devices. Default value is @c false. **/ Q_PROPERTY(bool pointer READ hasPointer NOTIFY hasPointerChanged) /** * The seat has touch devices. Default value is @c false. **/ Q_PROPERTY(bool touch READ hasTouch NOTIFY hasTouchChanged) /** * In a multiseat configuration this can be used by the client to help identify * which physical devices the seat represents. * Based on the seat configuration used by the compositor. **/ Q_PROPERTY(QString name READ name NOTIFY nameChanged) public: explicit Seat(QObject *parent = nullptr); virtual ~Seat(); /** * @returns @c true if managing a wl_seat. **/ bool isValid() const; /** * Setup this Seat to manage the @p seat. * When using Registry::createSeat there is no need to call this * method. **/ void setup(wl_seat *seat); /** * Releases the wl_seat interface. * After the interface has been released the Seat instance is no * longer valid and can be setup with another wl_seat interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this Seat. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_shell interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Seat gets destroyed. * * Right before the data is destroyed the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Sets the @p queue to use for creating Keyboard, Pointer and Touch. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating Keyboard, Pointer and Touch. **/ EventQueue *eventQueue(); bool hasKeyboard() const; bool hasPointer() const; bool hasTouch() const; QString name() const; operator wl_seat*(); operator wl_seat*() const; /** * Creates a Keyboard. * * This method may only be called if the Seat has a keyboard. * * @param parent The parent to pass to the created Keyboard. * @returns The created Keyboard. **/ Keyboard *createKeyboard(QObject *parent = nullptr); /** * Creates a Pointer. * * This method may only be called if the Seat has a pointer. * * @param parent The parent to pass to the created Pointer. * @returns The created Pointer. **/ Pointer *createPointer(QObject *parent = nullptr); /** * Creates a Touch. * * This method may only be called if the Seat has touch support. * * @param parent The parent to pass to the created Touch. * @returns The created Touch. **/ Touch *createTouch(QObject *parent = nullptr); Q_SIGNALS: void hasKeyboardChanged(bool); void hasPointerChanged(bool); void hasTouchChanged(bool); void nameChanged(const QString &name); /** * This signal is emitted right before the interface is going to be released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is going to be destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createSeat * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/server_decoration.cpp b/src/client/server_decoration.cpp index 4c3dbc7..20acf40 100644 --- a/src/client/server_decoration.cpp +++ b/src/client/server_decoration.cpp @@ -1,294 +1,280 @@ -/**************************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "server_decoration.h" #include "event_queue.h" #include "logging.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN ServerSideDecorationManager::Private { public: Private() = default; void setup(org_kde_kwin_server_decoration_manager *serversidedecorationmanager); WaylandPointer serversidedecorationmanager; EventQueue *queue = nullptr; ServerSideDecoration::Mode defaultMode = ServerSideDecoration::Mode::None; QVector decorations; private: static void defaultModeCallback(void *data, org_kde_kwin_server_decoration_manager *manager, uint32_t mode); static const struct org_kde_kwin_server_decoration_manager_listener s_listener; }; class Q_DECL_HIDDEN ServerSideDecoration::Private { public: Private(ServerSideDecoration *q); void setup(org_kde_kwin_server_decoration *serversidedecoration); WaylandPointer serversidedecoration; Mode mode = Mode::None; Mode defaultMode = Mode::None; private: static void modeCallback(void *data, org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, uint32_t mode); static const struct org_kde_kwin_server_decoration_listener s_listener; ServerSideDecoration *q; }; #ifndef K_DOXYGEN const org_kde_kwin_server_decoration_manager_listener ServerSideDecorationManager::Private::s_listener = { defaultModeCallback }; #endif void ServerSideDecorationManager::Private::defaultModeCallback(void *data, org_kde_kwin_server_decoration_manager *manager, uint32_t mode) { auto p = reinterpret_cast(data); Q_ASSERT(p->serversidedecorationmanager == manager); ServerSideDecoration::Mode m; switch (mode) { case ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE: m = ServerSideDecoration::Mode::None; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT: m = ServerSideDecoration::Mode::Client; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER: m = ServerSideDecoration::Mode::Server; break; default: // invalid mode cannot set qCWarning(KWAYLAND_CLIENT) << "Invalid decoration mode pushed by Server:" << mode; return; } p->defaultMode = m; // update the default mode on all decorations for (auto it = p->decorations.constBegin(); it != p->decorations.constEnd(); ++it) { (*it)->d->defaultMode = m; // TODO: do we need a signal? } } void ServerSideDecorationManager::Private::setup(org_kde_kwin_server_decoration_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!serversidedecorationmanager); serversidedecorationmanager.setup(manager); org_kde_kwin_server_decoration_manager_add_listener(serversidedecorationmanager, &s_listener, this); } ServerSideDecorationManager::ServerSideDecorationManager(QObject *parent) : QObject(parent) , d(new Private) { } ServerSideDecorationManager::~ServerSideDecorationManager() { release(); } void ServerSideDecorationManager::setup(org_kde_kwin_server_decoration_manager *serversidedecorationmanager) { d->setup(serversidedecorationmanager); } void ServerSideDecorationManager::release() { d->serversidedecorationmanager.release(); } void ServerSideDecorationManager::destroy() { d->serversidedecorationmanager.destroy(); } void ServerSideDecorationManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ServerSideDecorationManager::eventQueue() { return d->queue; } ServerSideDecorationManager::operator org_kde_kwin_server_decoration_manager*() { return d->serversidedecorationmanager; } ServerSideDecorationManager::operator org_kde_kwin_server_decoration_manager*() const { return d->serversidedecorationmanager; } bool ServerSideDecorationManager::isValid() const { return d->serversidedecorationmanager.isValid(); } ServerSideDecoration *ServerSideDecorationManager::create(Surface *surface, QObject *parent) { return create(*surface, parent); } ServerSideDecoration *ServerSideDecorationManager::create(wl_surface *surface, QObject *parent) { Q_ASSERT(isValid()); ServerSideDecoration *deco = new ServerSideDecoration(parent); auto w = org_kde_kwin_server_decoration_manager_create(d->serversidedecorationmanager, surface); if (d->queue) { d->queue->addProxy(w); } deco->d->defaultMode = d->defaultMode; deco->d->mode = d->defaultMode; deco->setup(w); return deco; } #ifndef K_DOXYGEN const org_kde_kwin_server_decoration_listener ServerSideDecoration::Private::s_listener = { modeCallback }; #endif ServerSideDecoration::Private::Private(ServerSideDecoration *q) : q(q) { } void ServerSideDecoration::Private::modeCallback(void *data, org_kde_kwin_server_decoration *decoration, uint32_t mode) { Q_UNUSED(decoration) Private *p = reinterpret_cast(data); Mode m; switch (mode) { case ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE: m = Mode::None; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT: m = Mode::Client; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER: m = Mode::Server; break; default: // invalid mode cannot set qCWarning(KWAYLAND_CLIENT) << "Invalid decoration mode pushed by Server:" << mode; return; } p->mode = m; emit p->q->modeChanged(); } ServerSideDecoration::ServerSideDecoration(QObject *parent) : QObject(parent) , d(new Private(this)) { } ServerSideDecoration::~ServerSideDecoration() { release(); } void ServerSideDecoration::Private::setup(org_kde_kwin_server_decoration *s) { Q_ASSERT(s); Q_ASSERT(!serversidedecoration.isValid()); serversidedecoration.setup(s); org_kde_kwin_server_decoration_add_listener(serversidedecoration, &s_listener, this); } void ServerSideDecoration::setup(org_kde_kwin_server_decoration *serversidedecoration) { d->setup(serversidedecoration); } void ServerSideDecoration::release() { d->serversidedecoration.release(); } void ServerSideDecoration::destroy() { d->serversidedecoration.destroy(); } ServerSideDecoration::operator org_kde_kwin_server_decoration*() { return d->serversidedecoration; } ServerSideDecoration::operator org_kde_kwin_server_decoration*() const { return d->serversidedecoration; } bool ServerSideDecoration::isValid() const { return d->serversidedecoration.isValid(); } void ServerSideDecoration::requestMode(Mode mode) { Q_ASSERT(d->serversidedecoration.isValid()); uint32_t wlMode = 0; switch (mode) { case Mode::None: wlMode = ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE; break; case Mode::Client: wlMode = ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT; break; case Mode::Server: wlMode = ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER; break; default: Q_UNREACHABLE(); } org_kde_kwin_server_decoration_request_mode(d->serversidedecoration, wlMode); } ServerSideDecoration::Mode ServerSideDecoration::mode() const { return d->mode; } ServerSideDecoration::Mode ServerSideDecoration::defaultMode() const { return d->defaultMode; } } } diff --git a/src/client/server_decoration.h b/src/client/server_decoration.h index e450230..69561b8 100644 --- a/src/client/server_decoration.h +++ b/src/client/server_decoration.h @@ -1,254 +1,240 @@ -/**************************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_SERVER_DECORATION_H #define KWAYLAND_CLIENT_SERVER_DECORATION_H #include #include struct org_kde_kwin_server_decoration_manager; struct org_kde_kwin_server_decoration; struct wl_surface; namespace KWayland { namespace Client { class EventQueue; class Surface; class ServerSideDecoration; /** * @short Wrapper for the org_kde_kwin_server_decoration_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_server_decoration_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the ServerSideDecorationManager interface: * @code * ServerSideDecorationManager *c = registry->createServerSideDecorationManager(name, version); * @endcode * * This creates the ServerSideDecorationManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ServerSideDecorationManager *c = new ServerSideDecorationManager; * c->setup(registry->bindServerSideDecorationManager(name, version)); * @endcode * * The ServerSideDecorationManager can be used as a drop-in replacement for any org_kde_kwin_server_decoration_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.6 **/ class KWAYLANDCLIENT_EXPORT ServerSideDecorationManager : public QObject { Q_OBJECT public: /** * Creates a new ServerSideDecorationManager. * Note: after constructing the ServerSideDecorationManager it is not yet valid and one needs * to call setup. In order to get a ready-to-use ServerSideDecorationManager prefer using * Registry::createServerSideDecorationManager. **/ explicit ServerSideDecorationManager(QObject *parent = nullptr); virtual ~ServerSideDecorationManager(); /** * Setup this ServerSideDecorationManager to manage the @p serversidedecorationmanager. * When using Registry::createServerSideDecorationManager there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration_manager *serversidedecorationmanager); /** * @returns @c true if managing a org_kde_kwin_server_decoration_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration_manager interface. * After the interface has been released the ServerSideDecorationManager instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration_manager interface. **/ void release(); /** * Destroys the data held by this ServerSideDecorationManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, serversidedecorationmanager, &ServerSideDecorationManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this ServerSideDecorationManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this ServerSideDecorationManager. **/ EventQueue *eventQueue(); ServerSideDecoration *create(Surface *surface, QObject *parent = nullptr); ServerSideDecoration *create(wl_surface *surface, QObject *parent = nullptr); operator org_kde_kwin_server_decoration_manager*(); operator org_kde_kwin_server_decoration_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the ServerSideDecorationManager got created by * Registry::createServerSideDecorationManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * @brief Describing how a Surface should be decorated. * * Use ServerSideDecorationManager::create to create a ServerSideDecoration. * * @see ServerSideDecorationManager * @since 5.6 **/ class KWAYLANDCLIENT_EXPORT ServerSideDecoration : public QObject { Q_OBJECT public: virtual ~ServerSideDecoration(); /** * Setup this ServerSideDecoration to manage the @p serversidedecoration. * When using ServerSideDecorationManager::createServerSideDecoration there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration *serversidedecoration); /** * @returns @c true if managing a org_kde_kwin_server_decoration. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration interface. * After the interface has been released the ServerSideDecoration instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration interface. **/ void release(); /** * Destroys the data held by this ServerSideDecoration. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * ServerDecoration gets destroyed. * * @see release **/ void destroy(); /** * Decoration mode used for the Surface. **/ enum class Mode { /** * Undecorated: neither client, nor server provide decoration. Example: popups. **/ None, /** * The decoration is part of the surface. **/ Client, /** * The surface gets embedded into a decoration frame provided by the Server. **/ Server, }; /** * Request the decoration @p mode for the Surface. * * The server will acknowledge the change which will trigger the modeChanged signal. * * @see mode * @see modeChanged **/ void requestMode(Mode mode); /** * @returns The current decoration mode for the Surface. * * The mode represents the mode pushed from the Server. * @see requestMode * @see modeChanged **/ Mode mode() const; /** * @returns The default decoration mode the server uses * * @see mode **/ Mode defaultMode() const; operator org_kde_kwin_server_decoration*(); operator org_kde_kwin_server_decoration*() const; Q_SIGNALS: /** * Emitted whenever the Server changes the decoration mode for the Surface. * @see requestMode * @see mode **/ void modeChanged(); private: friend class ServerSideDecorationManager; explicit ServerSideDecoration(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::ServerSideDecoration::Mode) #endif diff --git a/src/client/server_decoration_palette.cpp b/src/client/server_decoration_palette.cpp index d5207e1..2d37a60 100644 --- a/src/client/server_decoration_palette.cpp +++ b/src/client/server_decoration_palette.cpp @@ -1,184 +1,170 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "server_decoration_palette.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class ServerSideDecorationPaletteManager::Private { public: Private() = default; void setup(org_kde_kwin_server_decoration_palette_manager *arg); WaylandPointer serverdecomanager; EventQueue *queue = nullptr; }; ServerSideDecorationPaletteManager::ServerSideDecorationPaletteManager(QObject *parent) : QObject(parent) , d(new Private) { } void ServerSideDecorationPaletteManager::Private::setup(org_kde_kwin_server_decoration_palette_manager *arg) { Q_ASSERT(arg); Q_ASSERT(!serverdecomanager); serverdecomanager.setup(arg); } ServerSideDecorationPaletteManager::~ServerSideDecorationPaletteManager() { release(); } void ServerSideDecorationPaletteManager::setup(org_kde_kwin_server_decoration_palette_manager *serverdecomanager) { d->setup(serverdecomanager); } void ServerSideDecorationPaletteManager::release() { d->serverdecomanager.release(); } void ServerSideDecorationPaletteManager::destroy() { d->serverdecomanager.destroy(); } ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() { return d->serverdecomanager; } ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() const { return d->serverdecomanager; } bool ServerSideDecorationPaletteManager::isValid() const { return d->serverdecomanager.isValid(); } void ServerSideDecorationPaletteManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ServerSideDecorationPaletteManager::eventQueue() { return d->queue; } ServerSideDecorationPalette *ServerSideDecorationPaletteManager::create(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new ServerSideDecorationPalette(parent); auto w = org_kde_kwin_server_decoration_palette_manager_create(d->serverdecomanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class ServerSideDecorationPalette::Private { public: Private(ServerSideDecorationPalette *q); void setup(org_kde_kwin_server_decoration_palette *arg); WaylandPointer decoration_palette; private: ServerSideDecorationPalette *q; }; ServerSideDecorationPalette::Private::Private(ServerSideDecorationPalette *q) : q(q) { } ServerSideDecorationPalette::ServerSideDecorationPalette(QObject *parent) : QObject(parent) , d(new Private(this)) { } void ServerSideDecorationPalette::Private::setup(org_kde_kwin_server_decoration_palette *arg) { Q_ASSERT(arg); Q_ASSERT(!decoration_palette); decoration_palette.setup(arg); } ServerSideDecorationPalette::~ServerSideDecorationPalette() { release(); } void ServerSideDecorationPalette::setup(org_kde_kwin_server_decoration_palette *decoration_palette) { d->setup(decoration_palette); } void ServerSideDecorationPalette::release() { d->decoration_palette.release(); } void ServerSideDecorationPalette::destroy() { d->decoration_palette.destroy(); } ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() { return d->decoration_palette; } ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() const { return d->decoration_palette; } bool ServerSideDecorationPalette::isValid() const { return d->decoration_palette.isValid(); } void ServerSideDecorationPalette::setPalette(const QString &palette) { Q_ASSERT(isValid()); org_kde_kwin_server_decoration_palette_set_palette(*this, palette.toUtf8()); } } } diff --git a/src/client/server_decoration_palette.h b/src/client/server_decoration_palette.h index 85d0932..2e3417a 100644 --- a/src/client/server_decoration_palette.h +++ b/src/client/server_decoration_palette.h @@ -1,198 +1,184 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_SERVER_DECORATION_PALETTE_H #define KWAYLAND_CLIENT_SERVER_DECORATION_PALETTE_H #include #include struct org_kde_kwin_server_decoration_palette_manager; struct org_kde_kwin_server_decoration_palette; namespace KWayland { namespace Client { class EventQueue; class Surface; class ServerSideDecorationPalette; /** * @short Wrapper for the org_kde_kwin_server_decoration_palette_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_server_decoration_palette_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the ServerSideDecorationPaletteManager interface: * @code * ServerSideDecorationPaletteManager *c = registry->createServerSideDecorationPaletteManager(name, version); * @endcode * * This creates the ServerSideDecorationPaletteManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ServerSideDecorationPaletteManager *c = new ServerSideDecorationPaletteManager; * c->setup(registry->bindServerSideDecorationPaletteManager(name, version)); * @endcode * * The ServerSideDecorationPaletteManager can be used as a drop-in replacement for any org_kde_kwin_server_decoration_palette_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT ServerSideDecorationPaletteManager : public QObject { Q_OBJECT public: /** * Creates a new ServerSideDecorationPaletteManager. * Note: after constructing the ServerSideDecorationPaletteManager it is not yet valid and one needs * to call setup. In order to get a ready to use ServerSideDecorationPaletteManager prefer using * Registry::createServerSideDecorationPaletteManager. **/ explicit ServerSideDecorationPaletteManager(QObject *parent = nullptr); virtual ~ServerSideDecorationPaletteManager(); /** * Setup this ServerSideDecorationPaletteManager to manage the @p serverSideDecorationPaletteManager. * When using Registry::createServerSideDecorationPaletteManager there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration_palette_manager *serverSideDecorationPaletteManager); /** * @returns @c true if managing a org_kde_kwin_server_decoration_palette_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration_palette_manager interface. * After the interface has been released the ServerSideDecorationPaletteManager instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration_palette_manager interface. **/ void release(); /** * Destroys the data held by this ServerSideDecorationPaletteManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration_palette_manager interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, palettemanager, &ServerSideDecorationPaletteManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this ServerSideDecorationPaletteManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this ServerSideDecorationPaletteManager. **/ EventQueue *eventQueue(); ServerSideDecorationPalette *create(Surface *surface, QObject *parent = nullptr); operator org_kde_kwin_server_decoration_palette_manager*(); operator org_kde_kwin_server_decoration_palette_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the ServerSideDecorationPaletteManager got created by * Registry::createServerSideDecorationPaletteManager **/ void removed(); private: class Private; QScopedPointer d; }; class KWAYLANDCLIENT_EXPORT ServerSideDecorationPalette : public QObject { Q_OBJECT public: virtual ~ServerSideDecorationPalette(); /** * Setup this ServerSideDecorationPalette to manage the @p serversidedecorationpalette. * When using ServerSideDecorationPaletteManager::create there is no need to call this * method. **/ void setup(org_kde_kwin_server_decoration_palette *serversidedecorationpalette); /** * @returns @c true if managing a org_kde_kwin_server_decoration_palette. **/ bool isValid() const; /** * Releases the org_kde_kwin_server_decoration_palette interface. * After the interface has been released the ServerSideDecorationPalette instance is no * longer valid and can be setup with another org_kde_kwin_server_decoration_palette interface. **/ void release(); /** * Destroys the data held by this ServerSideDecorationPalette. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_server_decoration_palette interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, palette, &ServerSideDecorationPalette::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the palette to be used by the server side decorations. * Absolute file path, or name of palette in the user's config directory. * If set to empty the default palette will be used. */ void setPalette(const QString &palette); operator org_kde_kwin_server_decoration_palette*(); operator org_kde_kwin_server_decoration_palette*() const; private: friend class ServerSideDecorationPaletteManager; explicit ServerSideDecorationPalette(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/shadow.cpp b/src/client/shadow.cpp index 6e94735..ea558ce 100644 --- a/src/client/shadow.cpp +++ b/src/client/shadow.cpp @@ -1,211 +1,197 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "shadow.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN ShadowManager::Private { public: Private() = default; WaylandPointer manager; EventQueue *queue = nullptr; }; ShadowManager::ShadowManager(QObject *parent) : QObject(parent) , d(new Private) { } ShadowManager::~ShadowManager() { release(); } void ShadowManager::release() { d->manager.release(); } void ShadowManager::destroy() { d->manager.destroy(); } bool ShadowManager::isValid() const { return d->manager.isValid(); } void ShadowManager::setup(org_kde_kwin_shadow_manager *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager); d->manager.setup(manager); } void ShadowManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ShadowManager::eventQueue() { return d->queue; } Shadow *ShadowManager::createShadow(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); Shadow *s = new Shadow(parent); auto w = org_kde_kwin_shadow_manager_create(d->manager, *surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } void ShadowManager::removeShadow(Surface *surface) { Q_ASSERT(isValid()); org_kde_kwin_shadow_manager_unset(d->manager, *surface); } ShadowManager::operator org_kde_kwin_shadow_manager*() { return d->manager; } ShadowManager::operator org_kde_kwin_shadow_manager*() const { return d->manager; } class Shadow::Private { public: WaylandPointer shadow; }; Shadow::Shadow(QObject *parent) : QObject(parent) , d(new Private) { } Shadow::~Shadow() { release(); } void Shadow::release() { d->shadow.release(); } void Shadow::setup(org_kde_kwin_shadow *shadow) { Q_ASSERT(shadow); Q_ASSERT(!d->shadow); d->shadow.setup(shadow); } void Shadow::destroy() { d->shadow.destroy(); } bool Shadow::isValid() const { return d->shadow.isValid(); } void Shadow::setOffsets(const QMarginsF &margins) { Q_ASSERT(isValid()); org_kde_kwin_shadow_set_left_offset(d->shadow, wl_fixed_from_double(margins.left())); org_kde_kwin_shadow_set_top_offset(d->shadow, wl_fixed_from_double(margins.top())); org_kde_kwin_shadow_set_right_offset(d->shadow, wl_fixed_from_double(margins.right())); org_kde_kwin_shadow_set_bottom_offset(d->shadow, wl_fixed_from_double(margins.bottom())); } void Shadow::commit() { Q_ASSERT(isValid()); org_kde_kwin_shadow_commit(d->shadow); } #ifndef K_DOXYGEN #define attach( __PART__, __WAYLAND_PART__ ) \ void Shadow::attach##__PART__(wl_buffer *buffer) \ { \ Q_ASSERT(isValid()); \ org_kde_kwin_shadow_attach_##__WAYLAND_PART__(d->shadow, buffer); \ } \ void Shadow::attach##__PART__(Buffer *buffer) \ { \ if (!buffer) {\ return;\ }\ attach##__PART__(buffer->buffer()); \ } \ void Shadow::attach##__PART__(Buffer::Ptr buffer) \ { \ attach##__PART__(buffer.toStrongRef().data()); \ } attach(Left, left) attach(TopLeft, top_left) attach(Top, top) attach(TopRight, top_right) attach(Right, right) attach(BottomRight, bottom_right) attach(Bottom, bottom) attach(BottomLeft, bottom_left) #undef attach #endif Shadow::operator org_kde_kwin_shadow*() { return d->shadow; } Shadow::operator org_kde_kwin_shadow*() const { return d->shadow; } } } diff --git a/src/client/shadow.h b/src/client/shadow.h index 82da51a..be2fe49 100644 --- a/src/client/shadow.h +++ b/src/client/shadow.h @@ -1,246 +1,232 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SHADOW_H #define KWAYLAND_SHADOW_H #include "buffer.h" #include #include #include #include struct wl_buffer; struct org_kde_kwin_shadow; struct org_kde_kwin_shadow_manager; class QMarginsF; class QWindow; namespace KWayland { namespace Client { class EventQueue; class Shadow; class Surface; /** * @short Wrapper for the org_kde_kwin_shadow_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_shadow_manager interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the ShadowManager interface: * @code * ShadowManager *s = registry->createShadowManager(name, version); * @endcode * * This creates the ShadowManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ShadowManager *s = new ShadowManager; * s->setup(registry->bindShadowManager(name, version)); * @endcode * * The ShadowManager can be used as a drop-in replacement for any org_kde_kwin_shadow_manager * pointer as it provides matching cast operators. * * @see Registry * @since 5.4 **/ class KWAYLANDCLIENT_EXPORT ShadowManager : public QObject { Q_OBJECT public: /** * Creates a new ShadowManager. * Note: after constructing the ShadowManager it is not yet valid and one needs * to call setup. In order to get a ready to use ShadowManager prefer using * Registry::createShadowManager. **/ explicit ShadowManager(QObject *parent = nullptr); virtual ~ShadowManager(); /** * @returns @c true if managing a org_kde_kwin_shadow_manager. **/ bool isValid() const; /** * Setup this ShadowManager to manage the @p compositor. * When using Registry::createShadowManager there is no need to call this * method. **/ void setup(org_kde_kwin_shadow_manager *compositor); /** * Releases the org_kde_kwin_shadow_manager interface. * After the interface has been released the ShadowManager instance is no * longer valid and can be setup with another org_kde_kwin_shadow_manager interface. **/ void release(); /** * Destroys the data held by this ShadowManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_shadow_manager interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Shadow gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Shadow. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Shadow. **/ EventQueue *eventQueue(); /** * Creates and setup a new Shadow with @p parent. * @param parent The parent to pass to the Shadow. * @returns The new created Shadow **/ Shadow *createShadow(Surface *surface, QObject *parent = nullptr); void removeShadow(Surface *surface); operator org_kde_kwin_shadow_manager*(); operator org_kde_kwin_shadow_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createShadowManager * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_kwin_shadow interface. * * This class is a convenient wrapper for the org_kde_kwin_shadow interface. * To create a Shadow call Compositor::createShadow. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see Compositor **/ class KWAYLANDCLIENT_EXPORT Shadow : public QObject { Q_OBJECT public: virtual ~Shadow(); /** * Setup this Shadow to manage the @p shadow. * When using Compositor::createSurface there is no need to call this * method. **/ void setup(org_kde_kwin_shadow *shadow); /** * Releases the org_kde_kwin_shadow interface. * After the interface has been released the Shadow instance is no * longer valid and can be setup with another org_kde_kwin_shadow interface. **/ void release(); /** * Destroys the data held by this Shadow. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_shadow interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, shadow, &Shadow::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_kwin_shadow. **/ bool isValid() const; void commit(); void attachLeft(wl_buffer *buffer); void attachLeft(Buffer *buffer); void attachLeft(Buffer::Ptr buffer); void attachTopLeft(wl_buffer *buffer); void attachTopLeft(Buffer *buffer); void attachTopLeft(Buffer::Ptr buffer); void attachTop(wl_buffer *buffer); void attachTop(Buffer *buffer); void attachTop(Buffer::Ptr buffer); void attachTopRight(wl_buffer *buffer); void attachTopRight(Buffer *buffer); void attachTopRight(Buffer::Ptr buffer); void attachRight(wl_buffer *buffer); void attachRight(Buffer *buffer); void attachRight(Buffer::Ptr buffer); void attachBottomRight(wl_buffer *buffer); void attachBottomRight(Buffer *buffer); void attachBottomRight(Buffer::Ptr buffer); void attachBottom(wl_buffer *buffer); void attachBottom(Buffer *buffer); void attachBottom(Buffer::Ptr buffer); void attachBottomLeft(wl_buffer *buffer); void attachBottomLeft(Buffer *buffer); void attachBottomLeft(Buffer::Ptr buffer); void setOffsets(const QMarginsF &margins); operator org_kde_kwin_shadow*(); operator org_kde_kwin_shadow*() const; private: friend class ShadowManager; explicit Shadow(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/shell.cpp b/src/client/shell.cpp index c4ec0ed..94ea0f6 100644 --- a/src/client/shell.cpp +++ b/src/client/shell.cpp @@ -1,397 +1,383 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "shell.h" #include "event_queue.h" #include "output.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include #include #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Shell::Private { public: WaylandPointer shell; EventQueue *queue = nullptr; }; Shell::Shell(QObject *parent) : QObject(parent) , d(new Private) { } Shell::~Shell() { release(); } void Shell::destroy() { if (!d->shell) { return; } emit interfaceAboutToBeDestroyed(); d->shell.destroy(); } void Shell::release() { if (!d->shell) { return; } emit interfaceAboutToBeReleased(); d->shell.release(); } void Shell::setup(wl_shell *shell) { Q_ASSERT(!d->shell); Q_ASSERT(shell); d->shell.setup(shell); } void Shell::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *Shell::eventQueue() { return d->queue; } ShellSurface *Shell::createSurface(wl_surface *surface, QObject *parent) { Q_ASSERT(isValid()); ShellSurface *s = new ShellSurface(parent); connect(this, &Shell::interfaceAboutToBeReleased, s, &ShellSurface::release); connect(this, &Shell::interfaceAboutToBeDestroyed, s, &ShellSurface::destroy); auto w = wl_shell_get_shell_surface(d->shell, surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } ShellSurface *Shell::createSurface(Surface *surface, QObject *parent) { Q_ASSERT(surface); return createSurface(*surface, parent); } bool Shell::isValid() const { return d->shell.isValid(); } Shell::operator wl_shell*() { return d->shell; } Shell::operator wl_shell*() const { return d->shell; } class Q_DECL_HIDDEN ShellSurface::Private { public: Private(ShellSurface *q); void setup(wl_shell_surface *surface); WaylandPointer surface; QSize size; static QVector s_surfaces; private: void ping(uint32_t serial); static void pingCallback(void *data, struct wl_shell_surface *shellSurface, uint32_t serial); static void configureCallback(void *data, struct wl_shell_surface *shellSurface, uint32_t edges, int32_t width, int32_t height); static void popupDoneCallback(void *data, struct wl_shell_surface *shellSurface); ShellSurface *q; static const struct wl_shell_surface_listener s_listener; }; QVector ShellSurface::Private::s_surfaces = QVector(); ShellSurface::Private::Private(ShellSurface *q) : q(q) { } void ShellSurface::Private::setup(wl_shell_surface *s) { Q_ASSERT(s); Q_ASSERT(!surface); surface.setup(s); wl_shell_surface_add_listener(surface, &s_listener, this); } ShellSurface *ShellSurface::fromWindow(QWindow *window) { if (!window) { return nullptr; } QPlatformNativeInterface *native = qApp->platformNativeInterface(); if (!native) { return nullptr; } window->create(); wl_shell_surface *s = reinterpret_cast(native->nativeResourceForWindow(QByteArrayLiteral("wl_shell_surface"), window)); if (!s) { return nullptr; } if (auto surface = get(s)) { return surface; } ShellSurface *surface = new ShellSurface(window); surface->d->surface.setup(s, true); return surface; } ShellSurface *ShellSurface::fromQtWinId(WId wid) { QWindow *window = nullptr; for (auto win : qApp->allWindows()) { if (win->winId() == wid) { window = win; break; } } if (!window) { return nullptr; } return fromWindow(window); } ShellSurface *ShellSurface::get(wl_shell_surface *native) { auto it = std::find_if(Private::s_surfaces.constBegin(), Private::s_surfaces.constEnd(), [native](ShellSurface *s) { return s->d->surface == native; } ); if (it != Private::s_surfaces.constEnd()) { return *(it); } return nullptr; } ShellSurface::ShellSurface(QObject *parent) : QObject(parent) , d(new Private(this)) { Private::s_surfaces << this; } ShellSurface::~ShellSurface() { Private::s_surfaces.removeOne(this); release(); } void ShellSurface::release() { d->surface.release(); } void ShellSurface::destroy() { d->surface.destroy(); } #ifndef K_DOXYGEN const struct wl_shell_surface_listener ShellSurface::Private::s_listener = { pingCallback, configureCallback, popupDoneCallback }; #endif void ShellSurface::Private::configureCallback(void *data, wl_shell_surface *shellSurface, uint32_t edges, int32_t width, int32_t height) { Q_UNUSED(edges) auto s = reinterpret_cast(data); Q_ASSERT(s->surface == shellSurface); s->q->setSize(QSize(width, height)); } void ShellSurface::Private::pingCallback(void *data, wl_shell_surface *shellSurface, uint32_t serial) { auto s = reinterpret_cast(data); Q_ASSERT(s->surface == shellSurface); s->ping(serial); } void ShellSurface::Private::popupDoneCallback(void *data, wl_shell_surface *shellSurface) { auto s = reinterpret_cast(data); Q_ASSERT(s->surface == shellSurface); emit s->q->popupDone(); } void ShellSurface::setup(wl_shell_surface *surface) { d->setup(surface); } void ShellSurface::Private::ping(uint32_t serial) { wl_shell_surface_pong(surface, serial); emit q->pinged(); } void ShellSurface::setSize(const QSize &size) { if (d->size == size) { return; } d->size = size; emit sizeChanged(size); } void ShellSurface::setFullscreen(Output *output) { Q_ASSERT(isValid()); wl_shell_surface_set_fullscreen(d->surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, output ? output->output() : nullptr); } void ShellSurface::setMaximized(Output *output) { Q_ASSERT(isValid()); wl_shell_surface_set_maximized(d->surface, output ? output->output() : nullptr); } void ShellSurface::setToplevel() { Q_ASSERT(isValid()); wl_shell_surface_set_toplevel(d->surface); } void ShellSurface::setTransient(Surface *parent, const QPoint &offset, TransientFlags flags) { Q_ASSERT(isValid()); uint32_t wlFlags = 0; if (flags.testFlag(TransientFlag::NoFocus)) { wlFlags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; } wl_shell_surface_set_transient(d->surface, *parent, offset.x(), offset.y(), wlFlags); } void ShellSurface::setTransientPopup(Surface *parent, Seat *grabbedSeat, quint32 grabSerial, const QPoint &offset, TransientFlags flags) { Q_ASSERT(isValid()); Q_ASSERT(parent); Q_ASSERT(grabbedSeat); uint32_t wlFlags = 0; if (flags.testFlag(TransientFlag::NoFocus)) { wlFlags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE; } wl_shell_surface_set_popup(d->surface, *grabbedSeat, grabSerial, *parent, offset.x(), offset.y(), wlFlags); } void ShellSurface::requestMove(Seat *seat, quint32 serial) { Q_ASSERT(isValid()); Q_ASSERT(seat); wl_shell_surface_move(d->surface, *seat, serial); } void ShellSurface::requestResize(Seat *seat, quint32 serial, Qt::Edges edges) { Q_ASSERT(isValid()); Q_ASSERT(seat); uint wlEdge = WL_SHELL_SURFACE_RESIZE_NONE; if (edges.testFlag(Qt::TopEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) { wlEdge = WL_SHELL_SURFACE_RESIZE_TOP_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) { wlEdge = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) { wlEdge = WL_SHELL_SURFACE_RESIZE_TOP; } } else if (edges.testFlag(Qt::BottomEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) { wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) { wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) { wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM; } } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) { wlEdge = WL_SHELL_SURFACE_RESIZE_RIGHT; } else if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) { wlEdge = WL_SHELL_SURFACE_RESIZE_LEFT; } wl_shell_surface_resize(d->surface, *seat, serial, wlEdge); } void ShellSurface::setTitle(const QString &title) { wl_shell_surface_set_title(d->surface, title.toUtf8().constData()); } void ShellSurface::setWindowClass(const QByteArray &windowClass) { wl_shell_surface_set_class(d->surface, windowClass.constData()); } QSize ShellSurface::size() const { return d->size; } bool ShellSurface::isValid() const { return d->surface.isValid(); } ShellSurface::operator wl_shell_surface*() { return d->surface; } ShellSurface::operator wl_shell_surface*() const { return d->surface; } } } diff --git a/src/client/shell.h b/src/client/shell.h index ffac34c..24094b6 100644 --- a/src/client/shell.h +++ b/src/client/shell.h @@ -1,372 +1,358 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SHELL_H #define WAYLAND_SHELL_H #include #include #include #include #include struct wl_surface; struct wl_shell; struct wl_shell_surface; namespace KWayland { namespace Client { class EventQueue; class ShellSurface; class Output; class Seat; class Surface; /** * @short Wrapper for the wl_shell interface. * * This class provides a convenient wrapper for the wl_shell interface. * It's main purpose is to create a ShellSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * Shell *s = registry->createShell(name, version); * @endcode * * This creates the Shell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * Shell *s = new Shell; * s->setup(registry->bindShell(name, version)); * @endcode * * The Shell can be used as a drop-in replacement for any wl_shell * pointer as it provides matching cast operators. * * @see Registry * @see ShellSurface **/ class KWAYLANDCLIENT_EXPORT Shell : public QObject { Q_OBJECT public: explicit Shell(QObject *parent = nullptr); virtual ~Shell(); /** * @returns @c true if managing a wl_shell. **/ bool isValid() const; /** * Releases the wl_shell interface. * After the interface has been released the Shell instance is no * longer valid and can be setup with another wl_shell interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this Shell. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_shell interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, shell, &Shell::destroy); * @endcode * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(wl_shell *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); /** * Creates a ShellSurface for the given @p surface and sets it up. * * @param surface The native surface to create the ShellSurface for * @param parent The parent to use for the ShellSurface * @returns created ShellSurface **/ ShellSurface *createSurface(wl_surface *surface, QObject *parent = nullptr); /** * Creates a ShellSurface for the given @p surface and sets it up. * * @param surface The Surface to create the ShellSurface for * @param parent The parent to use for the ShellSurface * @returns created ShellSurface **/ ShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); operator wl_shell*(); operator wl_shell*() const; Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createShell * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the wl_shell_surface interface. * * This class is a convenient wrapper for the wl_shell_surface interface. * * To create an instance use Shell::createSurface. * * @see Shell * @see Surface **/ class KWAYLANDCLIENT_EXPORT ShellSurface : public QObject { Q_OBJECT /** * The size of the ShellSurface. **/ Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged) public: explicit ShellSurface(QObject *parent); virtual ~ShellSurface(); /** * Releases the wl_shell_surface interface. * After the interface has been released the ShellSurface instance is no * longer valid and can be setup with another wl_shell_surface interface. * * This method is automatically invoked when the Shell which created this * ShellSurface gets released. **/ void release(); /** * Destroys the data held by this ShellSurface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_shell_surface interface * once there is a new connection available. * * This method is automatically invoked when the Shell which created this * ShellSurface gets destroyed. * * @see release **/ void destroy(); /** * Setup this ShellSurface to manage the @p surface. * There is normally no need to call this method as it's invoked by * Shell::createSurface. **/ void setup(wl_shell_surface *surface); QSize size() const; void setSize(const QSize &size); /** * Sets the ShellSurface fullscreen on @p output. **/ void setFullscreen(Output *output = nullptr); void setMaximized(Output *output = nullptr); void setToplevel(); /** * Flags which can be passed to a transient surface. * @see setTransient * @since 5.5 **/ enum class TransientFlag { Default = 0x0, ///< Default: transient surface accepts keyboard focus NoFocus = 0x1 ///< Transient surface does not accept keyboard focus }; Q_DECLARE_FLAGS(TransientFlags, TransientFlag) /** * Sets this Surface as a transient for @p parent. * * @param parent The parent Surface of this surface * @param offset The offset of this Surface in the parent coordinate system * @param flags The flags for the transient * @since 5.5 **/ void setTransient(Surface *parent, const QPoint &offset = QPoint(), TransientFlags flags = TransientFlag::Default); /** * Sets this Surface as a popup transient for @p parent. * * A popup is a transient with an added pointer grab on the @p grabbedSeat. * * The popup grab can be created if the client has an implicit grab (e.g. button press) * on the @p grabbedSeat. It needs to pass the @p grabSerial indicating the implicit grab * to the request for setting the surface. The implicit grab is turned into a popup grab * which will persist after the implicit grab ends. The popup grab ends when the ShellSurface * gets destroyed or when the compositor breaks the grab through the {@link popupDone} signal. * * @param parent The parent Surface of this ShellSurface * @param grabbedSeat The Seat on which an implicit grab exists * @param grabSerial The serial of the implicit grab * @param offset The offset of this Surface in the parent coordinate system * @param flags The flags for the transient * @since 5.33 **/ void setTransientPopup(Surface *parent, Seat *grabbedSeat, quint32 grabSerial, const QPoint &offset = QPoint(), TransientFlags flags = TransientFlag::Default); bool isValid() const; /** * Requests a move on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to move the window * @param serial The serial of the pointer button press which should trigger the move * @since 5.5 **/ void requestMove(Seat *seat, quint32 serial); /** * Requests a resize on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to resize the window * @param serial The serial of the pointer button press which should trigger the resize * @param edges A hint for the compositor to set e.g. an appropriate cursor image * @since 5.5 **/ void requestResize(Seat *seat, quint32 serial, Qt::Edges edges); /** * Sets a short title for the surface. * * This string may be used to identify the surface in a task bar, window list, or other user * interface elements provided by the compositor. * * @since 5.55 **/ void setTitle(const QString &title); /** * Sets a window class for the surface. * * The surface class identifies the general class of applications to which the surface belongs. * A common convention is to use the file name (or the full path if it is a non-standard location) * of the application's .desktop file as the class. * * @since 5.55 **/ void setWindowClass(const QByteArray &windowClass); /** * Creates a ShellSurface for the given @p window. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned ShellSurface will be fully setup, but won't be released. It gets automatically * destroyed together with the @p window. * @since 5.28 **/ static ShellSurface *fromWindow(QWindow *window); /** * Creates a ShellSurface for the given @p winId. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned ShellSurface will be fully setup, but won't be released. It gets automatically * destroyed together with the QWindow corresponding * the @p wid. * @since 5.28 **/ static ShellSurface *fromQtWinId(WId wid); /** * @returns The Surface referencing the @p native wl_surface or @c null if there is no such Surface. * @since 5.28 **/ static ShellSurface *get(wl_shell_surface *native); operator wl_shell_surface*(); operator wl_shell_surface*() const; Q_SIGNALS: /** * Signal is emitted when the ShellSurface received a ping request. * The ShellSurface automatically responds to the ping. **/ void pinged(); void sizeChanged(const QSize &); /** * The popupDone signal is sent out when a popup grab is broken, that is, * when the user clicks a surface that doesn't belong to the client owning * the popup surface. * @see setTransientPopup * @since 5.33 **/ void popupDone(); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::ShellSurface::TransientFlag) Q_DECLARE_METATYPE(KWayland::Client::ShellSurface::TransientFlags) #endif diff --git a/src/client/shm_pool.cpp b/src/client/shm_pool.cpp index f1de351..5475e02 100644 --- a/src/client/shm_pool.cpp +++ b/src/client/shm_pool.cpp @@ -1,291 +1,277 @@ -/******************************************************************** -Copyright 2013 Martin Gräßlin +/* + SPDX-FileCopyrightText: 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "shm_pool.h" #include "event_queue.h" #include "buffer.h" #include "buffer_p.h" #include "logging.h" #include "wayland_pointer_p.h" // Qt #include #include #include // system #include #include // wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN ShmPool::Private { public: Private(ShmPool *q); bool createPool(); bool resizePool(int32_t newSize); QList>::iterator getBuffer(const QSize &size, int32_t stride, Buffer::Format format); WaylandPointer shm; WaylandPointer pool; void *poolData = nullptr; int32_t size = 1024; QScopedPointer tmpFile; bool valid = false; int offset = 0; QList> buffers; EventQueue *queue = nullptr; private: ShmPool *q; }; ShmPool::Private::Private(ShmPool *q) : tmpFile(new QTemporaryFile()) , q(q) { } ShmPool::ShmPool(QObject *parent) : QObject(parent) , d(new Private(this)) { } ShmPool::~ShmPool() { release(); } void ShmPool::release() { d->buffers.clear(); if (d->poolData) { munmap(d->poolData, d->size); d->poolData = nullptr; } d->pool.release(); d->shm.release(); d->tmpFile->close(); d->valid = false; d->offset = 0; } void ShmPool::destroy() { for (auto b : d->buffers) { b->d->destroy(); } d->buffers.clear(); if (d->poolData) { munmap(d->poolData, d->size); d->poolData = nullptr; } d->pool.destroy(); d->shm.destroy(); d->tmpFile->close(); d->valid = false; d->offset = 0; } void ShmPool::setup(wl_shm *shm) { Q_ASSERT(shm); Q_ASSERT(!d->shm); d->shm.setup(shm); d->valid = d->createPool(); } void ShmPool::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ShmPool::eventQueue() { return d->queue; } bool ShmPool::Private::createPool() { if (!tmpFile->open()) { qCDebug(KWAYLAND_CLIENT) << "Could not open temporary file for Shm pool"; return false; } if (unlink(tmpFile->fileName().toUtf8().constData()) != 0) { qCDebug(KWAYLAND_CLIENT) << "Unlinking temporary file for Shm pool from file system failed"; } if (ftruncate(tmpFile->handle(), size) < 0) { qCDebug(KWAYLAND_CLIENT) << "Could not set size for Shm pool file"; return false; } poolData = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); pool.setup(wl_shm_create_pool(shm, tmpFile->handle(), size)); if (!poolData || !pool) { qCDebug(KWAYLAND_CLIENT) << "Creating Shm pool failed"; return false; } return true; } bool ShmPool::Private::resizePool(int32_t newSize) { if (ftruncate(tmpFile->handle(), newSize) < 0) { qCDebug(KWAYLAND_CLIENT) << "Could not set new size for Shm pool file"; return false; } wl_shm_pool_resize(pool, newSize); munmap(poolData, size); poolData = mmap(nullptr, newSize, PROT_READ | PROT_WRITE, MAP_SHARED, tmpFile->handle(), 0); size = newSize; if (!poolData) { qCDebug(KWAYLAND_CLIENT) << "Resizing Shm pool failed"; return false; } emit q->poolResized(); return true; } namespace { static Buffer::Format toBufferFormat(const QImage &image) { switch (image.format()) { case QImage::Format_ARGB32_Premultiplied: return Buffer::Format::ARGB32; case QImage::Format_RGB32: return Buffer::Format::RGB32; case QImage::Format_ARGB32: qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied"; return Buffer::Format::ARGB32; default: qCWarning(KWAYLAND_CLIENT) << "Unsupported image format: " << image.format() << ". expect slow performance."; return Buffer::Format::ARGB32; } } } Buffer::Ptr ShmPool::createBuffer(const QImage& image) { if (image.isNull() || !d->valid) { return QWeakPointer(); } auto format = toBufferFormat(image); auto it = d->getBuffer(image.size(), image.bytesPerLine(), format); if (it == d->buffers.end()) { return QWeakPointer(); } if (format == Buffer::Format::ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) { auto imageCopy = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); (*it)->copy(imageCopy.bits()); } else { (*it)->copy(image.bits()); } return QWeakPointer(*it); } Buffer::Ptr ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format) { if (size.isEmpty() || !d->valid) { return QWeakPointer(); } auto it = d->getBuffer(size, stride, format); if (it == d->buffers.end()) { return QWeakPointer(); } (*it)->copy(src); return QWeakPointer(*it); } namespace { static wl_shm_format toWaylandFormat(Buffer::Format format) { switch (format) { case Buffer::Format::ARGB32: return WL_SHM_FORMAT_ARGB8888; case Buffer::Format::RGB32: return WL_SHM_FORMAT_XRGB8888; } abort(); } } Buffer::Ptr ShmPool::getBuffer(const QSize &size, int32_t stride, Buffer::Format format) { auto it = d->getBuffer(size, stride, format); if (it == d->buffers.end()) { return QWeakPointer(); } return QWeakPointer(*it); } QList>::iterator ShmPool::Private::getBuffer(const QSize &s, int32_t stride, Buffer::Format format) { for (auto it = buffers.begin(); it != buffers.end(); ++it) { auto buffer = *it; if (!buffer->isReleased() || buffer->isUsed()) { continue; } if (buffer->size() != s || buffer->stride() != stride || buffer->format() != format) { continue; } buffer->setReleased(false); return it; } const int32_t byteCount = s.height() * stride; if (offset + byteCount > size) { if (!resizePool(size + byteCount)) { return buffers.end(); } } // we don't have a buffer which we could reuse - need to create a new one wl_buffer *native = wl_shm_pool_create_buffer(pool, offset, s.width(), s.height(), stride, toWaylandFormat(format)); if (!native) { return buffers.end(); } if (queue) { queue->addProxy(native); } Buffer *buffer = new Buffer(q, native, s, stride, offset, format); offset += byteCount; auto it = buffers.insert(buffers.end(), QSharedPointer(buffer)); return it; } bool ShmPool::isValid() const { return d->valid; } void* ShmPool::poolAddress() const { return d->poolData; } wl_shm *ShmPool::shm() { return d->shm; } } } diff --git a/src/client/shm_pool.h b/src/client/shm_pool.h index d6af7c7..128e301 100644 --- a/src/client/shm_pool.h +++ b/src/client/shm_pool.h @@ -1,254 +1,240 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SHM_POOL_H #define WAYLAND_SHM_POOL_H #include #include "buffer.h" #include class QImage; class QSize; struct wl_shm; namespace KWayland { namespace Client { class EventQueue; /** * @short Wrapper class for wl_shm interface. * * This class holds a shared memory pool together with the Wayland server. * * To use this class one needs to interact with the Registry. There are two * possible ways to create a ShmPool instance: * @code * ShmPool *s = registry->createShmPool(name, version); * @endcode * * This creates the ShmPool and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * ShmPool *s = new ShmPool; * s->setup(registry->bindShm(name, version)); * @endcode * * The ShmPool holds a memory-mapped file from which it provides Buffers. * All Buffers are held by the ShmPool and can be reused. Whenever a Buffer * is requested the ShmPool tries to reuse an existing Buffer. A Buffer can * be reused if the following conditions hold * @li it's no longer marked as used * @li the server released the buffer * @li the size matches * @li the stride matches * @li the format matches * * The ownership of a Buffer stays with ShmPool. The ShmPool might destroy the * Buffer at any given time. Because of that ShmPool only provides QWeakPointer * for Buffers. Users should always check whether the pointer is still valid and * only promote to a QSharedPointer for a short time, e.g. to set new data. * * The ShmPool can provide Buffers for different purposes. One can create a Buffer * from an existing QImage. This will use a Buffer with same size, stride and image * format as the QImage and copy the content of the QImage into the Buffer. * The memory is not shared: * @code * QImage image(24, 24, QImage::Format_ARG32); * image.fill(Qt::transparent); * Buffer::Ptr buffer = s->createBuffer(image); * @endcode * * It is also possible to create a Buffer and copy the content from a generic location. * Like above this doesn't share the content but copies it: * @code * QImage image(24, 24, QImage::Format_ARG32); * image.fill(Qt::transparent); * Buffer::Ptr buffer = s->createBuffer(image.size(), image.bytesPerLine(), image.constBits()); * @endcode * * Last but not least it is possible to get a Buffer without copying content directly to it. * This means an empty area is just reserved and can be used to e.g. share the memory with a * QImage: * @code * const QSize size = QSize(24, 24); * const int stride = size.width() * 4; * Buffer::Ptr buffer = s->getBuffer(size, stride, Buffer::Format::RGB32); * if (!buffer) { * qDebug() << "Didn't get a valid Buffer"; * return; * } * QImage image(buffer.toStrongRef()->address(), size.width(), size.height(), stride, QImage::Format_RGB32); * image.fill(Qt::black); * @endcode * * A Buffer can be attached to a Surface: * @code * Compositor *c = registry.createCompositor(name, version); * Surface *s = c->createSurface(); * s->attachBuffer(buffer); * s->damage(QRect(QPoint(0, 0), size)); * @endcode * * Once a Buffer is attached to a Surface and the Surface is committed, it might be released * by the Wayland server and thus is free to be reused again. If the client code wants to * continue using the Buffer it must call Buffer::setUsed on it. This is important if the memory * is shared for example with a QImage as the memory buffer for a QImage must remain valid * throughout the life time of the QImage: * @code * buffer.toStrongRef()->setUsed(true); * @endcode * * This is also important for the case that the shared memory pool needs to be resized. * The ShmPool will automatically resize if it cannot provide a new Buffer. During the resize * all existing Buffers are unmapped and any shared objects must be recreated. The ShmPool emits * the signal poolResized() after the pool got resized. * * @see Buffer **/ class KWAYLANDCLIENT_EXPORT ShmPool : public QObject { Q_OBJECT public: explicit ShmPool(QObject *parent = nullptr); virtual ~ShmPool(); /** * @returns @c true if the ShmPool references a wl_shm interface and the shared memory pool * is setup. **/ bool isValid() const; /** * Setup this ShmPool to manage the @p shm. * This also creates the shared memory pool. * When using Registry::createShmPool there is no need to call this * method. **/ void setup(wl_shm *shm); /** * Releases the wl_shm interface. * After the interface has been released the ShmPool instance is no * longer valid and can be setup with another wl_shm interface. * * This also destroys the shared memory pool and all Buffers are destroyed. **/ void release(); /** * Destroys the data held by this ShmPool. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_shm interface * once there is a new connection available. * * All Buffers are destroyed! * * This method is automatically invoked when the Registry which created this * ShmPool gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a Buffer. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Buffer. **/ EventQueue *eventQueue(); /** * Provides a Buffer with: * @li same size as @p image * @li same stride as @p image * @li same format as @p image * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * The content of the @p image is copied into the buffer. The @p image and * returned Buffer do not share memory. * * @param image The image which should be copied into the Buffer * @return Buffer with copied content of @p image in success case, a @c null Buffer::Ptr otherwise * @see getBuffer **/ Buffer::Ptr createBuffer(const QImage &image); /** * Provides a Buffer with @p size, @p stride and @p format. * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * A memory copy is performed from @p src into the Buffer. The Buffer does not share * memory with @p src. * * @param size The requested size for the Buffer * @param stride The requested stride for the Buffer * @param src The source memory location to copy from * @param format The requested format for the Buffer * @return Buffer with copied content of @p src in success case, a @c null Buffer::Ptr otherwise * @see getBuffer **/ Buffer::Ptr createBuffer(const QSize &size, int32_t stride, const void *src, Buffer::Format format = Buffer::Format::ARGB32); void *poolAddress() const; /** * Provides a Buffer with @p size, @p stride and @p format. * * If the ShmPool fails to provide such a Buffer a @c null Buffer::Ptr is returned. * Unlike with createBuffer there is no memory copy performed. This provides a bare Buffer * to be used by the user. * * @param size The requested size for the Buffer * @param stride The requested stride for the Buffer * @param format The requested format for the Buffer * @return Buffer as requested in success case, a @c null Buffer::Ptr otherwise. * @see createBuffer **/ Buffer::Ptr getBuffer(const QSize &size, int32_t stride, Buffer::Format format = Buffer::Format::ARGB32); wl_shm *shm(); Q_SIGNALS: /** * This signal is emitted whenever the shared memory pool gets resized. * Any used Buffer must be remapped. **/ void poolResized(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createShmPool * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/slide.cpp b/src/client/slide.cpp index a6f0424..2da16c8 100644 --- a/src/client/slide.cpp +++ b/src/client/slide.cpp @@ -1,177 +1,163 @@ -/**************************************************************************** -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "slide.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "surface.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN SlideManager::Private { public: Private() = default; WaylandPointer slidemanager; EventQueue *queue = nullptr; }; SlideManager::SlideManager(QObject *parent) : QObject(parent) , d(new Private) { } SlideManager::~SlideManager() { release(); } void SlideManager::setup(org_kde_kwin_slide_manager *slidemanager) { Q_ASSERT(slidemanager); Q_ASSERT(!d->slidemanager); d->slidemanager.setup(slidemanager); } void SlideManager::release() { d->slidemanager.release(); } void SlideManager::destroy() { d->slidemanager.destroy(); } void SlideManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *SlideManager::eventQueue() { return d->queue; } SlideManager::operator org_kde_kwin_slide_manager*() { return d->slidemanager; } SlideManager::operator org_kde_kwin_slide_manager*() const { return d->slidemanager; } bool SlideManager::isValid() const { return d->slidemanager.isValid(); } Slide *SlideManager::createSlide(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); Slide *s = new Slide(parent); auto w = org_kde_kwin_slide_manager_create(d->slidemanager, *surface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } void SlideManager::removeSlide(Surface *surface) { org_kde_kwin_slide_manager_unset(d->slidemanager, *surface); } class Slide::Private { public: Private() = default; WaylandPointer slide; }; Slide::Slide(QObject *parent) : QObject(parent) , d(new Private) { } Slide::~Slide() { release(); } void Slide::setup(org_kde_kwin_slide *slide) { Q_ASSERT(slide); Q_ASSERT(!d->slide); d->slide.setup(slide); } void Slide::release() { d->slide.release(); } void Slide::destroy() { d->slide.destroy(); } Slide::operator org_kde_kwin_slide*() { return d->slide; } Slide::operator org_kde_kwin_slide*() const { return d->slide; } bool Slide::isValid() const { return d->slide.isValid(); } void Slide::commit() { Q_ASSERT(isValid()); org_kde_kwin_slide_commit(d->slide); } void Slide::setLocation(Slide::Location location) { org_kde_kwin_slide_set_location(d->slide, location); } void Slide::setOffset(qint32 offset) { org_kde_kwin_slide_set_offset(d->slide, offset); } } } diff --git a/src/client/slide.h b/src/client/slide.h index 3c56443..1542802 100644 --- a/src/client/slide.h +++ b/src/client/slide.h @@ -1,224 +1,210 @@ -/**************************************************************************** -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_SLIDE_H #define KWAYLAND_CLIENT_SLIDE_H #include #include struct org_kde_kwin_slide_manager; struct org_kde_kwin_slide; namespace KWayland { namespace Client { class EventQueue; class Slide; class Surface; /** * @short Wrapper for the org_kde_kwin_slide_manager interface. * * This class provides a convenient wrapper for the org_kde_kwin_slide_manager interface. * * Ask the compositor to move the surface from a location * to another with a slide animation. * * The from argument provides a clue about where the slide * animation begins, offset is the distance from screen * edge to begin the animation. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the SlideManager interface: * @code * SlideManager *c = registry->createSlideManager(name, version); * @endcode * * This creates the SlideManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * SlideManager *c = new SlideManager; * c->setup(registry->bindSlideManager(name, version)); * @endcode * * The SlideManager can be used as a drop-in replacement for any org_kde_kwin_slide_manager * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT SlideManager : public QObject { Q_OBJECT public: /** * Creates a new SlideManager. * Note: after constructing the SlideManager it is not yet valid and one needs * to call setup. In order to get a ready to use SlideManager prefer using * Registry::createSlideManager. **/ explicit SlideManager(QObject *parent = nullptr); virtual ~SlideManager(); /** * Setup this SlideManager to manage the @p slidemanager. * When using Registry::createSlideManager there is no need to call this * method. **/ void setup(org_kde_kwin_slide_manager *slidemanager); /** * @returns @c true if managing a org_kde_kwin_slide_manager. **/ bool isValid() const; /** * Releases the org_kde_kwin_slide_manager interface. * After the interface has been released the SlideManager instance is no * longer valid and can be setup with another org_kde_kwin_slide_manager interface. **/ void release(); /** * Destroys the data held by this SlideManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_slide_manager interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * SlideManager gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this SlideManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this SlideManager. **/ EventQueue *eventQueue(); Slide *createSlide(Surface *surface, QObject *parent = nullptr); void removeSlide(Surface *surface); operator org_kde_kwin_slide_manager*(); operator org_kde_kwin_slide_manager*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the SlideManager got created by * Registry::createSlideManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * TODO */ class KWAYLANDCLIENT_EXPORT Slide : public QObject { Q_OBJECT public: enum Location { Left = 0, /**< Slide from the left edge of the screen */ Top, /**< Slide from the top edge of the screen */ Right, /**< Slide from the bottom edge of the screen */ Bottom /**< Slide from the bottom edge of the screen */ }; virtual ~Slide(); /** * Setup this Slide to manage the @p slide. * When using SlideManager::createSlide there is no need to call this * method. **/ void setup(org_kde_kwin_slide *slide); /** * @returns @c true if managing a org_kde_kwin_slide. **/ bool isValid() const; /** * Releases the org_kde_kwin_slide interface. * After the interface has been released the Slide instance is no * longer valid and can be setup with another org_kde_kwin_slide interface. **/ void release(); /** * Destroys the data held by this Slide. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_slide interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, slide, &Slide::destroy); * @endcode * * @see release **/ void destroy(); void commit(); /** * Set the location of the screen to slide the window from */ void setLocation(Slide::Location location); /** * Set the offset from the screen edge * to make the window slide from */ void setOffset(qint32 offset); operator org_kde_kwin_slide*(); operator org_kde_kwin_slide*() const; private: friend class SlideManager; explicit Slide(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/subcompositor.cpp b/src/client/subcompositor.cpp index c187629..8f57ccd 100644 --- a/src/client/subcompositor.cpp +++ b/src/client/subcompositor.cpp @@ -1,106 +1,92 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "subcompositor.h" #include "event_queue.h" #include "subsurface.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN SubCompositor::Private { public: WaylandPointer subCompositor; EventQueue *queue = nullptr; }; SubCompositor::SubCompositor(QObject *parent) : QObject(parent) , d(new Private) { } SubCompositor::~SubCompositor() { release(); } void SubCompositor::release() { d->subCompositor.release(); } void SubCompositor::destroy() { d->subCompositor.destroy(); } void SubCompositor::setup(wl_subcompositor *subcompositor) { Q_ASSERT(subcompositor); Q_ASSERT(!d->subCompositor.isValid()); d->subCompositor.setup(subcompositor); } SubSurface *SubCompositor::createSubSurface(QPointer surface, QPointer parentSurface, QObject *parent) { Q_ASSERT(isValid()); SubSurface *s = new SubSurface(surface, parentSurface, parent); auto w = wl_subcompositor_get_subsurface(d->subCompositor, *surface, *parentSurface); if (d->queue) { d->queue->addProxy(w); } s->setup(w); return s; } bool SubCompositor::isValid() const { return d->subCompositor.isValid(); } SubCompositor::operator wl_subcompositor*() { return d->subCompositor; } SubCompositor::operator wl_subcompositor*() const { return d->subCompositor; } EventQueue *SubCompositor::eventQueue() { return d->queue; } void SubCompositor::setEventQueue(EventQueue *queue) { d->queue = queue; } } } diff --git a/src/client/subcompositor.h b/src/client/subcompositor.h index 814c669..18107b5 100644 --- a/src/client/subcompositor.h +++ b/src/client/subcompositor.h @@ -1,123 +1,109 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SUBCOMPOSITOR_H #define WAYLAND_SUBCOMPOSITOR_H #include #include #include struct wl_subcompositor; namespace KWayland { namespace Client { class EventQueue; class SubSurface; class Surface; /** * @short Wrapper for the wl_subcompositor interface. * * This class is a convenient wrapper for the wl_subcompositor interface. * The main purpose of this class is to create SubSurfaces. * * To create an instance use Registry::createSubCompositor. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT SubCompositor : public QObject { Q_OBJECT public: explicit SubCompositor(QObject *parent = nullptr); virtual ~SubCompositor(); /** * @returns @c true if managing a wl_subcompositor. **/ bool isValid() const; /** * Setup this SubCompositor to manage the @p subcompositor. * When using Registry::createSubCompositor there is no need to call this * method. **/ void setup(wl_subcompositor *subcompositor); /** * Releases the wl_subcompositor interface. * After the interface has been released the SubCompositor instance is no * longer valid and can be setup with another wl_subcompositor interface. **/ void release(); /** * Destroys the data held by this SubCompositor. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_subcompositor interface * once there is a new connection available. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating a SubSurface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a SubSurface. **/ EventQueue *eventQueue(); /** * Creates and setup a new SubSurface with @p parent. * @param parent The parent to pass to the Surface. * @returns The new created Surface **/ SubSurface *createSubSurface(QPointer surface, QPointer parentSurface, QObject *parent = nullptr); operator wl_subcompositor*(); operator wl_subcompositor*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createSubCompositor * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/subsurface.cpp b/src/client/subsurface.cpp index 3512ab6..37bbfe5 100644 --- a/src/client/subsurface.cpp +++ b/src/client/subsurface.cpp @@ -1,203 +1,189 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "subsurface.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN SubSurface::Private { public: Private(QPointer surface, QPointer parentSurface, SubSurface *q); void setup(wl_subsurface *subsurface); WaylandPointer subSurface; QPointer surface; QPointer parentSurface; Mode mode = Mode::Synchronized; QPoint pos = QPoint(0, 0); static SubSurface *cast(wl_subsurface *native); private: SubSurface *q; }; SubSurface::Private::Private(QPointer< Surface > surface, QPointer< Surface > parentSurface, SubSurface *q) : surface(surface) , parentSurface(parentSurface) , q(q) { } void SubSurface::Private::setup(wl_subsurface *subsurface) { Q_ASSERT(subsurface); Q_ASSERT(!subSurface.isValid()); subSurface.setup(subsurface); wl_subsurface_set_user_data(subsurface, this); } SubSurface *SubSurface::Private::cast(wl_subsurface *native) { return reinterpret_cast(wl_subsurface_get_user_data(native))->q; } SubSurface::SubSurface(QPointer< Surface > surface, QPointer< Surface > parentSurface, QObject *parent) : QObject(parent) , d(new Private(surface, parentSurface, this)) { } SubSurface::~SubSurface() { release(); } void SubSurface::setup(wl_subsurface *subsurface) { d->setup(subsurface); } void SubSurface::destroy() { d->subSurface.destroy(); } void SubSurface::release() { d->subSurface.release(); } bool SubSurface::isValid() const { return d->subSurface.isValid(); } QPointer< Surface > SubSurface::surface() const { return d->surface; } QPointer< Surface > SubSurface::parentSurface() const { return d->parentSurface; } void SubSurface::setMode(SubSurface::Mode mode) { if (mode == d->mode) { return; } d->mode = mode; switch (d->mode) { case Mode::Synchronized: wl_subsurface_set_sync(d->subSurface); break; case Mode::Desynchronized: wl_subsurface_set_desync(d->subSurface); break; } } SubSurface::Mode SubSurface::mode() const { return d->mode; } void SubSurface::setPosition(const QPoint &pos) { if (pos == d->pos) { return; } d->pos = pos; wl_subsurface_set_position(d->subSurface, pos.x(), pos.y()); } QPoint SubSurface::position() const { return d->pos; } void SubSurface::raise() { placeAbove(d->parentSurface); } void SubSurface::placeAbove(QPointer< SubSurface > sibling) { if (sibling.isNull()) { return; } placeAbove(sibling->surface()); } void SubSurface::placeAbove(QPointer< Surface > sibling) { if (sibling.isNull()) { return; } wl_subsurface_place_above(d->subSurface, *sibling); } void SubSurface::lower() { placeBelow(d->parentSurface); } void SubSurface::placeBelow(QPointer< Surface > sibling) { if (sibling.isNull()) { return; } wl_subsurface_place_below(d->subSurface, *sibling); } void SubSurface::placeBelow(QPointer< SubSurface > sibling) { if (sibling.isNull()) { return; } placeBelow(sibling->surface()); } QPointer< SubSurface > SubSurface::get(wl_subsurface *native) { return QPointer(Private::cast(native)); } SubSurface::operator wl_subsurface*() const { return d->subSurface; } SubSurface::operator wl_subsurface*() { return d->subSurface; } } } diff --git a/src/client/subsurface.h b/src/client/subsurface.h index 6504853..3e9cbd9 100644 --- a/src/client/subsurface.h +++ b/src/client/subsurface.h @@ -1,192 +1,178 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SUBSURFACE_H #define WAYLAND_SUBSURFACE_H #include #include #include struct wl_subsurface; namespace KWayland { namespace Client { class Surface; /** * @short Wrapper for the wl_subsurface interface. * * This class is a convenient wrapper for the wl_subsurface interface. * To create a SubSurface call SubCompositor::createSubSurface. * * A SubSurface is bound to a Surface and has a parent Surface. * A SubSurface can only be created for a Surface not already used in onther way, * e.g. as a ShellSurface. * * The SubSurface has a position in local coordinates to the parent Surface. * Please note that changing the position is a double buffered state and is only * applied once the parent surface is committed. The same applies for manipulating * the stacking order of the SubSurface's siblings. * * @see SubCompositor * @see Surface **/ class KWAYLANDCLIENT_EXPORT SubSurface : public QObject { Q_OBJECT public: explicit SubSurface(QPointer surface, QPointer parentSurface, QObject *parent = nullptr); virtual ~SubSurface(); /** * @returns @c true if managing a wl_subsurface. **/ bool isValid() const; /** * Setup this SubSurface to manage the @p subsurface. * When using SubCompositor::createSubSurface there is no need to call this * method. **/ void setup(wl_subsurface *subsurface); /** * Releases the wl_subsurface interface. * After the interface has been released the SubSurface instance is no * longer valid and can be setup with another wl_subsurface interface. **/ void release(); /** * Destroys the data held by this SubSurface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_subsurface interface * once there is a new connection available. * * @see release **/ void destroy(); /** * Operation Mode on how the Surface's commit should behave. **/ enum class Mode { Synchronized, Desynchronized }; /** * Sets the operation mode to @p mode. * Initially a SubSurface is in Synchronized Mode. **/ void setMode(Mode mode); Mode mode() const; /** * Sets the position in relative coordinates to the parent surface to @p pos. * * The change is only applied after the parent Surface got committed. **/ void setPosition(const QPoint &pos); QPoint position() const; /** * Raises this SubSurface above all siblings. * This is the same as calling placeAbove with the parent surface * as argument. * * The change is only applied after the parent surface got committed. * @see placeAbove **/ void raise(); /** * Places the SubSurface above the @p sibling. * * The change is only applied after the parent surface got committed. * @param sibling The SubSurface on top of which this SubSurface should be placed **/ void placeAbove(QPointer sibling); /** * Places the SubSurface above the @p referenceSurface. * * In case @p referenceSurface is the parent surface this SubSurface is * raised to the top of the stacking order. Otherwise it is put directly * above the @p referenceSurface in the stacking order. * * The change is only applied after the parent surface got committed. * @param referenceSurface Either a sibling or parent Surface **/ void placeAbove(QPointer referenceSurface); /** * Lowers this SubSurface below all siblings. * This is the same as calling placeBelow with the parent surface * as argument. * * The change is only applied after the parent surface got committed. * @see placeBelow **/ void lower(); /** * Places the SubSurface below the @p sibling. * * The change is only applied after the parent surface got committed. * @param sibling The SubSurface under which the SubSurface should be put **/ void placeBelow(QPointer sibling); /** * Places the SubSurface below the @p referenceSurface. * * In case @p referenceSurface is the parent surface this SubSurface is * lowered to the bottom of the stacking order. Otherwise it is put directly * below the @p referenceSurface in the stacking order. * * The change is only applied after the parent surface got committed. * @param referenceSurface Either a sibling or parent Surface **/ void placeBelow(QPointer referenceSurface); /** * @returns The Surface for which this SubSurface got created. **/ QPointer surface() const; /** * @returns The parent Surface of this SubSurface. **/ QPointer parentSurface() const; static QPointer get(wl_subsurface *native); operator wl_subsurface*(); operator wl_subsurface*() const; private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/surface.cpp b/src/client/surface.cpp index 89ce102..c960a82 100644 --- a/src/client/surface.cpp +++ b/src/client/surface.cpp @@ -1,361 +1,347 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "surface.h" #include "buffer.h" #include "region.h" #include "output.h" #include "wayland_pointer_p.h" #include #include #include #include #include // Wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Surface::Private { public: Private(Surface *q); void setupFrameCallback(); WaylandPointer surface; bool frameCallbackInstalled = false; QSize size; bool foreign = false; qint32 scale = 1; QVector outputs; void setup(wl_surface *s); static QList s_surfaces; private: void handleFrameCallback(); static void frameCallback(void *data, wl_callback *callback, uint32_t time); static void enterCallback(void *data, wl_surface *wl_surface, wl_output *output); static void leaveCallback(void *data, wl_surface *wl_surface, wl_output *output); Surface *q; static const wl_callback_listener s_listener; static const wl_surface_listener s_surfaceListener; }; QList Surface::Private::s_surfaces = QList(); Surface::Private::Private(Surface *q) : q(q) { } Surface::Surface(QObject *parent) : QObject(parent) , d(new Private(this)) { Private::s_surfaces << this; } Surface::~Surface() { Private::s_surfaces.removeAll(this); release(); } Surface *Surface::fromWindow(QWindow *window) { if (!window) { return nullptr; } QPlatformNativeInterface *native = qApp->platformNativeInterface(); if (!native) { return nullptr; } window->create(); wl_surface *s = reinterpret_cast(native->nativeResourceForWindow(QByteArrayLiteral("surface"), window)); if (!s) { return nullptr; } if (auto surface = get(s)) { return surface; } Surface *surface = new Surface(window); surface->d->surface.setup(s, true); return surface; } Surface *Surface::fromQtWinId(WId wid) { QWindow *window = nullptr; for (auto win : qApp->allWindows()) { if (win->winId() == wid) { window = win; break; } } if (!window) { return nullptr; } return fromWindow(window); } void Surface::release() { d->surface.release(); } void Surface::destroy() { d->surface.destroy(); } void Surface::setup(wl_surface *surface) { d->setup(surface); } void Surface::Private::setup(wl_surface *s) { Q_ASSERT(s); Q_ASSERT(!surface); surface.setup(s); wl_surface_add_listener(s, &s_surfaceListener, this); } void Surface::Private::frameCallback(void *data, wl_callback *callback, uint32_t time) { Q_UNUSED(time) auto s = reinterpret_cast(data); if (callback) { wl_callback_destroy(callback); } s->handleFrameCallback(); } void Surface::Private::handleFrameCallback() { frameCallbackInstalled = false; emit q->frameRendered(); } #ifndef K_DOXYGEN const struct wl_callback_listener Surface::Private::s_listener = { frameCallback }; const struct wl_surface_listener Surface::Private::s_surfaceListener = { enterCallback, leaveCallback }; #endif void Surface::Private::enterCallback(void *data, wl_surface *surface, wl_output *output) { Q_UNUSED(surface); auto s = reinterpret_cast(data); Output *o = Output::get(output); if (!o) { return; } s->outputs << o; QObject::connect(o, &Output::removed, s->q, [s, o]() { if (!s->outputs.contains(o)) { return; } s->outputs.removeOne(o); s->q->outputLeft(o); }); emit s->q->outputEntered(o); } void Surface::Private::leaveCallback(void *data, wl_surface *surface, wl_output *output) { Q_UNUSED(surface); auto s = reinterpret_cast(data); Output *o = Output::get(output); if (!o) { return; } s->outputs.removeOne(o); emit s->q->outputLeft(o); } void Surface::Private::setupFrameCallback() { Q_ASSERT(!frameCallbackInstalled); wl_callback *callback = wl_surface_frame(surface); wl_callback_add_listener(callback, &s_listener, this); frameCallbackInstalled = true; } void Surface::setupFrameCallback() { Q_ASSERT(isValid()); d->setupFrameCallback(); } void Surface::commit(Surface::CommitFlag flag) { Q_ASSERT(isValid()); if (flag == CommitFlag::FrameCallback) { setupFrameCallback(); } wl_surface_commit(d->surface); } void Surface::damage(const QRegion ®ion) { for (const QRect &rect : region) { damage(rect); } } void Surface::damage(const QRect &rect) { Q_ASSERT(isValid()); wl_surface_damage(d->surface, rect.x(), rect.y(), rect.width(), rect.height()); } void Surface::damageBuffer(const QRegion ®ion) { for (const QRect &r : region) { damageBuffer(r); } } void Surface::damageBuffer(const QRect &rect) { Q_ASSERT(isValid()); wl_surface_damage_buffer(d->surface, rect.x(), rect.y(), rect.width(), rect.height()); } void Surface::attachBuffer(wl_buffer *buffer, const QPoint &offset) { Q_ASSERT(isValid()); wl_surface_attach(d->surface, buffer, offset.x(), offset.y()); } void Surface::attachBuffer(Buffer *buffer, const QPoint &offset) { attachBuffer(buffer ? buffer->buffer() : nullptr, offset); } void Surface::attachBuffer(Buffer::Ptr buffer, const QPoint &offset) { attachBuffer(buffer.toStrongRef().data(), offset); } void Surface::setInputRegion(const Region *region) { Q_ASSERT(isValid()); if (region) { wl_surface_set_input_region(d->surface, *region); } else { wl_surface_set_input_region(d->surface, nullptr); } } void Surface::setOpaqueRegion(const Region *region) { Q_ASSERT(isValid()); if (region) { wl_surface_set_opaque_region(d->surface, *region); } else { wl_surface_set_opaque_region(d->surface, nullptr); } } void Surface::setSize(const QSize &size) { if (d->size == size) { return; } d->size = size; emit sizeChanged(d->size); } Surface *Surface::get(wl_surface *native) { auto it = std::find_if(Private::s_surfaces.constBegin(), Private::s_surfaces.constEnd(), [native](Surface *s) { return s->d->surface == native; } ); if (it != Private::s_surfaces.constEnd()) { return *(it); } return nullptr; } const QList< Surface* > &Surface::all() { return Private::s_surfaces; } bool Surface::isValid() const { return d->surface.isValid(); } QSize Surface::size() const { return d->size; } Surface::operator wl_surface*() { return d->surface; } Surface::operator wl_surface*() const { return d->surface; } quint32 Surface::id() const { wl_surface *s = *this; return wl_proxy_get_id(reinterpret_cast(s)); } qint32 Surface::scale() const { return d->scale; } void Surface::setScale(qint32 scale) { d->scale = scale; wl_surface_set_buffer_scale(d->surface, scale); } QVector Surface::outputs() const { return d->outputs; } } } diff --git a/src/client/surface.h b/src/client/surface.h index dc85753..3fa4954 100644 --- a/src/client/surface.h +++ b/src/client/surface.h @@ -1,312 +1,298 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SURFACE_H #define WAYLAND_SURFACE_H #include "buffer.h" #include #include #include #include #include struct wl_buffer; struct wl_surface; class QWindow; namespace KWayland { namespace Client { class Output; class Region; /** * @short Wrapper for the wl_surface interface. * * This class is a convenient wrapper for the wl_surface interface. * To create a Surface call Compositor::createSurface. * * The main purpose of this class is to setup the next frame which * should be rendered. Therefore it provides methods to add damage * and to attach a new Buffer and to finalize the frame by calling * commit. * * @see Compositor **/ class KWAYLANDCLIENT_EXPORT Surface : public QObject { Q_OBJECT public: explicit Surface(QObject *parent = nullptr); virtual ~Surface(); /** * Creates a Surface for the given @p window. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned Surface will be fully setup, but won't be released. It gets automatically * destroyed together with the @p window. * @since 5.4 **/ static Surface *fromWindow(QWindow *window); /** * Creates a Surface for the given @p winId. * This is an integration feature for QtWayland. On non-wayland platforms this method returns * @c nullptr as well as for not created QWindows. * * The returned Surface will be fully setup, but won't be released. It gets automatically * destroyed together with the QWindow corresponding * the @p wid. * @since 5.5 **/ static Surface *fromQtWinId(WId wid); /** * Setup this Surface to manage the @p surface. * When using Compositor::createSurface there is no need to call this * method. **/ void setup(wl_surface *surface); /** * Releases the wl_surface interface. * After the interface has been released the Surface instance is no * longer valid and can be setup with another wl_surface interface. **/ void release(); /** * Destroys the data held by this Surface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_surface interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * Surface gets destroyed. * * @see release **/ void destroy(); /** * @returns @c true if managing a wl_surface. **/ bool isValid() const; /** * Registers a frame rendered callback. * This registers a callback in the Wayland server to be notified once the * next frame for this Surface has been rendered. The Surface will emit the * signal frameRendered after receiving the callback from the server. * * Instead of using this method one should prefer using the CommitFlag::FrameCallback * in commit. This method is intended for cases when the Surface is going to * be committed on other ways, e.g. through the OpenGL/EGL stack. * * @see frameRendered * @see commit **/ void setupFrameCallback(); /** * Flags to be added to commit. * @li None: no flag * @li FrameCallback: register a frame rendered callback * * Instead of setting the FrameCallback flag one can also call * setupFrameCallback. If one uses setupFrameCallback one may not * use the FrameCallback flag when committing the Surface. * * @see commit * @see setupFrameCallback **/ enum class CommitFlag { None, FrameCallback }; void commit(CommitFlag flag = CommitFlag::FrameCallback); /** * Mark @p rect as damaged for the next frame. * @see damageBuffer **/ void damage(const QRect &rect); /** * Mark @p region as damaged for the next frame. * @see damageBuffer **/ void damage(const QRegion ®ion); /** * Mark @p rect in buffer coordinates as damaged for the next frame. * @see damage * @since 5.59 **/ void damageBuffer(const QRect &rect); /** * Mark @p region in buffer coordinates as damaged for the next frame. * @see damage * @since 5.59 **/ void damageBuffer(const QRegion ®ion); /** * Attaches the @p buffer to this Surface for the next frame. * @param buffer The buffer to attach to this Surface * @param offset Position of the new upper-left corner in relation to previous frame **/ void attachBuffer(wl_buffer *buffer, const QPoint &offset = QPoint()); /** * Overloaded method for convenience. **/ void attachBuffer(Buffer *buffer, const QPoint &offset = QPoint()); /** * Overloaded method for convenience. **/ void attachBuffer(Buffer::Ptr buffer, const QPoint &offset = QPoint()); /** * Sets the input region to @p region. * * This is a double buffered state and will be applied with the next Surface * commit. Initially the Surface is set up to an infinite input region. * By passing @c null as the input region, it gets reset to an infinite input * region. * * Note: the Region is being copied and can be destroyed directly after passing * to this method. * * @param region The new input region or an infinite region if @c null * @see commit **/ void setInputRegion(const Region *region = nullptr); /** * Sets the opaque region to @p region. * * This is a double buffered state and will be applied with the next Surface * commit. Initially the Surface is set up to an empty opaque region. * By passing @c null as the opaque region, it gets reset to an empty opaque * region. * * Note: the Region is being copied and can be destroyed directly after passing * to this method. * * @param region The new opaque region or an empty region if @c null * @see commit **/ void setOpaqueRegion(const Region *region = nullptr); void setSize(const QSize &size); QSize size() const; /** * The purpose of this method is to allow to supply higher resolution buffer data for use * on high resolution outputs. It's intended that the same buffer scale as the scale of the * output that the surface is displayed on is used. * This means the compositor can avoid scaling when rendering the surface on that output. * * Note that if @p scale is larger than 1 you have to attach a buffer that is larger * (by a factor of scale in each dimension) than the desired surface size. * * The default scale factor is 1. * * The state is only applied with the next commit. * * @see scale * @see commit * @since 5.22 **/ void setScale(qint32 scale); /** * @returns The current scale factor, if not explicitly set it's @c 1. * @see setScale * @since 5.22 **/ qint32 scale() const; operator wl_surface*(); operator wl_surface*() const; /** * @returns the id of the referenced wl_proxy. * @since 5.4 **/ quint32 id() const; /** * @returns All Outputs the Surface is on, may be none. * @see outputEntered * @see outputLeft * @since 5.27 **/ QVector outputs() const; /** * All Surfaces which are currently created. * TODO: KF6 return QList instead of const-ref **/ static const QList &all(); // krazy:exclude=constref /** * @returns The Surface referencing the @p native wl_surface or @c null if there is no such Surface. **/ static Surface *get(wl_surface *native); Q_SIGNALS: /** * Emitted when the server indicates that the last committed frame has been rendered. * The signal will only be emitted if a callback has been registered by either calling * setupFrameCallback or calling commit with the CommitFlag::FrameCallback. * @see setupFrameCallback * @see commit **/ void frameRendered(); void sizeChanged(const QSize&); /** * Emitted whenever a change in the Surface (e.g. creation, movement, resize) results in * a part of the Surface being within the scanout region of the Output @p o. * * @param o The Output the Surface intersects with * @see outputLeft * @see outputs * @since 5.27 **/ void outputEntered(KWayland::Client::Output *o); /** * Emitted whenever a change in the Surface (e.g. creation, movement, resize, unmapping) * results in the Surface no longer being within the scanout region of the Output @p o. * * @param o The Output the Surface no longer intersects with * @see outputEntered * @see outputs * @since 5.27 **/ void outputLeft(KWayland::Client::Output *o); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/textinput.cpp b/src/client/textinput.cpp index 2addfd9..f9280dd 100644 --- a/src/client/textinput.cpp +++ b/src/client/textinput.cpp @@ -1,228 +1,214 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_p.h" namespace KWayland { namespace Client { TextInput::Private::Private(Seat *seat) : seat(seat) { currentCommit.deleteSurrounding.afterLength = 0; currentCommit.deleteSurrounding.beforeLength = 0; pendingCommit.deleteSurrounding.afterLength = 0; pendingCommit.deleteSurrounding.beforeLength = 0; } TextInput::TextInput(Private *p, QObject *parent) : QObject(parent) , d(p) { } TextInput::~TextInput() = default; void TextInput::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *TextInput::eventQueue() const { return d->queue; } bool TextInput::isValid() const { return d->isValid(); } Surface *TextInput::enteredSurface() const { return d->enteredSurface; } bool TextInput::isInputPanelVisible() const { return d->inputPanelVisible; } void TextInput::enable(Surface *surface) { d->enable(surface); } void TextInput::disable(Surface *surface) { d->disable(surface); } void TextInput::showInputPanel() { d->showInputPanel(); } void TextInput::hideInputPanel() { d->hideInputPanel(); } void TextInput::reset() { d->reset(); } void TextInput::setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) { d->setSurroundingText(text, cursor, anchor); } void TextInput::setContentType(ContentHints hint, ContentPurpose purpose) { d->setContentType(hint, purpose); } void TextInput::setCursorRectangle(const QRect &rect) { d->setCursorRectangle(rect); } void TextInput::setPreferredLanguage(const QString &language) { d->setPreferredLanguage(language); } Qt::LayoutDirection TextInput::textDirection() const { return d->textDirection; } QByteArray TextInput::language() const { return d->language; } qint32 TextInput::composingTextCursorPosition() const { return d->currentPreEdit.cursor; } QByteArray TextInput::composingText() const { return d->currentPreEdit.text; } QByteArray TextInput::composingFallbackText() const { return d->currentPreEdit.commitText; } qint32 TextInput::anchorPosition() const { return d->currentCommit.anchor; } qint32 TextInput::cursorPosition() const { return d->currentCommit.cursor; } TextInput::DeleteSurroundingText TextInput::deleteSurroundingText() const { return d->currentCommit.deleteSurrounding; } QByteArray TextInput::commitText() const { return d->currentCommit.text; } TextInputManager::TextInputManager(Private *p, QObject *parent) : QObject(parent) , d(p) { } TextInputManager::~TextInputManager() = default; void TextInputManager::setup(wl_text_input_manager *textinputmanagerunstablev0) { d->setupV0(textinputmanagerunstablev0); } void TextInputManager::setup(zwp_text_input_manager_v2 *textinputmanagerunstablev2) { d->setupV2(textinputmanagerunstablev2); } void TextInputManager::release() { d->release(); } void TextInputManager::destroy() { d->destroy(); } void TextInputManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *TextInputManager::eventQueue() { return d->queue; } TextInputManager::operator wl_text_input_manager*() { return *(d.data()); } TextInputManager::operator wl_text_input_manager*() const { return *(d.data()); } TextInputManager::operator zwp_text_input_manager_v2*() { return *(d.data()); } TextInputManager::operator zwp_text_input_manager_v2*() const { return *(d.data()); } bool TextInputManager::isValid() const { return d->isValid(); } TextInput *TextInputManager::createTextInput(Seat *seat, QObject *parent) { return d->createTextInput(seat, parent); } } } diff --git a/src/client/textinput.h b/src/client/textinput.h index c7cde38..3a3b641 100644 --- a/src/client/textinput.h +++ b/src/client/textinput.h @@ -1,534 +1,520 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_TEXTINPUT_H #define KWAYLAND_CLIENT_TEXTINPUT_H #include #include struct wl_text_input; struct wl_text_input_manager; struct zwp_text_input_manager_v2; namespace KWayland { namespace Client { class EventQueue; class TextInputUnstableV0; class Surface; class Seat; /** * @brief TextInput represents a Wayland interface for text input. * * The TextInput allows to have text composed by the Compositor and be sent to * the client. * * Depending on the interface the TextInputManager got created for this class * encapsulates one of the following interfaces: * @li wl_text_input * @li zwp_text_input_v2 * * @since 5.23 **/ class KWAYLANDCLIENT_EXPORT TextInput : public QObject { Q_OBJECT public: virtual ~TextInput(); /** * @returns @c true if managing a resource. **/ bool isValid() const; /** * @returns The Surface which has the text input focus on this TextInput. * @see entered * @see left **/ Surface *enteredSurface() const; void setEventQueue(EventQueue *queue); EventQueue *eventQueue() const; /** * @returns whether the input panel (virtual keyboard) is currently visible on the screen * @see inputPanelStateChanged **/ bool isInputPanelVisible() const; /** * Enable text input in a @p surface (usually when a text entry inside of it has focus). * * This can be called before or after a surface gets text (or keyboard) focus via the * enter event. Text input to a surface is only active when it has the current * text (or keyboard) focus and is enabled. * @see deactivate **/ void enable(Surface *surface); /** * Disable text input in a @p surface (typically when there is no focus on any * text entry inside the surface). * @see enable **/ void disable(Surface *surface); /** * Requests input panels (virtual keyboard) to show. * @see hideInputPanel **/ void showInputPanel(); /** * Requests input panels (virtual keyboard) to hide. * @see showInputPanel **/ void hideInputPanel(); /** * Should be called by an editor widget when the input state should be * reset, for example after the text was changed outside of the normal * input method flow. **/ void reset(); /** * Sets the plain surrounding text around the input position. * * @param text The text surrounding the cursor position * @param cursor Index in the text describing the cursor position * @param anchor Index of the selection anchor, if no selection same as cursor **/ void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor); /** * The possible states for a keyEvent. * @see keyEvent **/ enum class KeyState { Pressed, Released }; /** * ContentHint allows to modify the behavior of the text input. **/ enum class ContentHint : uint32_t { /** * no special behaviour */ None = 0, /** * suggest word completions */ AutoCompletion = 1 << 0, /** * suggest word corrections */ AutoCorrection = 1 << 1, /** * switch to uppercase letters at the start of a sentence */ AutoCapitalization = 1 << 2, /** * prefer lowercase letters */ LowerCase = 1 << 3, /** * prefer uppercase letters */ UpperCase = 1 << 4, /** * prefer casing for titles and headings (can be language dependent) */ TitleCase = 1 << 5, /** * characters should be hidden */ HiddenText = 1 << 6, /** * typed text should not be stored */ SensitiveData = 1 << 7, /** * just latin characters should be entered */ Latin = 1 << 8, /** * the text input is multi line */ MultiLine = 1 << 9 }; Q_DECLARE_FLAGS(ContentHints, ContentHint) /** * The ContentPurpose allows to specify the primary purpose of a text input. * * This allows an input method to show special purpose input panels with * extra characters or to disallow some characters. */ enum class ContentPurpose : uint32_t { /** * default input, allowing all characters */ Normal, /** * allow only alphabetic characters **/ Alpha, /** * allow only digits */ Digits, /** * input a number (including decimal separator and sign) */ Number, /** * input a phone number */ Phone, /** * input an URL */ Url, /** * input an email address **/ Email, /** * input a name of a person */ Name, /** * input a password */ Password, /** * input a date */ Date, /** * input a time */ Time, /** * input a date and time */ DateTime, /** * input for a terminal */ Terminal }; /** * Sets the content @p purpose and content @p hints. * While the @p purpose is the basic purpose of an input field, the @p hints flags allow * to modify some of the behavior. **/ void setContentType(ContentHints hints, ContentPurpose purpose); /** * Sets the cursor outline @p rect in surface local coordinates. * * Allows the compositor to e.g. put a window with word suggestions * near the cursor. **/ void setCursorRectangle(const QRect &rect); /** * Sets a specific @p language. * * This allows for example a virtual keyboard to show a language specific layout. * The @p language argument is a RFC-3066 format language tag. **/ void setPreferredLanguage(const QString &language); /** * The text direction of input text. * * It is mainly needed for showing input cursor on correct side of the * editor when there is no input yet done and making sure neutral * direction text is laid out properly. * @see textDirectionChnaged **/ Qt::LayoutDirection textDirection() const; /** * The language of the input text. * * As long as the server has not emitted the language, the code will be empty. * * @returns a RFC-3066 format language tag in utf-8. * @see languageChanged **/ QByteArray language() const; /** * The cursor position inside the {@link composingText} (as byte offset) relative * to the start of the {@link composingText}. * If index is a negative number no cursor is shown. * @see composingText * @see composingTextChanged **/ qint32 composingTextCursorPosition() const; /** * The currently being composed text around the {@link composingTextCursorPosition}. * @see composingTextCursorPosition * @see composingTextChanged **/ QByteArray composingText() const; /** * The fallback text can be used to replace the {@link composingText} in some cases * (for example when losing focus). * * @see composingText * @see composingTextChanged **/ QByteArray composingFallbackText() const; /** * The commit text to be inserted. * * The commit text might be empty if only text should be deleted or the cursor be moved. * @see cursorPosition * @see anchorPosition * @see deleteSurroundingText * @see committed **/ QByteArray commitText() const; /** * The cursor position in bytes at which the {@link commitText} should be inserted. * @see committed **/ qint32 cursorPosition() const; /** * The text between anchorPosition and {@link cursorPosition} should be selected. * @see cursorPosition * @see committed **/ qint32 anchorPosition() const; /** * Holds the length before and after the cursor position to be deleted. **/ struct DeleteSurroundingText { quint32 beforeLength; quint32 afterLength; }; /** * @returns The length in bytes which should be deleted around the cursor position * @see committed **/ DeleteSurroundingText deleteSurroundingText() const; Q_SIGNALS: /** * Emitted whenever a Surface is focused on this TextInput. * @see enteredSurface * @see left **/ void entered(); /** * Emitted whenever a Surface loses the focus on this TextInput. * @see enteredSurface * @see entered **/ void left(); /** * Emitted whenever the state of the input panel (virtual keyboard changes). * @see isInputPanelVisible **/ void inputPanelStateChanged(); /** * Emitted whenver the text direction changes. * @see textDirection **/ void textDirectionChanged(); /** * Emitted whenever the language changes. * @see language **/ void languageChanged(); /** * Emitted when a key event was sent. * Key events are not used for normal text input operations, but for specific key symbols * which are not composable through text. * * @param xkbKeySym The XKB key symbol, not a key code * @param state Whether the event represents a press or release event * @param modifiers The hold modifiers on this event * @param time Timestamp of this event **/ void keyEvent(quint32 xkbKeySym, KWayland::Client::TextInput::KeyState state, Qt::KeyboardModifiers modifiers, quint32 time); /** * Emitted whenever the composing text and related states changed. * @see composingText * @see composingTextCursorPosition * @see composingFallbackText **/ void composingTextChanged(); /** * Emitted when the currently composing text got committed. * The {@link commitText} should get inserted at the {@link cursorPosition} and * the text around {@link deleteSurroundingText} should be deleted. * * @see commitText * @see cursorPosition * @see anchorPosition * @see deleteSurroundingText **/ void committed(); protected: class Private; QScopedPointer d; explicit TextInput(Private *p, QObject *parent = nullptr); }; /** * @brief Manager class for the TextInputManager interfaces. * * The TextInputManager supports multiple interfaces: * @li wl_text_input_manager * @li zwp_text_input_manager_v2 * * Due to that it is different to other manager classes. It can only be created through * the corresponding factory method in Registry. A manual setup is not directly possible. * * The only task of a TextInputManager is to create TextInput for a given Seat. * * @since 5.23 **/ class KWAYLANDCLIENT_EXPORT TextInputManager : public QObject { Q_OBJECT public: virtual ~TextInputManager(); /** * Setup this TextInputManager to manage the @p textinputmanagerunstablev0. * When using Registry::createTextInputManager there is no need to call this * method. **/ void setup(wl_text_input_manager *textinputmanagerunstablev0); /** * Setup this TextInputManager to manage the @p textinputmanagerunstablev0. * When using Registry::createTextInputManager there is no need to call this * method. **/ void setup(zwp_text_input_manager_v2 *textinputmanagerunstablev2); /** * @returns @c true if managing a resource. **/ bool isValid() const; /** * Releases the interface. * After the interface has been released the TextInputManager instance is no * longer valid and can be setup with another interface. **/ void release(); /** * Destroys the data held by this TextInputManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * TextInput gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this TextInputManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this TextInputManager. **/ EventQueue *eventQueue(); /** * Creates a TextInput for the @p seat. * * @param seat The Seat to create the TextInput for * @param parent The parent to use for the TextInput **/ TextInput *createTextInput(Seat *seat, QObject *parent = nullptr); /** * @returns @c null if not for a wl_text_input_manager **/ operator wl_text_input_manager*(); /** * @returns @c null if not for a wl_text_input_manager **/ operator wl_text_input_manager*() const; /** * @returns @c null if not for a zwp_text_input_manager_v2 **/ operator zwp_text_input_manager_v2*(); /** * @returns @c null if not for a zwp_text_input_manager_v2 **/ operator zwp_text_input_manager_v2*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the TextInputManager got created by * Registry::createTextInputManager **/ void removed(); protected: class Private; explicit TextInputManager(Private *p, QObject *parent = nullptr); QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::TextInput::KeyState) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentHint) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentPurpose) Q_DECLARE_METATYPE(KWayland::Client::TextInput::ContentHints) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::TextInput::ContentHints) #endif diff --git a/src/client/textinput_p.h b/src/client/textinput_p.h index 5fd95a1..11bc94d 100644 --- a/src/client/textinput_p.h +++ b/src/client/textinput_p.h @@ -1,245 +1,231 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_TEXTINPUT_P_H #define KWAYLAND_CLIENT_TEXTINPUT_P_H #include "textinput.h" #include struct wl_text_input; struct wl_text_input_manager; struct zwp_text_input_v2; namespace KWayland { namespace Client { class EventQueue; class TextInputUnstableV0; class Surface; class Seat; class TextInputManagerUnstableV0 : public TextInputManager { Q_OBJECT public: /** * Creates a new TextInputManagerUnstableV0. * Note: after constructing the TextInputManagerUnstableV0 it is not yet valid and one needs * to call setup. In order to get a ready to use TextInputManagerUnstableV0 prefer using * Registry::createTextInputManagerUnstableV0. **/ explicit TextInputManagerUnstableV0(QObject *parent = nullptr); virtual ~TextInputManagerUnstableV0(); private: class Private; Private *d_func() const; }; class TextInputManagerUnstableV2 : public TextInputManager { Q_OBJECT public: /** * Creates a new TextInputManagerUnstableV0. * Note: after constructing the TextInputManagerUnstableV0 it is not yet valid and one needs * to call setup. In order to get a ready to use TextInputManagerUnstableV0 prefer using * Registry::createTextInputManagerUnstableV0. **/ explicit TextInputManagerUnstableV2(QObject *parent = nullptr); virtual ~TextInputManagerUnstableV2(); private: class Private; QScopedPointer d; }; class Q_DECL_HIDDEN TextInputManager::Private { public: Private() = default; virtual ~Private() = default; virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() = 0; virtual void setupV0(wl_text_input_manager *textinputmanagerunstablev0) { Q_UNUSED(textinputmanagerunstablev0) } virtual void setupV2(zwp_text_input_manager_v2 *textinputmanagerunstablev2) { Q_UNUSED(textinputmanagerunstablev2) } virtual TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) = 0; virtual operator wl_text_input_manager*() { return nullptr; } virtual operator wl_text_input_manager*() const { return nullptr; } virtual operator zwp_text_input_manager_v2*() { return nullptr; } virtual operator zwp_text_input_manager_v2*() const { return nullptr; } EventQueue *queue = nullptr; }; class Q_DECL_HIDDEN TextInput::Private { public: Private(Seat *seat); virtual ~Private() = default; virtual bool isValid() const = 0; virtual void enable(Surface *surface) = 0; virtual void disable(Surface *surface) = 0; virtual void showInputPanel() = 0; virtual void hideInputPanel() = 0; virtual void setCursorRectangle(const QRect &rect) = 0; virtual void setPreferredLanguage(const QString &lang) = 0; virtual void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) = 0; virtual void reset() = 0; virtual void setContentType(ContentHints hint, ContentPurpose purpose) = 0; EventQueue *queue = nullptr; Seat *seat; Surface *enteredSurface = nullptr; quint32 latestSerial = 0; bool inputPanelVisible = false; Qt::LayoutDirection textDirection = Qt::LayoutDirectionAuto; QByteArray language; struct PreEdit { QByteArray text; QByteArray commitText; qint32 cursor = 0; bool cursorSet = false; }; PreEdit currentPreEdit; PreEdit pendingPreEdit; struct Commit { QByteArray text; qint32 cursor = 0; qint32 anchor = 0; DeleteSurroundingText deleteSurrounding; }; Commit currentCommit; Commit pendingCommit; }; class TextInputUnstableV0 : public TextInput { Q_OBJECT public: explicit TextInputUnstableV0(Seat *seat, QObject *parent = nullptr); virtual ~TextInputUnstableV0(); /** * Setup this TextInputUnstableV0 to manage the @p textinputunstablev0. * When using TextInputManagerUnstableV0::createTextInputUnstableV0 there is no need to call this * method. **/ void setup(wl_text_input *textinputunstablev0); /** * Releases the wl_text_input interface. * After the interface has been released the TextInputUnstableV0 instance is no * longer valid and can be setup with another wl_text_input interface. **/ void release(); /** * Destroys the data held by this TextInputUnstableV0. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_text_input interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, textinputunstablev0, &TextInputUnstableV0::destroy); * @endcode * * @see release **/ void destroy(); operator wl_text_input*(); operator wl_text_input*() const; private: class Private; Private *d_func() const; }; class TextInputUnstableV2 : public TextInput { Q_OBJECT public: explicit TextInputUnstableV2(Seat *seat, QObject *parent = nullptr); virtual ~TextInputUnstableV2(); /** * Setup this TextInputUnstableV2 to manage the @p textinputunstablev2. * When using TextInputManagerUnstableV2::createTextInputUnstableV2 there is no need to call this * method. **/ void setup(zwp_text_input_v2 *textinputunstablev2); /** * Releases the zwp_text_input_v2 interface. * After the interface has been released the TextInputUnstableV2 instance is no * longer valid and can be setup with another zwp_text_input_v2 interface. **/ void release(); /** * Destroys the data held by this TextInputUnstableV2. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zwp_text_input_v2 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, textinputunstablev2, &TextInputUnstableV2::destroy); * @endcode * * @see release **/ void destroy(); operator zwp_text_input_v2*(); operator zwp_text_input_v2*() const; private: class Private; Private *d_func() const; }; } } #endif diff --git a/src/client/textinput_v0.cpp b/src/client/textinput_v0.cpp index 1b52ed3..0254d14 100644 --- a/src/client/textinput_v0.cpp +++ b/src/client/textinput_v0.cpp @@ -1,501 +1,487 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_p.h" #include "event_queue.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class TextInputUnstableV0::Private : public TextInput::Private { public: Private(TextInputUnstableV0 *q, Seat *seat); void setup(wl_text_input *textinputmanagerunstablev0); bool isValid() const override; void enable(Surface *surface) override; void disable(Surface * surface) override; void showInputPanel() override; void hideInputPanel() override; void setCursorRectangle(const QRect &rect) override; void setPreferredLanguage(const QString &lang) override; void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) override; void reset() override; void setContentType(ContentHints hint, ContentPurpose purpose) override; WaylandPointer textinputunstablev0; private: static void enterCallaback(void *data, wl_text_input *wl_text_input, wl_surface *surface); static void leaveCallback(void *data, wl_text_input *wl_text_input); static void modifiersMapCallback(void *data, wl_text_input *wl_text_input, wl_array *map); static void inputPanelStateCallback(void *data, wl_text_input *wl_text_input, uint32_t state); static void preeditStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text, const char *commit); static void preeditStylingCallback(void *data, wl_text_input *wl_text_input, uint32_t index, uint32_t length, uint32_t style); static void preeditCursorCallback(void *data, wl_text_input *wl_text_input, int32_t index); static void commitStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text); static void cursorPositionCallback(void *data, wl_text_input *wl_text_input, int32_t index, int32_t anchor); static void deleteSurroundingTextCallback(void *data, wl_text_input *wl_text_input, int32_t index, uint32_t length); static void keysymCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers); static void languageCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *language); static void textDirectionCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t direction); TextInputUnstableV0 *q; static const wl_text_input_listener s_listener; }; const wl_text_input_listener TextInputUnstableV0::Private::s_listener = { enterCallaback, leaveCallback, modifiersMapCallback, inputPanelStateCallback, preeditStringCallback, preeditStylingCallback, preeditCursorCallback, commitStringCallback, cursorPositionCallback, deleteSurroundingTextCallback, keysymCallback, languageCallback, textDirectionCallback }; void TextInputUnstableV0::Private::enterCallaback(void *data, wl_text_input *wl_text_input, wl_surface *surface) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->enteredSurface = Surface::get(surface); emit t->q->entered(); } void TextInputUnstableV0::Private::leaveCallback(void *data, wl_text_input *wl_text_input) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->enteredSurface = nullptr; emit t->q->left(); } void TextInputUnstableV0::Private::modifiersMapCallback(void *data, wl_text_input *wl_text_input, wl_array *map) { Q_UNUSED(map) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); // TODO: implement } void TextInputUnstableV0::Private::inputPanelStateCallback(void *data, wl_text_input *wl_text_input, uint32_t state) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); if (t->inputPanelVisible != state) { t->inputPanelVisible = state; emit t->q->inputPanelStateChanged(); } } void TextInputUnstableV0::Private::preeditStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text, const char *commit) { Q_UNUSED(serial) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->pendingPreEdit.commitText = QByteArray(commit); t->pendingPreEdit.text = QByteArray(text); if (!t->pendingPreEdit.cursorSet) { t->pendingPreEdit.cursor = t->pendingPreEdit.text.length(); } t->currentPreEdit = t->pendingPreEdit; t->pendingPreEdit = TextInput::Private::PreEdit(); emit t->q->composingTextChanged(); } void TextInputUnstableV0::Private::preeditStylingCallback(void *data, wl_text_input *wl_text_input, uint32_t index, uint32_t length, uint32_t style) { Q_UNUSED(index) Q_UNUSED(length) Q_UNUSED(style) // TODO: implement auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); } void TextInputUnstableV0::Private::preeditCursorCallback(void *data, wl_text_input *wl_text_input, int32_t index) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->pendingPreEdit.cursor = index; t->pendingPreEdit.cursorSet = true; } void TextInputUnstableV0::Private::commitStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text) { Q_UNUSED(serial) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->pendingCommit.text = QByteArray(text); t->currentCommit = t->pendingCommit; // TODO: what are the proper values it should be set to? t->pendingCommit = TextInput::Private::Commit(); t->pendingCommit.deleteSurrounding.beforeLength = 0; t->pendingCommit.deleteSurrounding.afterLength = 0; emit t->q->committed(); } void TextInputUnstableV0::Private::cursorPositionCallback(void *data, wl_text_input *wl_text_input, int32_t index, int32_t anchor) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->pendingCommit.cursor = index; t->pendingCommit.anchor = anchor; } void TextInputUnstableV0::Private::deleteSurroundingTextCallback(void *data, wl_text_input *wl_text_input, int32_t index, uint32_t length) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); t->pendingCommit.deleteSurrounding.beforeLength = qAbs(index); t->pendingCommit.deleteSurrounding.afterLength = length - t->pendingCommit.deleteSurrounding.beforeLength; } void TextInputUnstableV0::Private::keysymCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t time, uint32_t sym, uint32_t wlState, uint32_t modifiers) { Q_UNUSED(serial) // TODO: add support for modifiers Q_UNUSED(modifiers) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); TextInput::KeyState state; switch (wlState) { case WL_KEYBOARD_KEY_STATE_RELEASED: state = TextInput::KeyState::Released; break; case WL_KEYBOARD_KEY_STATE_PRESSED: state = TextInput::KeyState::Pressed; break; default: // invalid return; } emit t->q->keyEvent(sym, state, Qt::KeyboardModifiers(), time); } void TextInputUnstableV0::Private::languageCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *language) { Q_UNUSED(serial) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); if (qstrcmp(t->language, language) != 0) { t->language = QByteArray(language); emit t->q->languageChanged(); } } void TextInputUnstableV0::Private::textDirectionCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t wlDirection) { Q_UNUSED(serial) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev0 == wl_text_input); Qt::LayoutDirection direction; switch (wlDirection) { case WL_TEXT_INPUT_TEXT_DIRECTION_LTR: direction = Qt::LeftToRight; break; case WL_TEXT_INPUT_TEXT_DIRECTION_RTL: direction = Qt::RightToLeft; break; case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO: direction = Qt::LayoutDirectionAuto; break; default: // invalid return; } if (direction != t->textDirection) { t->textDirection = direction; emit t->q->textDirectionChanged(); } } TextInputUnstableV0::Private::Private(TextInputUnstableV0 *q, Seat *seat) : TextInput::Private(seat) , q(q) { } void TextInputUnstableV0::Private::setup(wl_text_input *ti) { Q_ASSERT(ti); Q_ASSERT(!textinputunstablev0); textinputunstablev0.setup(ti); wl_text_input_add_listener(ti, &s_listener, this); } bool TextInputUnstableV0::Private::isValid() const { return textinputunstablev0.isValid(); } void TextInputUnstableV0::Private::enable(Surface *surface) { wl_text_input_activate(textinputunstablev0, *seat, *surface); } void TextInputUnstableV0::Private::disable(Surface *surface) { Q_UNUSED(surface) wl_text_input_deactivate(textinputunstablev0, *seat); } void TextInputUnstableV0::Private::showInputPanel() { wl_text_input_show_input_panel(textinputunstablev0); } void TextInputUnstableV0::Private::hideInputPanel() { wl_text_input_hide_input_panel(textinputunstablev0); } void TextInputUnstableV0::Private::setCursorRectangle(const QRect &rect) { wl_text_input_set_cursor_rectangle(textinputunstablev0, rect.x(), rect.y(), rect.width(), rect.height()); } void TextInputUnstableV0::Private::setPreferredLanguage(const QString &lang) { wl_text_input_set_preferred_language(textinputunstablev0, lang.toUtf8().constData()); } void TextInputUnstableV0::Private::setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) { wl_text_input_set_surrounding_text(textinputunstablev0, text.toUtf8().constData(), text.leftRef(cursor).toUtf8().length(), text.leftRef(anchor).toUtf8().length()); } void TextInputUnstableV0::Private::reset() { wl_text_input_reset(textinputunstablev0); } void TextInputUnstableV0::Private::setContentType(ContentHints hints, ContentPurpose purpose) { uint32_t wlHints = 0; uint32_t wlPurpose = 0; if (hints.testFlag(ContentHint::AutoCompletion)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_COMPLETION; } if (hints.testFlag(ContentHint::AutoCorrection)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_CORRECTION; } if (hints.testFlag(ContentHint::AutoCapitalization)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION; } if (hints.testFlag(ContentHint::LowerCase)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_LOWERCASE; } if (hints.testFlag(ContentHint::UpperCase)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_UPPERCASE; } if (hints.testFlag(ContentHint::TitleCase)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_TITLECASE; } if (hints.testFlag(ContentHint::HiddenText)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_HIDDEN_TEXT; } if (hints.testFlag(ContentHint::SensitiveData)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_SENSITIVE_DATA; } if (hints.testFlag(ContentHint::Latin)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_LATIN; } if (hints.testFlag(ContentHint::MultiLine)) { wlHints |= WL_TEXT_INPUT_CONTENT_HINT_MULTILINE; } switch (purpose) { case ContentPurpose::Normal: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL; break; case ContentPurpose::Alpha: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_ALPHA; break; case ContentPurpose::Digits: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS; break; case ContentPurpose::Number: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER; break; case ContentPurpose::Phone: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_PHONE; break; case ContentPurpose::Url: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_URL; break; case ContentPurpose::Email: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_EMAIL; break; case ContentPurpose::Name: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NAME; break; case ContentPurpose::Password: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_PASSWORD; break; case ContentPurpose::Date: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DATE; break; case ContentPurpose::Time: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_TIME; break; case ContentPurpose::DateTime: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DATETIME; break; case ContentPurpose::Terminal: wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_TERMINAL; break; } wl_text_input_set_content_type(textinputunstablev0, wlHints, wlPurpose); } TextInputUnstableV0::TextInputUnstableV0(Seat *seat, QObject *parent) : TextInput(new Private(this, seat), parent) { } TextInputUnstableV0::~TextInputUnstableV0() { release(); } TextInputUnstableV0::Private *TextInputUnstableV0::d_func() const { return reinterpret_cast(d.data()); } void TextInputUnstableV0::setup(wl_text_input *textinputunstablev0) { Q_D(); d->setup(textinputunstablev0); } void TextInputUnstableV0::release() { Q_D(); d->textinputunstablev0.release(); } void TextInputUnstableV0::destroy() { Q_D(); d->textinputunstablev0.destroy(); } TextInputUnstableV0::operator wl_text_input*() { Q_D(); return d->textinputunstablev0; } TextInputUnstableV0::operator wl_text_input*() const { Q_D(); return d->textinputunstablev0; } class TextInputManagerUnstableV0::Private : public TextInputManager::Private { public: Private() = default; void release() override; void destroy() override; bool isValid() override; void setupV0(wl_text_input_manager *ti) override; TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override; using TextInputManager::Private::operator zwp_text_input_manager_v2*; //overriding only one overload results in a compiler warning. This tells GCC we're doing it deliberately operator wl_text_input_manager*() override { return textinputmanagerunstablev0; } operator wl_text_input_manager*() const override { return textinputmanagerunstablev0; } WaylandPointer textinputmanagerunstablev0; }; void TextInputManagerUnstableV0::Private::release() { textinputmanagerunstablev0.release(); } void TextInputManagerUnstableV0::Private::destroy() { textinputmanagerunstablev0.destroy(); } bool TextInputManagerUnstableV0::Private::isValid() { return textinputmanagerunstablev0.isValid(); } TextInputManagerUnstableV0::TextInputManagerUnstableV0(QObject *parent) : TextInputManager(new Private, parent) { } TextInputManagerUnstableV0::Private *TextInputManagerUnstableV0::d_func() const { return reinterpret_cast(d.data()); } TextInputManagerUnstableV0::~TextInputManagerUnstableV0() { release(); } void TextInputManagerUnstableV0::Private::setupV0(wl_text_input_manager *ti) { Q_ASSERT(ti); Q_ASSERT(!textinputmanagerunstablev0); textinputmanagerunstablev0.setup(ti); } TextInput *TextInputManagerUnstableV0::Private::createTextInput(Seat *seat, QObject *parent) { Q_ASSERT(isValid()); TextInputUnstableV0 *t = new TextInputUnstableV0(seat, parent); auto w = wl_text_input_manager_create_text_input(textinputmanagerunstablev0); if (queue) { queue->addProxy(w); } t->setup(w); return t; } } } diff --git a/src/client/textinput_v2.cpp b/src/client/textinput_v2.cpp index 32219aa..01cd4d4 100644 --- a/src/client/textinput_v2.cpp +++ b/src/client/textinput_v2.cpp @@ -1,517 +1,503 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_p.h" #include "event_queue.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class TextInputUnstableV2::Private : public TextInput::Private { public: Private(TextInputUnstableV2 *q, Seat *seat); void setup(zwp_text_input_v2 *textinputmanagerunstablev0); bool isValid() const override; void enable(Surface *surface) override; void disable(Surface * surface) override; void showInputPanel() override; void hideInputPanel() override; void setCursorRectangle(const QRect &rect) override; void setPreferredLanguage(const QString &lang) override; void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) override; void reset() override; void setContentType(ContentHints hint, ContentPurpose purpose) override; WaylandPointer textinputunstablev2; private: static void enterCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface); static void leaveCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface); static void inputPanelStateCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height); static void preeditStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text, const char *commit); static void preeditStylingCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t index, uint32_t length, uint32_t style); static void preeditCursorCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index); static void commitStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text); static void cursorPositionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index, int32_t anchor); static void deleteSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t before_length, uint32_t after_length); static void modifiersMapCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, wl_array *map); static void keysymCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers); static void languageCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *language); static void textDirectionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t direction); static void configureSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t before_cursor, int32_t after_cursor); static void inputMethodChangedCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, uint32_t flags); TextInputUnstableV2 *q; static const zwp_text_input_v2_listener s_listener; }; const zwp_text_input_v2_listener TextInputUnstableV2::Private::s_listener = { enterCallback, leaveCallback, inputPanelStateCallback, preeditStringCallback, preeditStylingCallback, preeditCursorCallback, commitStringCallback, cursorPositionCallback, deleteSurroundingTextCallback, modifiersMapCallback, keysymCallback, languageCallback, textDirectionCallback, configureSurroundingTextCallback, inputMethodChangedCallback }; void TextInputUnstableV2::Private::enterCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->latestSerial = serial; t->enteredSurface = Surface::get(surface); emit t->q->entered(); } void TextInputUnstableV2::Private::leaveCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface) { Q_UNUSED(surface) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->enteredSurface = nullptr; t->latestSerial = serial; emit t->q->left(); } void TextInputUnstableV2::Private::inputPanelStateCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(x) Q_UNUSED(y) Q_UNUSED(width) Q_UNUSED(height) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); // TODO: add rect if (t->inputPanelVisible != state) { t->inputPanelVisible = state; emit t->q->inputPanelStateChanged(); } } void TextInputUnstableV2::Private::preeditStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text, const char *commit) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->pendingPreEdit.commitText = QByteArray(commit); t->pendingPreEdit.text = QByteArray(text); if (!t->pendingPreEdit.cursorSet) { t->pendingPreEdit.cursor = t->pendingPreEdit.text.length(); } t->currentPreEdit = t->pendingPreEdit; t->pendingPreEdit = TextInput::Private::PreEdit(); emit t->q->composingTextChanged(); } void TextInputUnstableV2::Private::preeditStylingCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t index, uint32_t length, uint32_t style) { Q_UNUSED(index) Q_UNUSED(length) Q_UNUSED(style) // TODO: implement auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); } void TextInputUnstableV2::Private::preeditCursorCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->pendingPreEdit.cursor = index; t->pendingPreEdit.cursorSet = true; } void TextInputUnstableV2::Private::commitStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->pendingCommit.text = QByteArray(text); t->currentCommit = t->pendingCommit; // TODO: what are the proper values it should be set to? t->pendingCommit = TextInput::Private::Commit(); t->pendingCommit.deleteSurrounding.beforeLength = 0; t->pendingCommit.deleteSurrounding.afterLength = 0; emit t->q->committed(); } void TextInputUnstableV2::Private::cursorPositionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index, int32_t anchor) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->pendingCommit.cursor = index; t->pendingCommit.anchor = anchor; } void TextInputUnstableV2::Private::deleteSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t before_length, uint32_t after_length) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); t->pendingCommit.deleteSurrounding.beforeLength = before_length; t->pendingCommit.deleteSurrounding.afterLength = after_length; } void TextInputUnstableV2::Private::modifiersMapCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, wl_array *map) { // TODO: implement Q_UNUSED(map) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); } void TextInputUnstableV2::Private::keysymCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t time, uint32_t sym, uint32_t wlState, uint32_t modifiers) { // TODO: add support for modifiers Q_UNUSED(modifiers) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); TextInput::KeyState state; switch (wlState) { case WL_KEYBOARD_KEY_STATE_RELEASED: state = TextInput::KeyState::Released; break; case WL_KEYBOARD_KEY_STATE_PRESSED: state = TextInput::KeyState::Pressed; break; default: // invalid return; } emit t->q->keyEvent(sym, state, Qt::KeyboardModifiers(), time); } void TextInputUnstableV2::Private::languageCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *language) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); if (qstrcmp(t->language, language) != 0) { t->language = QByteArray(language); emit t->q->languageChanged(); } } void TextInputUnstableV2::Private::textDirectionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t wlDirection) { auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); Qt::LayoutDirection direction; switch (wlDirection) { case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_LTR: direction = Qt::LeftToRight; break; case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_RTL: direction = Qt::RightToLeft; break; case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_AUTO: direction = Qt::LayoutDirectionAuto; break; default: // invalid return; } if (direction != t->textDirection) { t->textDirection = direction; emit t->q->textDirectionChanged(); } } void TextInputUnstableV2::Private::configureSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t before_cursor, int32_t after_cursor) { // TODO: implement Q_UNUSED(before_cursor) Q_UNUSED(after_cursor) auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); } void TextInputUnstableV2::Private::inputMethodChangedCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, uint32_t flags) { Q_UNUSED(serial) Q_UNUSED(flags) // TODO: implement auto t = reinterpret_cast(data); Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2); } TextInputUnstableV2::Private::Private(TextInputUnstableV2 *q, Seat *seat) : TextInput::Private(seat) , q(q) { } void TextInputUnstableV2::Private::setup(zwp_text_input_v2 *ti) { Q_ASSERT(ti); Q_ASSERT(!textinputunstablev2); textinputunstablev2.setup(ti); zwp_text_input_v2_add_listener(ti, &s_listener, this); } bool TextInputUnstableV2::Private::isValid() const { return textinputunstablev2.isValid(); } void TextInputUnstableV2::Private::enable(Surface *surface) { zwp_text_input_v2_enable(textinputunstablev2, *surface); } void TextInputUnstableV2::Private::disable(Surface * surface) { zwp_text_input_v2_disable(textinputunstablev2, *surface); } void TextInputUnstableV2::Private::showInputPanel() { zwp_text_input_v2_show_input_panel(textinputunstablev2); } void TextInputUnstableV2::Private::hideInputPanel() { zwp_text_input_v2_hide_input_panel(textinputunstablev2); } void TextInputUnstableV2::Private::setCursorRectangle(const QRect &rect) { zwp_text_input_v2_set_cursor_rectangle(textinputunstablev2, rect.x(), rect.y(), rect.width(), rect.height()); } void TextInputUnstableV2::Private::setPreferredLanguage(const QString &lang) { zwp_text_input_v2_set_preferred_language(textinputunstablev2, lang.toUtf8().constData()); } void TextInputUnstableV2::Private::setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) { zwp_text_input_v2_set_surrounding_text(textinputunstablev2, text.toUtf8().constData(), text.leftRef(cursor).toUtf8().length(), text.leftRef(anchor).toUtf8().length()); } void TextInputUnstableV2::Private::reset() { zwp_text_input_v2_update_state(textinputunstablev2, latestSerial, ZWP_TEXT_INPUT_V2_UPDATE_STATE_RESET); } void TextInputUnstableV2::Private::setContentType(ContentHints hints, ContentPurpose purpose) { uint32_t wlHints = 0; uint32_t wlPurpose = 0; if (hints.testFlag(ContentHint::AutoCompletion)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION; } if (hints.testFlag(ContentHint::AutoCorrection)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION; } if (hints.testFlag(ContentHint::AutoCapitalization)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION; } if (hints.testFlag(ContentHint::LowerCase)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE; } if (hints.testFlag(ContentHint::UpperCase)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE; } if (hints.testFlag(ContentHint::TitleCase)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_TITLECASE; } if (hints.testFlag(ContentHint::HiddenText)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT; } if (hints.testFlag(ContentHint::SensitiveData)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA; } if (hints.testFlag(ContentHint::Latin)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN; } if (hints.testFlag(ContentHint::MultiLine)) { wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE; } switch (purpose) { case ContentPurpose::Normal: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL; break; case ContentPurpose::Alpha: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_ALPHA; break; case ContentPurpose::Digits: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS; break; case ContentPurpose::Number: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER; break; case ContentPurpose::Phone: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE; break; case ContentPurpose::Url: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL; break; case ContentPurpose::Email: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL; break; case ContentPurpose::Name: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NAME; break; case ContentPurpose::Password: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PASSWORD; break; case ContentPurpose::Date: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE; break; case ContentPurpose::Time: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME; break; case ContentPurpose::DateTime: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME; break; case ContentPurpose::Terminal: wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TERMINAL; break; } zwp_text_input_v2_set_content_type(textinputunstablev2, wlHints, wlPurpose); } TextInputUnstableV2::TextInputUnstableV2(Seat *seat, QObject *parent) : TextInput(new Private(this, seat), parent) { } TextInputUnstableV2::~TextInputUnstableV2() { release(); } TextInputUnstableV2::Private *TextInputUnstableV2::d_func() const { return reinterpret_cast(d.data()); } void TextInputUnstableV2::setup(zwp_text_input_v2 *textinputunstablev2) { Q_D(); d->setup(textinputunstablev2); } void TextInputUnstableV2::release() { Q_D(); d->textinputunstablev2.release(); } void TextInputUnstableV2::destroy() { Q_D(); d->textinputunstablev2.destroy(); } TextInputUnstableV2::operator zwp_text_input_v2*() { Q_D(); return d->textinputunstablev2; } TextInputUnstableV2::operator zwp_text_input_v2*() const { Q_D(); return d->textinputunstablev2; } class TextInputManagerUnstableV2::Private : public TextInputManager::Private { public: Private() = default; void release() override; void destroy() override; bool isValid() override; void setupV2(zwp_text_input_manager_v2 *ti) override; TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override; using TextInputManager::Private::operator wl_text_input_manager*; operator zwp_text_input_manager_v2*() override { return textinputmanagerunstablev2; } operator zwp_text_input_manager_v2*() const override { return textinputmanagerunstablev2; } WaylandPointer textinputmanagerunstablev2; }; void TextInputManagerUnstableV2::Private::release() { textinputmanagerunstablev2.release(); } void TextInputManagerUnstableV2::Private::destroy() { textinputmanagerunstablev2.destroy(); } bool TextInputManagerUnstableV2::Private::isValid() { return textinputmanagerunstablev2.isValid(); } TextInputManagerUnstableV2::TextInputManagerUnstableV2(QObject *parent) : TextInputManager(new Private, parent) { } TextInputManagerUnstableV2::~TextInputManagerUnstableV2() = default; void TextInputManagerUnstableV2::Private::setupV2(zwp_text_input_manager_v2 *ti) { Q_ASSERT(ti); Q_ASSERT(!textinputmanagerunstablev2); textinputmanagerunstablev2.setup(ti); } TextInput *TextInputManagerUnstableV2::Private::createTextInput(Seat *seat, QObject *parent) { Q_ASSERT(isValid()); TextInputUnstableV2 *t = new TextInputUnstableV2(seat, parent); auto w = zwp_text_input_manager_v2_get_text_input(textinputmanagerunstablev2, *seat); if (queue) { queue->addProxy(w); } t->setup(w); return t; } } } diff --git a/src/client/touch.cpp b/src/client/touch.cpp index f4bd32c..3f48131 100644 --- a/src/client/touch.cpp +++ b/src/client/touch.cpp @@ -1,298 +1,284 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "touch.h" #include "surface.h" #include "wayland_pointer_p.h" // Qt #include #include #include // wayland #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN Touch::Private { public: Private(Touch *q); void setup(wl_touch *t); WaylandPointer touch; bool active = false; QVector sequence; TouchPoint *getActivePoint(qint32 id) const; private: static void downCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y); static void upCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, int32_t id); static void motionCallback(void *data, wl_touch *touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y); static void frameCallback(void *data, wl_touch *touch); static void cancelCallback(void *data, wl_touch *touch); void down(quint32 serial, quint32 time, qint32 id, const QPointF &position, const QPointer &surface); void up(quint32 serial, quint32 time, qint32 id); void motion(quint32 time, qint32 id, const QPointF &position); Touch *q; static const wl_touch_listener s_listener; }; class TouchPoint::Private { public: qint32 id = 0; quint32 downSerial = 0; quint32 upSerial = 0; QPointer surface; QVector positions; QVector timestamps; bool down = true; }; TouchPoint::TouchPoint() : d(new Private) { } TouchPoint::~TouchPoint() = default; QPointF TouchPoint::position() const { if (d->positions.isEmpty()) { return QPointF(); } return d->positions.last(); } QVector< QPointF > TouchPoint::positions() const { return d->positions; } quint32 TouchPoint::downSerial() const { return d->downSerial; } quint32 TouchPoint::upSerial() const { return d->upSerial; } QPointer< Surface > TouchPoint::surface() const { return d->surface; } quint32 TouchPoint::time() const { if (d->timestamps.isEmpty()) { return 0; } return d->timestamps.last(); } QVector< quint32 > TouchPoint::timestamps() const { return d->timestamps; } bool TouchPoint::isDown() const { return d->down; } qint32 TouchPoint::id() const { return d->id; } Touch::Private::Private(Touch *q) : q(q) { } void Touch::Private::setup(wl_touch *t) { Q_ASSERT(t); Q_ASSERT(!touch); touch.setup(t); wl_touch_add_listener(touch, &s_listener, this); } const wl_touch_listener Touch::Private::s_listener = { downCallback, upCallback, motionCallback, frameCallback, cancelCallback }; void Touch::Private::downCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) { auto t = reinterpret_cast(data); Q_ASSERT(t->touch == touch); t->down(serial, time, id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y)), QPointer(Surface::get(surface))); } void Touch::Private::down(quint32 serial, quint32 time, qint32 id, const QPointF &position, const QPointer< Surface> &surface) { TouchPoint *p = new TouchPoint; p->d->downSerial = serial; p->d->surface = surface; p->d->id = id; p->d->positions << position; p->d->timestamps << time; if (active) { sequence << p; emit q->pointAdded(p); } else { qDeleteAll(sequence); sequence.clear(); sequence << p; active = true; emit q->sequenceStarted(p); } } TouchPoint *Touch::Private::getActivePoint(qint32 id) const { auto it = std::find_if(sequence.constBegin(), sequence.constEnd(), [id] (TouchPoint *p) { return p->id() == id && p->isDown(); } ); if (it == sequence.constEnd()) { return nullptr; } return *it; } void Touch::Private::upCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, int32_t id) { auto t = reinterpret_cast(data); Q_ASSERT(t->touch == touch); t->up(serial, time, id); } void Touch::Private::up(quint32 serial, quint32 time, qint32 id) { TouchPoint *p = getActivePoint(id); if (!p) { return; } p->d->timestamps << time; p->d->upSerial = serial; p->d->down = false; emit q->pointRemoved(p); // check whether the sequence ended for (auto it = sequence.constBegin(); it != sequence.constEnd(); ++it) { if ((*it)->isDown()) { return; } } // no touch point is down active = false; emit q->sequenceEnded(); } void Touch::Private::motionCallback(void *data, wl_touch *touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { auto t = reinterpret_cast(data); Q_ASSERT(t->touch == touch); t->motion(time, id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void Touch::Private::motion(quint32 time, qint32 id, const QPointF &position) { TouchPoint *p = getActivePoint(id); if (!p) { return; } p->d->positions << position; p->d->timestamps << time; emit q->pointMoved(p); } void Touch::Private::frameCallback(void *data, wl_touch *touch) { auto t = reinterpret_cast(data); Q_ASSERT(t->touch == touch); emit t->q->frameEnded(); } void Touch::Private::cancelCallback(void *data, wl_touch *touch) { auto t = reinterpret_cast(data); Q_ASSERT(t->touch == touch); t->active = false; emit t->q->sequenceCanceled(); } Touch::Touch(QObject *parent) : QObject(parent) , d(new Private(this)) { } Touch::~Touch() { release(); } void Touch::destroy() { d->touch.destroy(); } void Touch::release() { d->touch.release(); } void Touch::setup(wl_touch *touch) { d->setup(touch); } bool Touch::isValid() const { return d->touch.isValid(); } Touch::operator wl_touch *() const { return d->touch; } Touch::operator wl_touch *() { return d->touch; } QVector< TouchPoint* > Touch::sequence() const { return d->sequence; } } } diff --git a/src/client/touch.h b/src/client/touch.h index 9c09f11..e30d89b 100644 --- a/src/client/touch.h +++ b/src/client/touch.h @@ -1,196 +1,182 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_TOUCH_H #define WAYLAND_TOUCH_H #include #include #include struct wl_touch; namespace KWayland { namespace Client { class Surface; class Touch; /** * TODO */ class KWAYLANDCLIENT_EXPORT TouchPoint { public: virtual ~TouchPoint(); /** * Unique in the scope of all TouchPoints currently being down. * As soon as the TouchPoint is now longer down another TouchPoint * might get assigned the id. **/ qint32 id() const; /** * The serial when the down event happened. **/ quint32 downSerial() const; /** * The serial when the up event happened. **/ quint32 upSerial() const; /** * Most recent timestamp **/ quint32 time() const; /** * All timestamps, references the positions. * That is each position has a timestamp. **/ QVector timestamps() const; /** * Most recent position **/ QPointF position() const; /** * All positions this TouchPoint had, updated with each move. **/ QVector positions() const; /** * The Surface this TouchPoint happened on. **/ QPointer surface() const; /** * @c true if currently down, @c false otherwise. **/ bool isDown() const; private: friend class Touch; explicit TouchPoint(); class Private; QScopedPointer d; }; /** * @short Wrapper for the wl_touch interface. * * This class is a convenient wrapper for the wl_touch interface. * * To create an instance use Seat::createTouch. * * @see Seat **/ class KWAYLANDCLIENT_EXPORT Touch : public QObject { Q_OBJECT public: explicit Touch(QObject *parent = nullptr); virtual ~Touch(); /** * @returns @c true if managing a wl_pointer. **/ bool isValid() const; /** * Setup this Touch to manage the @p touch. * When using Seat::createTouch there is no need to call this * method. **/ void setup(wl_touch *touch); /** * Releases the wl_touch interface. * After the interface has been released the Touch instance is no * longer valid and can be setup with another wl_touch interface. * * This method is automatically invoked when the Seat which created this * Touch gets released. **/ void release(); /** * Destroys the data held by this Touch. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new wl_touch interface * once there is a new connection available. * * This method is automatically invoked when the Seat which created this * Touch gets destroyed. * * @see release **/ void destroy(); /** * The TouchPoints of the latest touch event sequence. * Only valid till the next touch event sequence is started **/ QVector sequence() const; operator wl_touch*(); operator wl_touch*() const; Q_SIGNALS: /** * A new touch sequence is started. The previous sequence is discarded. * @param startPoint The first point which started the sequence **/ void sequenceStarted(KWayland::Client::TouchPoint *startPoint); /** * Sent if the compositor decides the touch stream is a global * gesture. **/ void sequenceCanceled(); /** * Emitted once all touch points are no longer down. **/ void sequenceEnded(); /** * Indicates the end of a contact point list. **/ void frameEnded(); /** * TouchPoint @p point got added to the sequence. **/ void pointAdded(KWayland::Client::TouchPoint *point); /** * TouchPoint @p point is no longer down. * A new TouchPoint might reuse the Id of the @p point. **/ void pointRemoved(KWayland::Client::TouchPoint *point); /** * TouchPoint @p point moved. **/ void pointMoved(KWayland::Client::TouchPoint *point); private: class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::TouchPoint*) #endif diff --git a/src/client/wayland_pointer_p.h b/src/client/wayland_pointer_p.h index 4f56932..6b4b1b0 100644 --- a/src/client/wayland_pointer_p.h +++ b/src/client/wayland_pointer_p.h @@ -1,104 +1,90 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_POINTER_P_H #define WAYLAND_POINTER_P_H struct wl_proxy; namespace KWayland { namespace Client { template class WaylandPointer { public: WaylandPointer() = default; WaylandPointer(Pointer *p) : m_pointer(p) {} WaylandPointer(const WaylandPointer &other) = delete; virtual ~WaylandPointer() { release(); } void setup(Pointer *pointer, bool foreign = false) { Q_ASSERT(pointer); Q_ASSERT(!m_pointer); m_pointer = pointer; m_foreign = foreign; } void release() { if (!m_pointer) { return; } if (!m_foreign) { deleter(m_pointer); } m_pointer = nullptr; } void destroy() { if (!m_pointer) { return; } if (!m_foreign) { free(m_pointer); } m_pointer = nullptr; } bool isValid() const { return m_pointer != nullptr; } operator Pointer*() { return m_pointer; } operator Pointer*() const { return m_pointer; } operator wl_proxy*() { return reinterpret_cast(m_pointer); } Pointer *operator->() { return m_pointer; } operator bool() { return isValid(); } operator bool() const { return isValid(); } private: Pointer *m_pointer = nullptr; bool m_foreign = false; }; } } #endif diff --git a/src/client/xdgdecoration.cpp b/src/client/xdgdecoration.cpp index a820941..47916b1 100644 --- a/src/client/xdgdecoration.cpp +++ b/src/client/xdgdecoration.cpp @@ -1,238 +1,224 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgdecoration.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "xdgshell.h" #include #include "wayland-xdg-decoration-unstable-v1-client-protocol.h" namespace KWayland { namespace Client { class Q_DECL_HIDDEN XdgDecorationManager::Private { public: Private() = default; void setup(zxdg_decoration_manager_v1 *arg); WaylandPointer xdgdecorationmanager; EventQueue *queue = nullptr; }; XdgDecorationManager::XdgDecorationManager(QObject *parent) : QObject(parent) , d(new Private) { } void XdgDecorationManager::Private::setup(zxdg_decoration_manager_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!xdgdecorationmanager); xdgdecorationmanager.setup(arg); } XdgDecorationManager::~XdgDecorationManager() { release(); } void XdgDecorationManager::setup(zxdg_decoration_manager_v1 *xdgdecorationmanager) { d->setup(xdgdecorationmanager); } void XdgDecorationManager::release() { d->xdgdecorationmanager.release(); } void XdgDecorationManager::destroy() { d->xdgdecorationmanager.destroy(); } XdgDecorationManager::operator zxdg_decoration_manager_v1*() { return d->xdgdecorationmanager; } XdgDecorationManager::operator zxdg_decoration_manager_v1*() const { return d->xdgdecorationmanager; } bool XdgDecorationManager::isValid() const { return d->xdgdecorationmanager.isValid(); } void XdgDecorationManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgDecorationManager::eventQueue() { return d->queue; } XdgDecoration *XdgDecorationManager::getToplevelDecoration(XdgShellSurface *toplevel, QObject *parent) { Q_ASSERT(isValid()); xdg_toplevel *toplevel_resource = *toplevel; if (!toplevel_resource) { //i.e using XDGShellV5 qWarning() << "Trying to create an XdgDecoration without an XDGShell stable toplevel object"; return nullptr; } auto p = new XdgDecoration(parent); auto w = zxdg_decoration_manager_v1_get_toplevel_decoration(d->xdgdecorationmanager, toplevel_resource); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN XdgDecoration::Private { public: Private(XdgDecoration *q); void setup(zxdg_toplevel_decoration_v1 *arg); WaylandPointer xdgdecoration; XdgDecoration::Mode m_mode = XdgDecoration::Mode::ClientSide; private: XdgDecoration *q; private: static void configureCallback(void *data, zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode); static const zxdg_toplevel_decoration_v1_listener s_listener; }; const zxdg_toplevel_decoration_v1_listener XdgDecoration::Private::s_listener = { configureCallback }; void XdgDecoration::Private::configureCallback(void *data, zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t m) { auto p = reinterpret_cast(data); Q_ASSERT(p->xdgdecoration == zxdg_toplevel_decoration_v1); switch (m) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: p->m_mode = XdgDecoration::Mode::ClientSide; break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: p->m_mode = XdgDecoration::Mode::ServerSide; break; } emit p->q->modeChanged(p->m_mode); } XdgDecoration::Private::Private(XdgDecoration *q) : q(q) { } XdgDecoration::XdgDecoration(QObject *parent) : QObject(parent) , d(new Private(this)) { } void XdgDecoration::Private::setup(zxdg_toplevel_decoration_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!xdgdecoration); xdgdecoration.setup(arg); zxdg_toplevel_decoration_v1_add_listener(xdgdecoration, &s_listener, this); } XdgDecoration::~XdgDecoration() { release(); } void XdgDecoration::setup(zxdg_toplevel_decoration_v1 *xdgdecoration) { d->setup(xdgdecoration); } void XdgDecoration::release() { d->xdgdecoration.release(); } void XdgDecoration::destroy() { d->xdgdecoration.destroy(); } XdgDecoration::operator zxdg_toplevel_decoration_v1*() { return d->xdgdecoration; } XdgDecoration::operator zxdg_toplevel_decoration_v1*() const { return d->xdgdecoration; } bool XdgDecoration::isValid() const { return d->xdgdecoration.isValid(); } void XdgDecoration::setMode(XdgDecoration::Mode mode) { Q_ASSERT(isValid()); uint32_t mode_raw; switch (mode) { case XdgDecoration::Mode::ClientSide: mode_raw = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; break; default: mode_raw = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; break; } zxdg_toplevel_decoration_v1_set_mode(d->xdgdecoration, mode_raw); } void XdgDecoration::unsetMode() { Q_ASSERT(isValid()); zxdg_toplevel_decoration_v1_unset_mode(d->xdgdecoration); } XdgDecoration::Mode XdgDecoration::mode() const { return d->m_mode; } } } diff --git a/src/client/xdgdecoration.h b/src/client/xdgdecoration.h index cbee0b9..8ddb58f 100644 --- a/src/client/xdgdecoration.h +++ b/src/client/xdgdecoration.h @@ -1,220 +1,206 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDG_DECORATION_UNSTABLE_V1_H #define KWAYLAND_CLIENT_XDG_DECORATION_UNSTABLE_V1_H #include #include struct zxdg_decoration_manager_v1; struct zxdg_toplevel_decoration_v1; namespace KWayland { namespace Client { class EventQueue; class XdgDecoration; class XdgShellSurface; /** * @short Wrapper for the zxdg_decoration_manager_v1 interface. * * This class provides a convenient wrapper for the zxdg_decoration_manager_v1 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the XdgDecorationManager interface: * @code * XdgDecorationManager *c = registry->createXdgDecorationManager(name, version); * @endcode * * This creates the XdgDecorationManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * XdgDecorationManager *c = new XdgDecorationManager; * c->setup(registry->bindXdgDecorationManager(name, version)); * @endcode * * The XdgDecorationManager can be used as a drop-in replacement for any zxdg_decoration_manager_v1 * pointer as it provides matching cast operators. * * If you use the QtWayland QPA you do not need to use this class. * * @see Registry * @since 5.54 **/ class KWAYLANDCLIENT_EXPORT XdgDecorationManager : public QObject { Q_OBJECT public: /** * Creates a new XdgDecorationManager. * Note: after constructing the XdgDecorationManager it is not yet valid and one needs * to call setup. In order to get a ready to use XdgDecorationManager prefer using * Registry::createXdgDecorationManager. **/ explicit XdgDecorationManager(QObject *parent = nullptr); virtual ~XdgDecorationManager(); /** * Setup this XdgDecorationManager to manage the @p xdgdecorationmanager. * When using Registry::createXdgDecorationManager there is no need to call this * method. **/ void setup(zxdg_decoration_manager_v1 *xdgdecorationmanager); /** * @returns @c true if managing a zxdg_decoration_manager_v1. **/ bool isValid() const; /** * Releases the zxdg_decoration_manager_v1 interface. * After the interface has been released the XdgDecorationManager instance is no * longer valid and can be setup with another zxdg_decoration_manager_v1 interface. **/ void release(); /** * Destroys the data held by this XdgDecorationManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_decoration_manager_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgdecorationmanager, &XdgDecorationManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this XdgDecorationManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this XdgDecorationManager. **/ EventQueue *eventQueue(); XdgDecoration *getToplevelDecoration(XdgShellSurface *toplevel, QObject *parent = nullptr); operator zxdg_decoration_manager_v1*(); operator zxdg_decoration_manager_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the XdgDecorationManager got created by * Registry::createXdgDecorationManager **/ void removed(); private: class Private; QScopedPointer d; }; class KWAYLANDCLIENT_EXPORT XdgDecoration : public QObject { Q_OBJECT public: enum class Mode { ClientSide, ServerSide }; Q_ENUM(Mode) virtual ~XdgDecoration(); /** * Setup this XdgDecoration to manage the @p xdgdecoration. * When using XdgDecorationManager::createXdgDecoration there is no need to call this * method. **/ void setup(zxdg_toplevel_decoration_v1 *xdgdecoration); /** * @returns @c true if managing a zxdg_toplevel_decoration_v1. **/ bool isValid() const; /** * Releases the zxdg_toplevel_decoration_v1 interface. * After the interface has been released the XdgDecoration instance is no * longer valid and can be setup with another zxdg_toplevel_decoration_v1 interface. **/ void release(); /** * Destroys the data held by this XdgDecoration. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_toplevel_decoration_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgdecoration, &XdgDecoration::destroy); * @endcode * * @see release **/ void destroy(); /** * @brief Request that the server puts us in a given mode. The compositor will respond with a modeChange * The compositor may ignore this request. */ void setMode(Mode mode); /** * @brief Unset our requested mode. The compositor can then configure this surface with the default mode */ void unsetMode(); /** * The mode configured by the server. */ Mode mode() const; operator zxdg_toplevel_decoration_v1*(); operator zxdg_toplevel_decoration_v1*() const; Q_SIGNALS: void modeChanged(KWayland::Client::XdgDecoration::Mode mode); private: friend class XdgDecorationManager; explicit XdgDecoration(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/xdgforeign.cpp b/src/client/xdgforeign.cpp index 16fe265..5fee9a0 100644 --- a/src/client/xdgforeign.cpp +++ b/src/client/xdgforeign.cpp @@ -1,271 +1,257 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgforeign.h" #include "xdgforeign_p.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { XdgExporter::Private::Private() { } XdgExporter::Private::~Private() { } XdgExporter::XdgExporter(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgExporter::~XdgExporter() { release(); } void XdgExporter::setup(zxdg_exporter_v2 *exporter) { d->setupV2(exporter); } void XdgExporter::release() { d->release(); } void XdgExporter::destroy() { d->destroy(); } XdgExporter::operator zxdg_exporter_v2*() { return d->exporterV2(); } XdgExporter::operator zxdg_exporter_v2*() const { return d->exporterV2(); } bool XdgExporter::isValid() const { return d->isValid(); } void XdgExporter::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgExporter::eventQueue() { return d->queue; } XdgExported *XdgExporter::exportTopLevel(Surface *surface, QObject *parent) { return d->exportTopLevelV2(surface, parent); } XdgImporter::Private::Private() { } XdgImporter::Private::~Private() { } XdgImporter::XdgImporter(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgImporter::~XdgImporter() { release(); } void XdgImporter::setup(zxdg_importer_v2 *importer) { d->setupV2(importer); } void XdgImporter::release() { d->release(); } void XdgImporter::destroy() { d->destroy(); } XdgImporter::operator zxdg_importer_v2*() { return d->importerV2(); } XdgImporter::operator zxdg_importer_v2*() const { return d->importerV2(); } bool XdgImporter::isValid() const { return d->isValid(); } void XdgImporter::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgImporter::eventQueue() { return d->queue; } XdgImported *XdgImporter::importTopLevel(const QString & handle, QObject *parent) { Q_ASSERT(isValid()); return d->importTopLevelV2(handle, parent); } XdgExported::XdgExported(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgExported::Private::Private(XdgExported *q) : q(q) { } XdgExported::Private::~Private() { } XdgExported::~XdgExported() { release(); } void XdgExported::setup(zxdg_exported_v2 *exported) { d->setupV2(exported); } void XdgExported::release() { d->release(); } void XdgExported::destroy() { d->destroy(); } QString XdgExported::handle() const { return d->handle; } XdgExported::operator zxdg_exported_v2*() { return d->exportedV2(); } XdgExported::operator zxdg_exported_v2*() const { return d->exportedV2(); } bool XdgExported::isValid() const { return d->isValid(); } XdgImported::Private::Private(XdgImported *q) : q(q) { } XdgImported::Private::~Private() { } XdgImported::XdgImported(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgImported::~XdgImported() { release(); } void XdgImported::setup(zxdg_imported_v2 *imported) { d->setupV2(imported); } void XdgImported::release() { d->release(); } void XdgImported::destroy() { d->destroy(); } XdgImported::operator zxdg_imported_v2*() { return d->importedV2(); } XdgImported::operator zxdg_imported_v2*() const { return d->importedV2(); } bool XdgImported::isValid() const { return d->isValid(); } void XdgImported::setParentOf(Surface *surface) { Q_ASSERT(isValid()); d->setParentOf(surface); } } } diff --git a/src/client/xdgforeign.h b/src/client/xdgforeign.h index 2b4392c..683cc31 100644 --- a/src/client/xdgforeign.h +++ b/src/client/xdgforeign.h @@ -1,384 +1,370 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDGFOREIGN_H #define KWAYLAND_CLIENT_XDGFOREIGN_H #include "surface.h" #include #include struct zxdg_exporter_v2; struct zxdg_importer_v2; struct zxdg_exported_v2; struct zxdg_imported_v2; namespace KWayland { namespace Client { class EventQueue; class Surface; class XdgExported; class XdgImported; /** * @short Wrapper for the zxdg_exporter_v2 interface. * * This class provides a convenient wrapper for the zxdg_exporter_v2 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the interface: * @code * *c = registry->create(name, version); * @endcode * * This creates the and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * *c = new ; * c->setup(registry->bind(name, version)); * @endcode * * The can be used as a drop-in replacement for any zxdg_exporter_v2 * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT XdgExporter : public QObject { Q_OBJECT public: virtual ~XdgExporter(); /** * Setup this to manage the @p . * When using Registry::create there is no need to call this * method. **/ void setup(zxdg_exporter_v2 *); /** * @returns @c true if managing a zxdg_exporter_v2. **/ bool isValid() const; /** * Releases the zxdg_exporter_v2 interface. * After the interface has been released the instance is no * longer valid and can be setup with another zxdg_exporter_v2 interface. **/ void release(); /** * Destroys the data held by this . * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_exporter_v2 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, , &::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this . **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this . **/ EventQueue *eventQueue(); /** * The export request exports the passed surface so that it can later be * imported via XdgImporter::importTopLevel. * A surface may be exported multiple times, and each exported handle may * be used to create an XdgImported multiple times. * @param surface the surface which we want to export an handle. * @param parent the parent in the QObject's hierarchy of the new XdgExported */ XdgExported *exportTopLevel(Surface *surface, QObject *parent = nullptr); operator zxdg_exporter_v2*(); operator zxdg_exporter_v2*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the got created by * Registry::create **/ void removed(); protected: class Private; explicit XdgExporter(Private *p, QObject *parent = nullptr); QScopedPointer d; }; /** * @short Wrapper for the zxdg_importer_v2 interface. * * This class provides a convenient wrapper for the zxdg_importer_v2 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the interface: * @code * *c = registry->create(name, version); * @endcode * * This creates the and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * *c = new ; * c->setup(registry->bind(name, version)); * @endcode * * The can be used as a drop-in replacement for any zxdg_importer_v2 * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT XdgImporter : public QObject { Q_OBJECT public: virtual ~XdgImporter(); /** * Setup this to manage the @p . * When using Registry::create there is no need to call this * method. **/ void setup(zxdg_importer_v2 *); /** * @returns @c true if managing a zxdg_importer_v2. **/ bool isValid() const; /** * Releases the zxdg_importer_v2 interface. * After the interface has been released the instance is no * longer valid and can be setup with another zxdg_importer_v2 interface. **/ void release(); /** * Destroys the data held by this . * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_importer_v2 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, , &::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this . **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this . **/ EventQueue *eventQueue(); /** * Imports a surface from any client given a handle * retrieved by exporting said surface using XdgExporter::exportTopLevel. * When called, a new XdgImported object will be created. * This new object represents the imported surface, and the importing * client can manipulate its relationship using it. * * @param handle the unique handle that represent an exported toplevel surface. * it has to have been generated by the XdgExporter by either this * or some other process (which would have communicated the handle * in some way, such as command line or a DBus call) * @param parent the parent in the QObject's hierarchy of the new XdgImported */ XdgImported *importTopLevel(const QString & handle, QObject *parent = nullptr); operator zxdg_importer_v2*(); operator zxdg_importer_v2*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the got created by * Registry::create **/ void removed(); protected: class Private; explicit XdgImporter(Private *p, QObject *parent = nullptr); QScopedPointer d; }; class KWAYLANDCLIENT_EXPORT XdgExported : public QObject { Q_OBJECT public: virtual ~XdgExported(); /** * Setup this to manage the @p . * When using ::create there is no need to call this * method. **/ void setup(zxdg_exported_v2 *); /** * @returns @c true if managing a zxdg_exported_v2. **/ bool isValid() const; /** * Releases the zxdg_exported_v2 interface. * After the interface has been released the instance is no * longer valid and can be setup with another zxdg_exported_v2 interface. **/ void release(); /** * Destroys the data held by this . * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_exported_v2 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, , &::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns The unique handle corresponding tho this exported surface. * Any process can import this toplevel surface provided they know this * unique string. */ QString handle() const; operator zxdg_exported_v2*(); operator zxdg_exported_v2*() const; Q_SIGNALS: /** * Emitted when the exported window is fully initialized. * the handle will be valid at this point **/ void done(); protected: friend class XdgExporter; class Private; explicit XdgExported(Private *p, QObject *parent = nullptr); QScopedPointer d; }; class KWAYLANDCLIENT_EXPORT XdgImported : public QObject { Q_OBJECT public: virtual ~XdgImported(); /** * Setup this to manage the @p . * When using ::create there is no need to call this * method. **/ void setup(zxdg_imported_v2 *); /** * @returns @c true if managing a zxdg_imported_v2. **/ bool isValid() const; /** * Releases the zxdg_imported_v2 interface. * After the interface has been released the instance is no * longer valid and can be setup with another zxdg_imported_v2 interface. **/ void release(); /** * Destroys the data held by this . * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_imported_v2 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, , &::destroy); * @endcode * * @see release **/ void destroy(); /** * Set the imported surface as the parent of some surface of the client. * The passed surface must be a toplevel xdg_surface. * Calling this function sets up a surface to surface relation with the same * stacking and positioning semantics as XdgShellSurface::setTransientFor * * @param surface the child surface, which must belong to this process. */ void setParentOf(Surface *surface); operator zxdg_imported_v2*(); operator zxdg_imported_v2*() const; Q_SIGNALS: /** * Emitted when the imported surface is not valid anymore, * for instance because it's no longer exported on the other end */ void importedDestroyed(); protected: friend class XdgImporter; class Private; explicit XdgImported(Private *p, QObject *parent = nullptr); QScopedPointer d; }; } } #endif diff --git a/src/client/xdgforeign_p.h b/src/client/xdgforeign_p.h index 9814974..1e55147 100644 --- a/src/client/xdgforeign_p.h +++ b/src/client/xdgforeign_p.h @@ -1,113 +1,99 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDGFOREIGN_P_H #define KWAYLAND_CLIENT_XDGFOREIGN_P_H #include "xdgforeign.h" #include namespace KWayland { namespace Client { class XdgExportedUnstableV2; class XdgImportedUnstableV2; class Q_DECL_HIDDEN XdgExporter::Private { public: Private(); virtual ~Private(); virtual XdgExported *exportTopLevelV2(Surface *surface, QObject *parent) = 0; virtual void setupV2(zxdg_exporter_v2 *arg) = 0; virtual zxdg_exporter_v2 *exporterV2() = 0; virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() = 0; EventQueue *queue = nullptr; }; class Q_DECL_HIDDEN XdgImporter::Private { public: Private(); virtual ~Private(); virtual XdgImported *importTopLevelV2(const QString & handle, QObject *parent) = 0; virtual void setupV2(zxdg_importer_v2 *arg) = 0; virtual zxdg_importer_v2 *importerV2() = 0; virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() = 0; EventQueue *queue = nullptr; }; class Q_DECL_HIDDEN XdgExported::Private { public: Private(XdgExported *q); virtual ~Private(); virtual void setupV2(zxdg_exported_v2 *) = 0; virtual zxdg_exported_v2 *exportedV2() = 0; virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() = 0; QString handle; protected: XdgExported *q; }; class Q_DECL_HIDDEN XdgImported::Private { public: Private(XdgImported *q); virtual ~Private(); virtual void setupV2(zxdg_imported_v2 *) = 0; virtual zxdg_imported_v2 *importedV2() = 0; virtual void setParentOf(Surface *surface) = 0; virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() = 0; protected: XdgImported *q; }; } } #endif diff --git a/src/client/xdgforeign_v2.cpp b/src/client/xdgforeign_v2.cpp index ede2d87..f4e55d5 100644 --- a/src/client/xdgforeign_v2.cpp +++ b/src/client/xdgforeign_v2.cpp @@ -1,337 +1,323 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgforeign_v2.h" #include "xdgforeign_p.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN XdgExporterUnstableV2::Private : public XdgExporter::Private { public: Private(); XdgExported *exportTopLevelV2(Surface *surface, QObject *parent) override; void setupV2(zxdg_exporter_v2 *arg) override; zxdg_exporter_v2 *exporterV2() override; void release() override; void destroy() override; bool isValid() override; WaylandPointer exporter; }; XdgExporterUnstableV2::Private::Private() : XdgExporter::Private() {} zxdg_exporter_v2 *XdgExporterUnstableV2::Private::exporterV2() { return exporter; } void XdgExporterUnstableV2::Private::release() { exporter.release(); } void XdgExporterUnstableV2::Private::destroy() { exporter.destroy(); } bool XdgExporterUnstableV2::Private::isValid() { return exporter.isValid(); } XdgExported *XdgExporterUnstableV2::Private::exportTopLevelV2(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new XdgExportedUnstableV2(parent); auto w = zxdg_exporter_v2_export_toplevel(exporter, *surface); if (queue) { queue->addProxy(w); } p->setup(w); return p; } XdgExporterUnstableV2::XdgExporterUnstableV2(QObject *parent) : XdgExporter(new Private, parent) { } void XdgExporterUnstableV2::Private::setupV2(zxdg_exporter_v2 *arg) { Q_ASSERT(arg); Q_ASSERT(!exporter); exporter.setup(arg); } XdgExporterUnstableV2::~XdgExporterUnstableV2() { } class Q_DECL_HIDDEN XdgImporterUnstableV2::Private : public XdgImporter::Private { public: Private(); XdgImported *importTopLevelV2(const QString & handle, QObject *parent) override; void setupV2(zxdg_importer_v2 *arg) override; zxdg_importer_v2 *importerV2() override; void release() override; void destroy() override; bool isValid() override; WaylandPointer importer; EventQueue *queue = nullptr; }; XdgImporterUnstableV2::Private::Private() : XdgImporter::Private() {} zxdg_importer_v2 *XdgImporterUnstableV2::Private::importerV2() { return importer; } void XdgImporterUnstableV2::Private::release() { importer.release(); } void XdgImporterUnstableV2::Private::destroy() { importer.destroy(); } bool XdgImporterUnstableV2::Private::isValid() { return importer.isValid(); } XdgImported *XdgImporterUnstableV2::Private::importTopLevelV2(const QString & handle, QObject *parent) { Q_ASSERT(isValid()); auto p = new XdgImportedUnstableV2(parent); auto w = zxdg_importer_v2_import_toplevel(importer, handle.toUtf8()); if (queue) { queue->addProxy(w); } p->setup(w); return p; } XdgImporterUnstableV2::XdgImporterUnstableV2(QObject *parent) : XdgImporter(new Private, parent) { } void XdgImporterUnstableV2::Private::setupV2(zxdg_importer_v2 *arg) { Q_ASSERT(arg); Q_ASSERT(!importer); importer.setup(arg); } XdgImporterUnstableV2::~XdgImporterUnstableV2() { } class Q_DECL_HIDDEN XdgExportedUnstableV2::Private : public XdgExported::Private { public: Private(XdgExportedUnstableV2 *q); void setupV2(zxdg_exported_v2 *arg) override; zxdg_exported_v2 *exportedV2() override; void release() override; void destroy() override; bool isValid() override; WaylandPointer exported; private: static void handleCallback(void *data, zxdg_exported_v2 *zxdg_exported_v2, const char * handle); static const zxdg_exported_v2_listener s_listener; }; zxdg_exported_v2 *XdgExportedUnstableV2::Private::exportedV2() { return exported; } void XdgExportedUnstableV2::Private::release() { exported.release(); } void XdgExportedUnstableV2::Private::destroy() { exported.destroy(); } bool XdgExportedUnstableV2::Private::isValid() { return exported.isValid(); } const zxdg_exported_v2_listener XdgExportedUnstableV2::Private::s_listener = { handleCallback }; void XdgExportedUnstableV2::Private::handleCallback(void *data, zxdg_exported_v2 *zxdg_exported_v2, const char * handle) { auto p = reinterpret_cast(data); Q_ASSERT(p->exported == zxdg_exported_v2); p->handle = handle; emit p->q->done(); } XdgExportedUnstableV2::XdgExportedUnstableV2(QObject *parent) : XdgExported(new Private(this), parent) { } void XdgExportedUnstableV2::Private::setupV2(zxdg_exported_v2 *arg) { Q_ASSERT(arg); Q_ASSERT(!exported); exported.setup(arg); zxdg_exported_v2_add_listener(exported, &s_listener, this); } XdgExportedUnstableV2::Private::Private(XdgExportedUnstableV2 *q) : XdgExported::Private::Private(q) { } XdgExportedUnstableV2::~XdgExportedUnstableV2() { } class Q_DECL_HIDDEN XdgImportedUnstableV2::Private : public XdgImported::Private { public: Private(XdgImportedUnstableV2 *q); void setupV2(zxdg_imported_v2 *arg) override; zxdg_imported_v2 *importedV2() override; void setParentOf(Surface *surface) override; void release() override; void destroy() override; bool isValid() override; WaylandPointer imported; private: static void destroyedCallback(void *data, zxdg_imported_v2 *zxdg_imported_v2); static const zxdg_imported_v2_listener s_listener; }; XdgImportedUnstableV2::Private::Private(XdgImportedUnstableV2 *q) : XdgImported::Private::Private(q) { } zxdg_imported_v2 *XdgImportedUnstableV2::Private::importedV2() { return imported; } void XdgImportedUnstableV2::Private::release() { imported.release(); } void XdgImportedUnstableV2::Private::destroy() { imported.destroy(); } bool XdgImportedUnstableV2::Private::isValid() { return imported.isValid(); } void XdgImportedUnstableV2::Private::setParentOf(Surface *surface) { Q_ASSERT(isValid()); zxdg_imported_v2_set_parent_of(imported, *surface); } const zxdg_imported_v2_listener XdgImportedUnstableV2::Private::s_listener = { destroyedCallback }; void XdgImportedUnstableV2::Private::destroyedCallback(void *data, zxdg_imported_v2 *zxdg_imported_v2) { auto p = reinterpret_cast(data); Q_ASSERT(p->imported == zxdg_imported_v2); p->q->release(); emit p->q->importedDestroyed(); } XdgImportedUnstableV2::XdgImportedUnstableV2(QObject *parent) : XdgImported(new Private(this), parent) { } void XdgImportedUnstableV2::Private::setupV2(zxdg_imported_v2 *arg) { Q_ASSERT(arg); Q_ASSERT(!imported); imported.setup(arg); zxdg_imported_v2_add_listener(imported, &s_listener, this); } XdgImportedUnstableV2::~XdgImportedUnstableV2() { } } } diff --git a/src/client/xdgforeign_v2.h b/src/client/xdgforeign_v2.h index 2500d99..dc53d3d 100644 --- a/src/client/xdgforeign_v2.h +++ b/src/client/xdgforeign_v2.h @@ -1,153 +1,139 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDGFOREIGN_V2_H #define KWAYLAND_CLIENT_XDGFOREIGN_V2_H #include "xdgforeign.h" #include "surface.h" #include #include struct zxdg_exporter_v2; struct zxdg_importer_v2; struct zxdg_exported_v2; struct zxdg_imported_v2; namespace KWayland { namespace Client { class EventQueue; class Surface; class XdgExportedUnstableV2; class XdgImportedUnstableV2; /** * @short Wrapper for the zxdg_exporter_v2 interface. * * This class provides a convenient wrapper for the zxdg_exporter_v2 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the interface: * @code * *c = registry->create(name, version); * @endcode * * This creates the and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * *c = new ; * c->setup(registry->bind(name, version)); * @endcode * * The can be used as a drop-in replacement for any zxdg_exporter_v2 * pointer as it provides matching cast operators. * * @see Registry **/ class Q_DECL_HIDDEN XdgExporterUnstableV2 : public XdgExporter { Q_OBJECT public: /** * Creates a new . * Note: after constructing the it is not yet valid and one needs * to call setup. In order to get a ready to use prefer using * Registry::create. **/ explicit XdgExporterUnstableV2(QObject *parent = nullptr); virtual ~XdgExporterUnstableV2(); private: class Private; }; /** * @short Wrapper for the zxdg_importer_v2 interface. * * This class provides a convenient wrapper for the zxdg_importer_v2 interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the interface: * @code * *c = registry->create(name, version); * @endcode * * This creates the and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * *c = new ; * c->setup(registry->bind(name, version)); * @endcode * * The can be used as a drop-in replacement for any zxdg_importer_v2 * pointer as it provides matching cast operators. * * @see Registry **/ class Q_DECL_HIDDEN XdgImporterUnstableV2 : public XdgImporter { Q_OBJECT public: /** * Creates a new . * Note: after constructing the it is not yet valid and one needs * to call setup. In order to get a ready to use prefer using * Registry::create. **/ explicit XdgImporterUnstableV2(QObject *parent = nullptr); virtual ~XdgImporterUnstableV2(); private: class Private; }; class Q_DECL_HIDDEN XdgExportedUnstableV2 : public XdgExported { Q_OBJECT public: virtual ~XdgExportedUnstableV2(); private: friend class XdgExporterUnstableV2; explicit XdgExportedUnstableV2(QObject *parent = nullptr); class Private; }; class Q_DECL_HIDDEN XdgImportedUnstableV2 : public XdgImported { Q_OBJECT public: virtual ~XdgImportedUnstableV2(); private: friend class XdgImporterUnstableV2; explicit XdgImportedUnstableV2(QObject *parent = nullptr); class Private; }; } } #endif diff --git a/src/client/xdgoutput.cpp b/src/client/xdgoutput.cpp index 7cdfb11..4881dcd 100644 --- a/src/client/xdgoutput.cpp +++ b/src/client/xdgoutput.cpp @@ -1,240 +1,226 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgoutput.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "output.h" #include #include #include namespace KWayland { namespace Client { class XdgOutputManager::Private { public: Private() = default; void setup(zxdg_output_manager_v1 *arg); WaylandPointer xdgoutputmanager; EventQueue *queue = nullptr; }; XdgOutputManager::XdgOutputManager(QObject *parent) : QObject(parent) , d(new Private) { } void XdgOutputManager::Private::setup(zxdg_output_manager_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!xdgoutputmanager); xdgoutputmanager.setup(arg); } XdgOutputManager::~XdgOutputManager() { release(); } void XdgOutputManager::setup(zxdg_output_manager_v1 *xdgoutputmanager) { d->setup(xdgoutputmanager); } void XdgOutputManager::release() { d->xdgoutputmanager.release(); } void XdgOutputManager::destroy() { d->xdgoutputmanager.destroy(); } XdgOutputManager::operator zxdg_output_manager_v1*() { return d->xdgoutputmanager; } XdgOutputManager::operator zxdg_output_manager_v1*() const { return d->xdgoutputmanager; } bool XdgOutputManager::isValid() const { return d->xdgoutputmanager.isValid(); } void XdgOutputManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgOutputManager::eventQueue() { return d->queue; } XdgOutput *XdgOutputManager::getXdgOutput(Output *output, QObject *parent) { Q_ASSERT(isValid()); auto p = new XdgOutput(parent); auto w = zxdg_output_manager_v1_get_xdg_output(d->xdgoutputmanager, *output); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } struct XdgOutputBuffer { QPoint logicalPosition; QSize logicalSize; }; class XdgOutput::Private { public: Private(XdgOutput *q); void setup(zxdg_output_v1 *arg); WaylandPointer xdgoutput; XdgOutputBuffer current; XdgOutputBuffer pending; private: XdgOutput *q; private: static void logical_positionCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y); static void logical_sizeCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height); static void doneCallback(void *data, zxdg_output_v1 *zxdg_output_v1); static const zxdg_output_v1_listener s_listener; }; const zxdg_output_v1_listener XdgOutput::Private::s_listener = { logical_positionCallback, logical_sizeCallback, doneCallback }; void XdgOutput::Private::logical_positionCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) { auto p = reinterpret_cast(data); Q_ASSERT(p->xdgoutput == zxdg_output_v1); p->pending.logicalPosition = QPoint(x,y); } void XdgOutput::Private::logical_sizeCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height) { auto p = reinterpret_cast(data); Q_ASSERT(p->xdgoutput == zxdg_output_v1); p->pending.logicalSize = QSize(width,height); } void XdgOutput::Private::doneCallback(void *data, zxdg_output_v1 *zxdg_output_v1) { auto p = reinterpret_cast(data); Q_ASSERT(p->xdgoutput == zxdg_output_v1); std::swap(p->current, p->pending); if (p->current.logicalSize != p->pending.logicalSize || p->current.logicalPosition != p->pending.logicalPosition) { emit p->q->changed(); } } XdgOutput::Private::Private(XdgOutput *qptr) : q(qptr) { } XdgOutput::XdgOutput(QObject *parent) : QObject(parent) , d(new Private(this)) { } void XdgOutput::Private::setup(zxdg_output_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!xdgoutput); xdgoutput.setup(arg); zxdg_output_v1_add_listener(xdgoutput, &s_listener, this); } XdgOutput::~XdgOutput() { release(); } void XdgOutput::setup(zxdg_output_v1 *xdgoutput) { d->setup(xdgoutput); } void XdgOutput::release() { d->xdgoutput.release(); } void XdgOutput::destroy() { d->xdgoutput.destroy(); } QSize XdgOutput::logicalSize() const { return d->current.logicalSize; } QPoint XdgOutput::logicalPosition() const { return d->current.logicalPosition; } XdgOutput::operator zxdg_output_v1*() { return d->xdgoutput; } XdgOutput::operator zxdg_output_v1*() const { return d->xdgoutput; } bool XdgOutput::isValid() const { return d->xdgoutput.isValid(); } } } diff --git a/src/client/xdgoutput.h b/src/client/xdgoutput.h index bec23e5..ee066d2 100644 --- a/src/client/xdgoutput.h +++ b/src/client/xdgoutput.h @@ -1,229 +1,215 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDGOUTPUT_H #define KWAYLAND_CLIENT_XDGOUTPUT_H #include #include #include #include struct zxdg_output_manager_v1; struct zxdg_output_v1; namespace KWayland { namespace Client { class EventQueue; class XdgOutput; class Output; /** * @short Wrapper for the zxdg_output_manager_v1 interface. * * This class provides a convenient wrapper for the zxdg_output_manager_v1 interface. * * This provides the logical size of the output. This is useful in case it doesn't match the * pixelSize / outputScale. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the XdgOutputManager interface: * @code * XdgOutputManager *c = registry->createXdgOutputManager(name, version); * @endcode * * This creates the XdgOutputManager and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * XdgOutputManager *c = new XdgOutputManager; * c->setup(registry->bindXdgOutputManager(name, version)); * @endcode * * The XdgOutputManager can be used as a drop-in replacement for any zxdg_output_manager_v1 * pointer as it provides matching cast operators. * * @since 5.47 * * @see Registry **/ class KWAYLANDCLIENT_EXPORT XdgOutputManager : public QObject { Q_OBJECT public: /** * Creates a new XdgOutputManager. * Note: after constructing the XdgOutputManager it is not yet valid and one needs * to call setup. In order to get a ready to use XdgOutputManager prefer using * Registry::createXdgOutputManager. **/ explicit XdgOutputManager(QObject *parent = nullptr); virtual ~XdgOutputManager(); /** * Setup this XdgOutputManager to manage the @p xdgoutputmanager. * When using Registry::createXdgOutputManager there is no need to call this * method. **/ void setup(zxdg_output_manager_v1 *xdgoutputmanager); /** * @returns @c true if managing a zxdg_output_manager_v1. **/ bool isValid() const; /** * Releases the zxdg_output_manager_v1 interface. * After the interface has been released the XdgOutputManager instance is no * longer valid and can be setup with another zxdg_output_manager_v1 interface. **/ void release(); /** * Destroys the data held by this XdgOutputManager. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_output_manager_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgoutputmanager, &XdgOutputManager::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this XdgOutputManager. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this XdgOutputManager. **/ EventQueue *eventQueue(); XdgOutput *getXdgOutput(Output *output, QObject *parent = nullptr); operator zxdg_output_manager_v1*(); operator zxdg_output_manager_v1*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the XdgOutputManager got created by * Registry::createXdgOutputManager **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the zxdg_output_v1 interface. * * This class provides a convenient wrapper for the zxdg_output_v1 interface. * * The XdgOutputManager can be used as a drop-in replacement for any zxdg_output_v1 * pointer as it provides matching cast operators. * * This protocol provides a potentially more correct size and position of the screen * than Output with respect to scaling. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT XdgOutput : public QObject { Q_OBJECT public: virtual ~XdgOutput(); /** * Setup this XdgOutput to manage the @p xdgoutput. * When using XdgOutputManager::createXdgOutput there is no need to call this * method. **/ void setup(zxdg_output_v1 *xdgoutput); /** * @returns @c true if managing a zxdg_output_v1. **/ bool isValid() const; /** * Releases the zxdg_output_v1 interface. * After the interface has been released the XdgOutput instance is no * longer valid and can be setup with another zxdg_output_v1 interface. **/ void release(); /** * Destroys the data held by this XdgOutput. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new zxdg_output_v1 interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgoutput, &XdgOutput::destroy); * @endcode * * @see release **/ void destroy(); operator zxdg_output_v1*(); operator zxdg_output_v1*() const; /** * The top left position of the output in compositor co-ordinates */ QPoint logicalPosition() const; /** * The size of the output in compositor co-ordinates * (i.e pixel size / output scale) */ QSize logicalSize() const; Q_SIGNALS: /** * Emitted when the logical position or size changes */ void changed(); private: friend class XdgOutputManager; explicit XdgOutput(QObject *parent = nullptr); class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/xdgshell.cpp b/src/client/xdgshell.cpp index ea62dc6..5c381e4 100644 --- a/src/client/xdgshell.cpp +++ b/src/client/xdgshell.cpp @@ -1,496 +1,482 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_p.h" #include "event_queue.h" #include "wayland_pointer_p.h" #include "seat.h" #include "surface.h" #include "output.h" #include "../compat/wayland-xdg-shell-v5-client-protocol.h" namespace KWayland { namespace Client { XdgShell::Private::~Private() = default; XdgShell::XdgShell(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgShell::~XdgShell() { release(); } void XdgShell::setup(xdg_shell *xdgshellv5) { d->setupV5(xdgshellv5); } void XdgShell::setup(zxdg_shell_v6 *xdgshellv6) { d->setupV6(xdgshellv6); } void XdgShell::setup(xdg_wm_base *xdg_wm_base) { d->setup(xdg_wm_base); } void XdgShell::release() { d->release(); } void XdgShell::destroy() { d->destroy(); } void XdgShell::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgShell::eventQueue() { return d->queue; } XdgShell::operator xdg_shell*() { return *(d.data()); } XdgShell::operator xdg_shell*() const { return *(d.data()); } XdgShell::operator zxdg_shell_v6*() { return *(d.data()); } XdgShell::operator zxdg_shell_v6*() const { return *(d.data()); } XdgShell::operator xdg_wm_base*() { return *(d.data()); } XdgShell::operator xdg_wm_base*() const { return *(d.data()); } bool XdgShell::isValid() const { return d->isValid(); } XdgShellSurface *XdgShell::createSurface(Surface *surface, QObject *parent) { return d->getXdgSurface(surface, parent); } XdgShellPopup *XdgShell::createPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) { return d->getXdgPopup(surface, parentSurface, seat, serial, parentPos, parent); } XdgShellPopup *XdgShell::createPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) { return d->getXdgPopup(surface, parentSurface, positioner, parent); } XdgShellPopup *XdgShell::createPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) { return d->getXdgPopup(surface, parentSurface, positioner, parent); } XdgShellSurface::Private::Private(XdgShellSurface *q) : q(q) { } XdgShellSurface::Private::~Private() = default; XdgShellSurface::XdgShellSurface(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgShellSurface::~XdgShellSurface() { release(); } void XdgShellSurface::setup(xdg_surface *xdgsurfacev5) { d->setupV5(xdgsurfacev5); } void XdgShellSurface::setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_toplevel_v6 *xdgtoplevelv6) { d->setupV6(xdgsurfacev6, xdgtoplevelv6); } void XdgShellSurface::setup(xdg_surface *xdgsurface, xdg_toplevel *xdgtoplevel) { d->setup(xdgsurface, xdgtoplevel); } void XdgShellSurface::release() { d->release(); } void XdgShellSurface::destroy() { d->destroy(); } void XdgShellSurface::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgShellSurface::eventQueue() { return d->queue; } XdgShellSurface::operator xdg_surface*() { return *(d.data()); } XdgShellSurface::operator xdg_surface*() const { return *(d.data()); } XdgShellSurface::operator xdg_toplevel*() { return *(d.data()); } XdgShellSurface::operator xdg_toplevel*() const { return *(d.data()); } XdgShellSurface::operator zxdg_surface_v6*() { return *(d.data()); } XdgShellSurface::operator zxdg_surface_v6*() const { return *(d.data()); } XdgShellSurface::operator zxdg_toplevel_v6*() { return *(d.data()); } XdgShellSurface::operator zxdg_toplevel_v6*() const { return *(d.data()); } bool XdgShellSurface::isValid() const { return d->isValid(); } void XdgShellSurface::setTransientFor(XdgShellSurface *parent) { d->setTransientFor(parent); } void XdgShellSurface::setTitle(const QString &title) { d->setTitle(title); } void XdgShellSurface::setAppId(const QByteArray &appId) { d->setAppId(appId); } void XdgShellSurface::requestShowWindowMenu(Seat *seat, quint32 serial, const QPoint &pos) { d->showWindowMenu(seat, serial, pos.x(), pos.y()); } void XdgShellSurface::requestMove(Seat *seat, quint32 serial) { d->move(seat, serial); } void XdgShellSurface::requestResize(Seat *seat, quint32 serial, Qt::Edges edges) { d->resize(seat, serial, edges); } void XdgShellSurface::ackConfigure(quint32 serial) { d->ackConfigure(serial); } void XdgShellSurface::setMaximized(bool set) { if (set) { d->setMaximized(); } else { d->unsetMaximized(); } } void XdgShellSurface::setFullscreen(bool set, Output *output) { if (set) { d->setFullscreen(output); } else { d->unsetFullscreen(); } } void XdgShellSurface::setMaxSize(const QSize &size) { d->setMaxSize(size); } void XdgShellSurface::setMinSize(const QSize &size) { d->setMinSize(size); } void XdgShellSurface::setWindowGeometry(const QRect &windowGeometry) { d->setWindowGeometry(windowGeometry); } void XdgShellSurface::requestMinimize() { d->setMinimized(); } void XdgShellSurface::setSize(const QSize &size) { if (d->size == size) { return; } d->size = size; emit sizeChanged(size); } QSize XdgShellSurface::size() const { return d->size; } XdgShellPopup::Private::~Private() = default; XdgShellPopup::Private::Private(XdgShellPopup *q) : q(q) { } XdgShellPopup::XdgShellPopup(Private *p, QObject *parent) : QObject(parent) , d(p) { } XdgShellPopup::~XdgShellPopup() { release(); } void XdgShellPopup::setup(xdg_popup *xdgpopupv5) { d->setupV5(xdgpopupv5); } void XdgShellPopup::setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_popup_v6 *xdgpopupv6) { d->setupV6(xdgsurfacev6, xdgpopupv6); } void XdgShellPopup::setup(xdg_surface *surface, xdg_popup *popup) { d->setup(surface, popup); } void XdgShellPopup::release() { d->release(); } void XdgShellPopup::destroy() { d->destroy(); } void XdgShellPopup::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *XdgShellPopup::eventQueue() { return d->queue; } void XdgShellPopup::requestGrab(KWayland::Client::Seat* seat, quint32 serial) { d->requestGrab(seat, serial); } void XdgShellPopup::ackConfigure(quint32 serial) { d->ackConfigure(serial); } void XdgShellPopup::setWindowGeometry(const QRect &windowGeometry) { d->setWindowGeometry(windowGeometry); } XdgShellPopup::operator xdg_surface*() { return *(d.data()); } XdgShellPopup::operator xdg_surface*() const { return *(d.data()); } XdgShellPopup::operator xdg_popup*() { return *(d.data()); } XdgShellPopup::operator xdg_popup*() const { return *(d.data()); } XdgShellPopup::operator zxdg_surface_v6*() { return *(d.data()); } XdgShellPopup::operator zxdg_surface_v6*() const { return *(d.data()); } XdgShellPopup::operator zxdg_popup_v6*() { return *(d.data()); } XdgShellPopup::operator zxdg_popup_v6*() const { return *(d.data()); } bool XdgShellPopup::isValid() const { return d->isValid(); } XdgPositioner::XdgPositioner(const QSize& initialSize, const QRect& anchor) :d (new Private) { d->initialSize = initialSize; d->anchorRect = anchor; } XdgPositioner::XdgPositioner(const XdgPositioner &other) :d (new Private) { *d = *other.d; } XdgPositioner::~XdgPositioner() { } void XdgPositioner::setInitialSize(const QSize& size) { d->initialSize = size; } QSize XdgPositioner::initialSize() const { return d->initialSize; } void XdgPositioner::setAnchorRect(const QRect& anchor) { d->anchorRect = anchor; } QRect XdgPositioner::anchorRect() const { return d->anchorRect; } void XdgPositioner::setAnchorEdge(Qt::Edges edge) { d->anchorEdge = edge; } Qt::Edges XdgPositioner::anchorEdge() const { return d->anchorEdge; } void XdgPositioner::setAnchorOffset(const QPoint& offset) { d->anchorOffset = offset; } QPoint XdgPositioner::anchorOffset() const { return d->anchorOffset; } void XdgPositioner::setGravity(Qt::Edges edge) { d->gravity = edge; } Qt::Edges XdgPositioner::gravity() const { return d->gravity; } void XdgPositioner::setConstraints(Constraints constraints) { d->constraints = constraints; } XdgPositioner::Constraints XdgPositioner::constraints() const { return d->constraints; } } } diff --git a/src/client/xdgshell.h b/src/client/xdgshell.h index 32ff7ba..f44a1ed 100644 --- a/src/client/xdgshell.h +++ b/src/client/xdgshell.h @@ -1,658 +1,644 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDG_SHELL_H #define KWAYLAND_CLIENT_XDG_SHELL_H #include #include #include #include //This is a mix of structs for both xdgshell unstable v5 AND xdg wm base stable struct xdg_wm_base; struct xdg_shell; struct xdg_surface; struct xdg_popup; struct xdg_toplevel; struct zxdg_shell_v6; struct zxdg_toplevel_v6; struct zxdg_surface_v6; struct zxdg_popup_v6; struct zxdg_position_v6; namespace KWayland { namespace Client { class EventQueue; class Output; class Surface; class Seat; class XdgShellPopup; class XdgShellSurface; /** * Builder class describing how a popup should be positioned * when created * * @since 5.39 */ class KWAYLANDCLIENT_EXPORT XdgPositioner { public: /* * Flags describing how a popup should be reposition if constrained */ enum class Constraint { /* * Slide the popup on the X axis until there is room */ SlideX = 1 << 0, /* * Slide the popup on the Y axis until there is room */ SlideY = 1 << 1, /* * Invert the anchor and gravity on the X axis */ FlipX = 1 << 2, /* * Invert the anchor and gravity on the Y axis */ FlipY = 1 << 3, /* * Resize the popup in the X axis */ ResizeX = 1 << 4, /* * Resize the popup in the Y axis */ ResizeY = 1 << 5 }; Q_DECLARE_FLAGS(Constraints, Constraint) XdgPositioner(const QSize &initialSize = QSize(), const QRect &anchor = QRect()); XdgPositioner(const XdgPositioner &other); ~XdgPositioner(); /** * Which edge of the anchor should the popup be positioned around */ //KF6 TODO use a better data type (enum of 8 options) rather than flags which allow invalid values Qt::Edges anchorEdge() const; void setAnchorEdge(Qt::Edges edge); /** * Specifies in what direction the popup should be positioned around the anchor * i.e if the gravity is "bottom", then then the top of top of the popup will be at the anchor edge * if the gravity is top, then the bottom of the popup will be at the anchor edge * */ //KF6 TODO use a better data type (enum of 8 options) rather than flags which allow invalid values Qt::Edges gravity() const; void setGravity(Qt::Edges edge); /** * The area this popup should be positioned around */ QRect anchorRect() const; void setAnchorRect(const QRect &anchor); /** * The size of the surface that is to be positioned. */ QSize initialSize() const; void setInitialSize(const QSize &size); /** * Specifies how the compositor should position the popup if it does not fit in the requested position */ Constraints constraints() const; void setConstraints(Constraints constraints); /** * An additional offset that should be applied from the anchor. */ QPoint anchorOffset() const; void setAnchorOffset(const QPoint &offset); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the xdg_shell interface. * * This class provides a convenient wrapper for the xdg_shell interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the XdgShell interface: * @code * XdgShell *c = registry->createXdgShell(name, version); * @endcode * * This creates the XdgShell and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * XdgShell *c = new XdgShell; * c->setup(registry->bindXdgShell(name, version)); * @endcode * * The XdgShell can be used as a drop-in replacement for any xdg_shell * pointer as it provides matching cast operators. * * @see Registry * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShell : public QObject { Q_OBJECT public: virtual ~XdgShell(); /** * Setup this XdgShell to manage the @p xdgshellv5. * When using Registry::createXdgShell there is no need to call this * method. **/ void setup(xdg_shell *xdgshellv5); /** * Setup this XdgShell to manage the @p xdgshellv6. * When using Registry::createXdgShell there is no need to call this * method. **/ void setup(zxdg_shell_v6 *xdgshellv6); /** * Setup this XdgShell to manage the @p xdg_wm_base. * When using Registry::createXdgShell there is no need to call this * method. **/ void setup(xdg_wm_base *xdg_wm_base); /** * @returns @c true if managing a xdg_shell. **/ bool isValid() const; /** * Releases the xdg_shell interface. * After the interface has been released the XdgShell instance is no * longer valid and can be setup with another xdg_shell interface. **/ void release(); /** * Destroys the data held by this XdgShell. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new xdg_shell interface * once there is a new connection available. * * This method is automatically invoked when the Registry which created this * XdgShell gets destroyed. * * @see release **/ void destroy(); /** * Sets the @p queue to use for creating objects with this XdgShell. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating objects with this XdgShell. **/ EventQueue *eventQueue(); /** * Creates a new XdgShellSurface for the given @p surface. **/ XdgShellSurface *createSurface(Surface *surface, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface. * This method is only valid for Xdgv5 **/ XdgShellPopup *createPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface with the given @p positioner. * This method is only valid for Xdgv6 onwards. * @since 5.39 **/ XdgShellPopup *createPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent = nullptr); /** * Creates a new XdgShellPopup for the given @p surface on top of @p parentSurface with the given @p positioner. * @since 5.39 **/ XdgShellPopup *createPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent = nullptr); operator xdg_wm_base*(); operator xdg_wm_base*() const; operator xdg_shell*(); operator xdg_shell*() const; operator zxdg_shell_v6*(); operator zxdg_shell_v6*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the XdgShell got created by * Registry::createXdgShell **/ void removed(); protected: /** * Creates a new XdgShell. * Note: after constructing the XdgShell it is not yet valid and one needs * to call setup. In order to get a ready to use XdgShell prefer using * Registry::createXdgShell. **/ class Private; explicit XdgShell(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; /** * * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShellSurface : public QObject { Q_OBJECT public: virtual ~XdgShellSurface(); /** * States the Surface can be in **/ enum class State { /** * The Surface is maximized. **/ Maximized = 1 << 0, /** * The Surface is fullscreen. **/ Fullscreen = 1 << 1, /** * The Surface is currently being resized by the Compositor. **/ Resizing = 1 << 2, /** * The Surface is considered active. Does not imply keyboard focus. **/ Activated = 1 << 3 }; Q_DECLARE_FLAGS(States, State) /** * Setup this XdgShellSurface to manage the @p xdgsurfacev5. * When using XdgShell::createXdgShellSurface there is no need to call this * method. **/ void setup(xdg_surface *xdgsurfacev5); /** * Setup this XdgShellSurface to manage the @p toplevel on the relevant @p xdgsurfacev6 * When using XdgShell::createXdgShellSurface there is no need to call this * method. **/ void setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_toplevel_v6 *toplevel); /** * Setup this XdgShellSurface to manage the @p toplevel on the relevant @p xdgsurface * When using XdgShell::createXdgShellSurface there is no need to call this * method. **/ void setup(xdg_surface *xdgsurface, xdg_toplevel *toplevel); /** * @returns @c true if managing a xdg_surface. **/ bool isValid() const; /** * Releases the xdg_surface interface. * After the interface has been released the XdgShellSurface instance is no * longer valid and can be setup with another xdg_surface interface. **/ void release(); /** * Destroys the data held by this XdgShellSurface. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new xdg_surface interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgsurfacev5, &XdgShellSurface::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * The currently configured size. * @see sizeChanged * @see setSize **/ QSize size() const; /** * Sets the size for the XdgShellSurface to @p size. * This is mostly an internal information. The actual size of the XdgShellSurface is * determined by the size of the Buffer attached to the XdgShellSurface's Surface. * * @param size The new size to be used for the XdgShellSurface * @see size * @see sizeChanged **/ void setSize(const QSize &size); /** * Set this XdgShellSurface as transient for @p parent. **/ void setTransientFor(XdgShellSurface *parent); /** * Sets the window title of this XdgShellSurface to @p title. **/ void setTitle(const QString &title); /** * Set an application identifier for the surface. **/ void setAppId(const QByteArray &appId); /** * Requests to show the window menu at @p pos in surface coordinates. **/ void requestShowWindowMenu(Seat *seat, quint32 serial, const QPoint &pos); /** * Requests a move on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to move the window * @param serial The serial of the pointer button press which should trigger the move **/ void requestMove(Seat *seat, quint32 serial); /** * Requests a resize on the given @p seat after the pointer button press with the given @p serial. * * @param seat The seat on which to resize the window * @param serial The serial of the pointer button press which should trigger the resize * @param edges A hint for the compositor to set e.g. an appropriate cursor image **/ void requestResize(Seat *seat, quint32 serial, Qt::Edges edges); /** * When a configure event is received, if a client commits the * Surface in response to the configure event, then the client * must make an ackConfigure request sometime before the commit * request, passing along the @p serial of the configure event. * @see configureRequested **/ void ackConfigure(quint32 serial); /** * Request to set this XdgShellSurface to be maximized if @p set is @c true. * If @p set is @c false it requests to unset the maximized state - if set. * * @param set Whether the XdgShellSurface should be maximized **/ void setMaximized(bool set); /** * Request to set this XdgShellSurface as fullscreen on @p output. * If @p set is @c true the Surface should be set to fullscreen, otherwise restore * from fullscreen state. * * @param set Whether the Surface should be fullscreen or not * @param output Optional output as hint to the compositor where the Surface should be put **/ void setFullscreen(bool set, Output *output = nullptr); /** * Request to the compositor to minimize this XdgShellSurface. **/ void requestMinimize(); /** * Set this surface to have a given maximum size * @since 5.39 */ void setMaxSize(const QSize &size); /** * Set this surface to have a given minimum size * @since 5.39 */ void setMinSize(const QSize &size); /** * Sets the position of the window contents within the buffer * @since 5.59 */ void setWindowGeometry(const QRect &windowGeometry); operator xdg_surface*(); operator xdg_surface*() const; operator xdg_toplevel*(); operator xdg_toplevel*() const; operator zxdg_surface_v6*(); operator zxdg_surface_v6*() const; operator zxdg_toplevel_v6*(); operator zxdg_toplevel_v6*() const; Q_SIGNALS: /** * The compositor requested to close this window. **/ void closeRequested(); /** * The compositor sent a configure with the new @p size and the @p states. * Before the next commit of the surface the @p serial needs to be passed to ackConfigure. **/ void configureRequested(const QSize &size, KWayland::Client::XdgShellSurface::States states, quint32 serial); /** * Emitted whenever the size of the XdgShellSurface changes by e.g. receiving a configure request. * * @see configureRequested * @see size * @see setSize **/ void sizeChanged(const QSize &); protected: class Private; explicit XdgShellSurface(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; /** * A XdgShellPopup is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * @since 5.25 **/ class KWAYLANDCLIENT_EXPORT XdgShellPopup : public QObject { Q_OBJECT public: virtual ~XdgShellPopup(); /** * Setup this XdgShellPopup to manage the @p xdgpopupv5. * When using XdgShell::createXdgShellPopup there is no need to call this * method. * * @deprecated Since 5.49. This was for XDGShellV5, this is now deprecated **/ KWAYLANDCLIENT_DEPRECATED_VERSION(5, 49, "Use XDGShell version >5") void setup(xdg_popup *xdgpopupv5); /** * Setup this XdgShellPopup to manage the @p xdgpopupv6 on associated @p xdgsurfacev6 * When using XdgShell::createXdgShellPopup there is no need to call this * method. * @since 5.39 **/ void setup(zxdg_surface_v6 *xdgsurfacev6, zxdg_popup_v6 *xdgpopup6); /** * Setup this XdgShellPopup to manage the @p xdgpopupv on associated @p xdgsurface * When using XdgShell::createXdgShellPopup there is no need to call this * method. * @since 5.XDGSTABLE **/ void setup(xdg_surface *xdgsurface, xdg_popup *xdgpopup); /** * @returns @c true if managing an xdg_popup. **/ bool isValid() const; /** * Releases the xdg_popup interface. * After the interface has been released the XdgShellPopup instance is no * longer valid and can be setup with another xdg_popup interface. **/ void release(); /** * Destroys the data held by this XdgShellPopup. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new xdg_popup interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, xdgpopupv5, &XdgShellPopup::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); /** * Requests a grab on this popup * @since 5.39 */ void requestGrab(Seat *seat, quint32 serial); /** * When a configure event is received, if a client commits the * Surface in response to the configure event, then the client * must make an ackConfigure request sometime before the commit * request, passing along the @p serial of the configure event. * @see configureRequested * @since 5.56 **/ void ackConfigure(quint32 serial); /** * Sets the position of the window contents within the buffer * @since 5.59 */ void setWindowGeometry(const QRect &windowGeometry); operator xdg_surface*(); operator xdg_surface*() const; operator xdg_popup*(); operator xdg_popup*() const; operator zxdg_surface_v6*(); operator zxdg_surface_v6*() const; operator zxdg_popup_v6*(); operator zxdg_popup_v6*() const; Q_SIGNALS: /** * This signal is emitted when a XdgShellPopup is dismissed by the * compositor. The user should delete this instance at this point. **/ void popupDone(); /** * Emitted when the server has configured the popup with the final location of @p relativePosition * This is emitted for V6 surfaces only * @since 5.39 **/ void configureRequested(const QRect &relativePosition, quint32 serial); protected: class Private; explicit XdgShellPopup(Private *p, QObject *parent = nullptr); private: QScopedPointer d; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::XdgShellSurface::States) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Client::XdgPositioner::Constraints) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner) Q_DECLARE_METATYPE(KWayland::Client::XdgShellSurface::State) Q_DECLARE_METATYPE(KWayland::Client::XdgShellSurface::States) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner::Constraint) Q_DECLARE_METATYPE(KWayland::Client::XdgPositioner::Constraints) #endif diff --git a/src/client/xdgshell_p.h b/src/client/xdgshell_p.h index 90a305e..de7cceb 100644 --- a/src/client/xdgshell_p.h +++ b/src/client/xdgshell_p.h @@ -1,361 +1,347 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_CLIENT_XDGSHELL_P_H #define KWAYLAND_CLIENT_XDGSHELL_P_H #include "xdgshell.h" #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN XdgShell::Private { public: virtual ~Private(); virtual void setupV5(xdg_shell *xdgshellv5) { Q_UNUSED(xdgshellv5) } virtual void setupV6(zxdg_shell_v6 *xdgshellv6) { Q_UNUSED(xdgshellv6) } virtual void setup(xdg_wm_base *xdgshell) { Q_UNUSED(xdgshell); } virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() const = 0; virtual operator xdg_shell*() { return nullptr; } virtual operator xdg_shell*() const { return nullptr; } virtual operator zxdg_shell_v6*() { return nullptr; } virtual operator zxdg_shell_v6*() const { return nullptr; } virtual operator xdg_wm_base*() { return nullptr; } virtual operator xdg_wm_base*() const { return nullptr; } virtual XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) = 0; virtual XdgShellPopup *getXdgPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) { Q_UNUSED(surface) Q_UNUSED(parentSurface) Q_UNUSED(seat) Q_UNUSED(serial) Q_UNUSED(parentPos) Q_UNUSED(parent) Q_ASSERT(false); return nullptr; }; virtual XdgShellPopup *getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) { Q_UNUSED(surface) Q_UNUSED(parentSurface) Q_UNUSED(positioner) Q_UNUSED(parent) Q_ASSERT(false); return nullptr; } virtual XdgShellPopup *getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) { Q_UNUSED(surface) Q_UNUSED(parentSurface) Q_UNUSED(positioner) Q_UNUSED(parent) Q_ASSERT(false); return nullptr; } EventQueue *queue = nullptr; protected: Private() = default; }; class XdgShellUnstableV5 : public XdgShell { Q_OBJECT public: explicit XdgShellUnstableV5(QObject *parent = nullptr); virtual ~XdgShellUnstableV5(); private: class Private; }; class XdgShellUnstableV6 : public XdgShell { Q_OBJECT public: explicit XdgShellUnstableV6(QObject *parent = nullptr); virtual ~XdgShellUnstableV6(); private: class Private; }; class XdgShellStable : public XdgShell { Q_OBJECT public: explicit XdgShellStable(QObject *parent = nullptr); virtual ~XdgShellStable(); private: class Private; }; class XdgShellSurfaceUnstableV5 : public XdgShellSurface { Q_OBJECT public: virtual ~XdgShellSurfaceUnstableV5(); private: explicit XdgShellSurfaceUnstableV5(QObject *parent = nullptr); friend class XdgShellUnstableV5; class Private; }; class XdgTopLevelUnstableV6 : public XdgShellSurface { Q_OBJECT public: virtual ~XdgTopLevelUnstableV6(); private: explicit XdgTopLevelUnstableV6(QObject *parent = nullptr); friend class XdgShellUnstableV6; class Private; }; class XdgTopLevelStable : public XdgShellSurface { Q_OBJECT public: virtual ~XdgTopLevelStable(); private: explicit XdgTopLevelStable(QObject *parent = nullptr); friend class XdgShellStable; class Private; }; class Q_DECL_HIDDEN XdgShellSurface::Private { public: virtual ~Private(); EventQueue *queue = nullptr; QSize size; virtual void setupV5(xdg_surface *surface) { Q_UNUSED(surface) } virtual void setupV6(zxdg_surface_v6 *surface, zxdg_toplevel_v6 *toplevel) { Q_UNUSED(toplevel) Q_UNUSED(surface) } virtual void setup(xdg_surface *surface, xdg_toplevel *toplevel) { Q_UNUSED(surface) Q_UNUSED(toplevel) } virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() const = 0; virtual operator xdg_surface*() { return nullptr; } virtual operator xdg_surface*() const { return nullptr; } virtual operator xdg_toplevel*() { return nullptr; } virtual operator xdg_toplevel*() const { return nullptr; } virtual operator zxdg_surface_v6*() { return nullptr; } virtual operator zxdg_surface_v6*() const { return nullptr; } virtual operator zxdg_toplevel_v6*() { return nullptr; } virtual operator zxdg_toplevel_v6*() const { return nullptr; } virtual void setTransientFor(XdgShellSurface *parent) = 0; virtual void setTitle(const QString &title) = 0; virtual void setAppId(const QByteArray &appId) = 0; virtual void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) = 0; virtual void move(Seat *seat, quint32 serial) = 0; virtual void resize(Seat *seat, quint32 serial, Qt::Edges edges) = 0; virtual void ackConfigure(quint32 serial) = 0; virtual void setMaximized() = 0; virtual void unsetMaximized() = 0; virtual void setFullscreen(Output *output) = 0; virtual void unsetFullscreen() = 0; virtual void setMinimized() = 0; virtual void setMaxSize(const QSize &size) = 0; virtual void setMinSize(const QSize &size) = 0; virtual void setWindowGeometry(const QRect &windowGeometry) { Q_UNUSED(windowGeometry); } protected: Private(XdgShellSurface *q); XdgShellSurface *q; }; class Q_DECL_HIDDEN XdgShellPopup::Private { public: Private(XdgShellPopup *q); virtual ~Private(); EventQueue *queue = nullptr; virtual void setupV5(xdg_popup *p) { Q_UNUSED(p) } virtual void setupV6(zxdg_surface_v6 *s, zxdg_popup_v6 *p) { Q_UNUSED(s) Q_UNUSED(p) } virtual void setup(xdg_surface *s, xdg_popup *p) { Q_UNUSED(s) Q_UNUSED(p) } virtual void release() = 0; virtual void destroy() = 0; virtual bool isValid() const = 0; virtual void requestGrab(Seat *seat, quint32 serial) { Q_UNUSED(seat); Q_UNUSED(serial); }; virtual void ackConfigure(quint32 serial) { Q_UNUSED(serial); } virtual void setWindowGeometry(const QRect &windowGeometry) { Q_UNUSED(windowGeometry); } virtual operator xdg_surface*() { return nullptr; } virtual operator xdg_surface*() const { return nullptr; } virtual operator xdg_popup*() { return nullptr; } virtual operator xdg_popup*() const { return nullptr; } virtual operator zxdg_surface_v6*() { return nullptr; } virtual operator zxdg_surface_v6*() const { return nullptr; } virtual operator zxdg_popup_v6*() { return nullptr; } virtual operator zxdg_popup_v6*() const { return nullptr; } protected: XdgShellPopup *q; private: }; class XdgPositioner::Private { public: QSize initialSize; QRect anchorRect; Qt::Edges gravity; Qt::Edges anchorEdge; XdgPositioner::Constraints constraints; QPoint anchorOffset; }; class XdgShellPopupUnstableV5 : public XdgShellPopup { public: virtual ~XdgShellPopupUnstableV5(); private: explicit XdgShellPopupUnstableV5(QObject *parent = nullptr); friend class XdgShellUnstableV5; class Private; }; class XdgShellPopupUnstableV6 : public XdgShellPopup { public: virtual ~XdgShellPopupUnstableV6(); private: explicit XdgShellPopupUnstableV6(QObject *parent = nullptr); friend class XdgShellUnstableV6; class Private; }; class XdgShellPopupStable : public XdgShellPopup { public: virtual ~XdgShellPopupStable(); private: explicit XdgShellPopupStable(QObject *parent = nullptr); friend class XdgShellStable; class Private; }; } } #endif diff --git a/src/client/xdgshell_stable.cpp b/src/client/xdgshell_stable.cpp index 146a9b6..b68b626 100644 --- a/src/client/xdgshell_stable.cpp +++ b/src/client/xdgshell_stable.cpp @@ -1,624 +1,610 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_p.h" #include "event_queue.h" #include "output.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class XdgShellStable::Private : public XdgShell::Private { public: void setup(xdg_wm_base *shell) override; void release() override; void destroy() override; bool isValid() const override; XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) override; XdgShellPopup *getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) override; XdgShellPopup *getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) override; using XdgShell::Private::operator xdg_shell*; using XdgShell::Private::operator zxdg_shell_v6*; operator xdg_wm_base*() override { return xdg_shell_base; } operator xdg_wm_base*() const override { return xdg_shell_base; } private: XdgShellPopup *internalGetXdgPopup(Surface *surface, xdg_surface *parentSurface, const XdgPositioner &positioner, QObject *parent); static void pingCallback(void *data, struct xdg_wm_base *shell, uint32_t serial); WaylandPointer xdg_shell_base; static const struct xdg_wm_base_listener s_shellListener; }; const struct xdg_wm_base_listener XdgShellStable::Private::s_shellListener = { pingCallback, }; void XdgShellStable::Private::pingCallback(void *data, struct xdg_wm_base *shell, uint32_t serial) { Q_UNUSED(data) xdg_wm_base_pong(shell, serial); } void XdgShellStable::Private::setup(xdg_wm_base *shell) { Q_ASSERT(shell); Q_ASSERT(!xdg_shell_base); xdg_shell_base.setup(shell); xdg_wm_base_add_listener(shell, &s_shellListener, this); } void XdgShellStable::Private::release() { xdg_shell_base.release(); } void XdgShellStable::Private::destroy() { xdg_shell_base.destroy(); } bool XdgShellStable::Private::isValid() const { return xdg_shell_base.isValid(); } XdgShellSurface *XdgShellStable::Private::getXdgSurface(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto ss = xdg_wm_base_get_xdg_surface(xdg_shell_base, *surface); if (!ss) { return nullptr; } auto s = new XdgTopLevelStable(parent); auto toplevel = xdg_surface_get_toplevel(ss); if (queue) { queue->addProxy(ss); queue->addProxy(toplevel); } s->setup(ss, toplevel); return s; } XdgShellPopup *XdgShellStable::Private::getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) { return internalGetXdgPopup(surface, *parentSurface, positioner, parent); } XdgShellPopup *XdgShellStable::Private::getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) { return internalGetXdgPopup(surface, *parentSurface, positioner, parent); } XdgShellPopup *XdgShellStable::Private::internalGetXdgPopup(Surface *surface, xdg_surface *parentSurface, const XdgPositioner &positioner, QObject *parent) { Q_ASSERT(isValid()); auto ss = xdg_wm_base_get_xdg_surface(xdg_shell_base, *surface); if (!ss) { return nullptr; } auto p = xdg_wm_base_create_positioner(xdg_shell_base); auto anchorRect = positioner.anchorRect(); xdg_positioner_set_anchor_rect(p, anchorRect.x(), anchorRect.y(), anchorRect.width(), anchorRect.height()); QSize initialSize = positioner.initialSize(); xdg_positioner_set_size(p, initialSize.width(), initialSize.height()); QPoint anchorOffset = positioner.anchorOffset(); if (!anchorOffset.isNull()) { xdg_positioner_set_offset(p, anchorOffset.x(), anchorOffset.y()); } uint32_t anchor = XDG_POSITIONER_ANCHOR_NONE; if (positioner.anchorEdge().testFlag(Qt::TopEdge)) { if (positioner.anchorEdge().testFlag(Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::TopEdge)) { anchor = XDG_POSITIONER_ANCHOR_TOP_LEFT; } else if (positioner.anchorEdge().testFlag(Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::TopEdge)) { anchor = XDG_POSITIONER_ANCHOR_TOP_RIGHT; } else if ((positioner.anchorEdge() & ~Qt::TopEdge) == Qt::Edges()) { anchor = XDG_POSITIONER_ANCHOR_TOP; } } else if (positioner.anchorEdge().testFlag(Qt::BottomEdge)) { if (positioner.anchorEdge().testFlag(Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::BottomEdge)) { anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; } else if (positioner.anchorEdge().testFlag(Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::BottomEdge)) { anchor = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; } else if ((positioner.anchorEdge() & ~Qt::BottomEdge) == Qt::Edges()) { anchor = XDG_POSITIONER_ANCHOR_BOTTOM; } } else if (positioner.anchorEdge().testFlag(Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::Edges())) { anchor = XDG_POSITIONER_ANCHOR_RIGHT; } else if (positioner.anchorEdge().testFlag(Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::Edges())) { anchor = XDG_POSITIONER_ANCHOR_LEFT; } if (anchor != 0) { xdg_positioner_set_anchor(p, anchor); } uint32_t gravity = XDG_POSITIONER_GRAVITY_NONE; if (positioner.gravity().testFlag(Qt::TopEdge)) { if (positioner.gravity().testFlag(Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::TopEdge)) { gravity = XDG_POSITIONER_GRAVITY_TOP_LEFT; } else if (positioner.gravity().testFlag(Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::TopEdge)) { gravity = XDG_POSITIONER_GRAVITY_TOP_RIGHT; } else if ((positioner.gravity() & ~Qt::TopEdge) == Qt::Edges()) { gravity = XDG_POSITIONER_GRAVITY_TOP; } } else if (positioner.gravity().testFlag(Qt::BottomEdge)) { if (positioner.gravity().testFlag(Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::BottomEdge)) { gravity = XDG_POSITIONER_GRAVITY_BOTTOM_LEFT; } else if (positioner.gravity().testFlag(Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::BottomEdge)) { gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; } else if ((positioner.gravity() & ~Qt::BottomEdge) == Qt::Edges()) { gravity = XDG_POSITIONER_GRAVITY_BOTTOM; } } else if (positioner.gravity().testFlag(Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::Edges())) { gravity = XDG_POSITIONER_GRAVITY_RIGHT; } else if (positioner.gravity().testFlag(Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::Edges())) { gravity = XDG_POSITIONER_GRAVITY_LEFT; } if (gravity != 0) { xdg_positioner_set_gravity(p, gravity); } uint32_t constraint = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; if (positioner.constraints().testFlag(XdgPositioner::Constraint::SlideX)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::SlideY)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::FlipX)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::FlipY)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeX)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeY)) { constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; } if (constraint != 0) { xdg_positioner_set_constraint_adjustment(p, constraint); } XdgShellPopup *s = new XdgShellPopupStable(parent); auto popup = xdg_surface_get_popup(ss, parentSurface, p); if (queue) { //deliberately not adding the positioner because the positioner has no events sent to it queue->addProxy(ss); queue->addProxy(popup); } s->setup(ss, popup); xdg_positioner_destroy(p); return s; } XdgShellStable::XdgShellStable(QObject *parent) : XdgShell(new Private, parent) { } XdgShellStable::~XdgShellStable() = default; //A top level wraps both xdg_surface and xdg_top_level into the public API XdgShelllSurface class XdgTopLevelStable::Private : public XdgShellSurface::Private { public: Private(XdgShellSurface *q); WaylandPointer xdgtoplevel; WaylandPointer xdgsurface; void setup(xdg_surface *surface, xdg_toplevel *toplevel) override; void release() override; void destroy() override; bool isValid() const override; using XdgShellSurface::Private::operator zxdg_toplevel_v6*; using XdgShellSurface::Private::operator zxdg_surface_v6*; operator xdg_surface*() override { return xdgsurface; } operator xdg_surface*() const override { return xdgsurface; } operator xdg_toplevel*() override { return xdgtoplevel; } operator xdg_toplevel*() const override { return xdgtoplevel; } void setTransientFor(XdgShellSurface *parent) override; void setTitle(const QString &title) override; void setAppId(const QByteArray &appId) override; void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) override; void move(Seat *seat, quint32 serial) override; void resize(Seat *seat, quint32 serial, Qt::Edges edges) override; void ackConfigure(quint32 serial) override; void setMaximized() override; void unsetMaximized() override; void setFullscreen(Output *output) override; void unsetFullscreen() override; void setMinimized() override; void setMaxSize(const QSize &size) override; void setMinSize(const QSize &size) override; void setWindowGeometry(const QRect &windowGeometry) override; private: QSize pendingSize; States pendingState; static void configureCallback(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state); static void closeCallback(void *data, xdg_toplevel *xdg_toplevel); static void surfaceConfigureCallback(void *data, xdg_surface *xdg_surface, uint32_t serial); static const struct xdg_toplevel_listener s_toplevelListener; static const struct xdg_surface_listener s_surfaceListener; }; const struct xdg_toplevel_listener XdgTopLevelStable::Private::s_toplevelListener = { configureCallback, closeCallback }; const struct xdg_surface_listener XdgTopLevelStable::Private::s_surfaceListener = { surfaceConfigureCallback }; void XdgTopLevelStable::Private::surfaceConfigureCallback(void *data, struct xdg_surface *surface, uint32_t serial) { Q_UNUSED(surface) auto s = static_cast(data); s->q->configureRequested(s->pendingSize, s->pendingState, serial); if (!s->pendingSize.isNull()) { s->q->setSize(s->pendingSize); s->pendingSize = QSize(); } s->pendingState = {}; } void XdgTopLevelStable::Private::configureCallback(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state) { Q_UNUSED(xdg_toplevel) auto s = static_cast(data); States states; uint32_t *statePtr = static_cast(state->data); for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { switch (statePtr[i]) { case XDG_TOPLEVEL_STATE_MAXIMIZED: states = states | XdgShellSurface::State::Maximized; break; case XDG_TOPLEVEL_STATE_FULLSCREEN: states = states | XdgShellSurface::State::Fullscreen; break; case XDG_TOPLEVEL_STATE_RESIZING: states = states | XdgShellSurface::State::Resizing; break; case XDG_TOPLEVEL_STATE_ACTIVATED: states = states | XdgShellSurface::State::Activated; break; } } s->pendingSize = QSize(width, height); s->pendingState = states; } void XdgTopLevelStable::Private::closeCallback(void *data, xdg_toplevel *xdg_toplevel) { auto s = static_cast(data); Q_ASSERT(s->xdgtoplevel == xdg_toplevel); emit s->q->closeRequested(); } XdgTopLevelStable::Private::Private(XdgShellSurface *q) : XdgShellSurface::Private(q) { } void XdgTopLevelStable::Private::setup(xdg_surface *surface, xdg_toplevel *topLevel) { Q_ASSERT(surface); Q_ASSERT(!xdgtoplevel); xdgsurface.setup(surface); xdgtoplevel.setup(topLevel); xdg_surface_add_listener(xdgsurface, &s_surfaceListener, this); xdg_toplevel_add_listener(xdgtoplevel, &s_toplevelListener, this); } void XdgTopLevelStable::Private::release() { xdgtoplevel.release(); xdgsurface.release(); } void XdgTopLevelStable::Private::destroy() { xdgtoplevel.destroy(); xdgsurface.destroy(); } bool XdgTopLevelStable::Private::isValid() const { return xdgtoplevel.isValid() && xdgsurface.isValid(); } void XdgTopLevelStable::Private::setTransientFor(XdgShellSurface *parent) { xdg_toplevel *parentSurface = nullptr; if (parent) { parentSurface = *parent; } xdg_toplevel_set_parent(xdgtoplevel, parentSurface); } void XdgTopLevelStable::Private::setTitle(const QString & title) { xdg_toplevel_set_title(xdgtoplevel, title.toUtf8().constData()); } void XdgTopLevelStable::Private::setAppId(const QByteArray & appId) { xdg_toplevel_set_app_id(xdgtoplevel, appId.constData()); } void XdgTopLevelStable::Private::showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) { xdg_toplevel_show_window_menu(xdgtoplevel, *seat, serial, x, y); } void XdgTopLevelStable::Private::move(Seat *seat, quint32 serial) { xdg_toplevel_move(xdgtoplevel, *seat, serial); } void XdgTopLevelStable::Private::resize(Seat *seat, quint32 serial, Qt::Edges edges) { uint wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_NONE; if (edges.testFlag(Qt::TopEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP; } } else if (edges.testFlag(Qt::BottomEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; } } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; } else if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) { wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; } xdg_toplevel_resize(xdgtoplevel, *seat, serial, wlEdge); } void XdgTopLevelStable::Private::ackConfigure(quint32 serial) { xdg_surface_ack_configure(xdgsurface, serial); } void XdgTopLevelStable::Private::setMaximized() { xdg_toplevel_set_maximized(xdgtoplevel); } void XdgTopLevelStable::Private::unsetMaximized() { xdg_toplevel_unset_maximized(xdgtoplevel); } void XdgTopLevelStable::Private::setFullscreen(Output *output) { wl_output *o = nullptr; if (output) { o = *output; } xdg_toplevel_set_fullscreen(xdgtoplevel, o); } void XdgTopLevelStable::Private::unsetFullscreen() { xdg_toplevel_unset_fullscreen(xdgtoplevel); } void XdgTopLevelStable::Private::setMinimized() { xdg_toplevel_set_minimized(xdgtoplevel); } void XdgTopLevelStable::Private::setMaxSize(const QSize &size) { xdg_toplevel_set_max_size(xdgtoplevel, size.width(), size.height()); } void XdgTopLevelStable::Private::setMinSize(const QSize &size) { xdg_toplevel_set_min_size(xdgtoplevel, size.width(), size.height()); } void XdgTopLevelStable::Private::setWindowGeometry(const QRect &windowGeometry) { xdg_surface_set_window_geometry(xdgsurface, windowGeometry.x(), windowGeometry.y(), windowGeometry.width(), windowGeometry.height()); } XdgTopLevelStable::XdgTopLevelStable(QObject *parent) : XdgShellSurface(new Private(this), parent) { } XdgTopLevelStable::~XdgTopLevelStable() = default; class XdgShellPopupStable::Private : public XdgShellPopup::Private { public: Private(XdgShellPopup *q); void setup(xdg_surface *s, xdg_popup *p) override; void release() override; void destroy() override; bool isValid() const override; void requestGrab(Seat *seat, quint32 serial) override; void ackConfigure(quint32 serial) override; void setWindowGeometry(const QRect &windowGeometry) override; using XdgShellPopup::Private::operator zxdg_popup_v6*; using XdgShellPopup::Private::operator zxdg_surface_v6*; operator xdg_surface*() override { return xdgsurface; } operator xdg_surface*() const override { return xdgsurface; } operator xdg_popup*() override { return xdgpopup; } operator xdg_popup*() const override { return xdgpopup; } WaylandPointer xdgsurface; WaylandPointer xdgpopup; QRect pendingRect; private: static void configureCallback(void *data, xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height); static void popupDoneCallback(void *data, xdg_popup *xdg_popup); static void surfaceConfigureCallback(void *data, xdg_surface *xdg_surface, uint32_t serial); static const struct xdg_popup_listener s_popupListener; static const struct xdg_surface_listener s_surfaceListener; }; const struct xdg_popup_listener XdgShellPopupStable::Private::s_popupListener = { configureCallback, popupDoneCallback }; const struct xdg_surface_listener XdgShellPopupStable::Private::s_surfaceListener = { surfaceConfigureCallback, }; void XdgShellPopupStable::Private::configureCallback(void *data, xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(xdg_popup) auto s = static_cast(data); s->pendingRect = QRect(x, y, width, height); } void XdgShellPopupStable::Private::surfaceConfigureCallback(void *data, struct xdg_surface *surface, uint32_t serial) { Q_UNUSED(surface) auto s = static_cast(data); s->q->configureRequested(s->pendingRect, serial); s->pendingRect = QRect(); } void XdgShellPopupStable::Private::popupDoneCallback(void *data, xdg_popup *xdg_popup) { auto s = static_cast(data); Q_ASSERT(s->xdgpopup == xdg_popup); emit s->q->popupDone(); } XdgShellPopupStable::Private::Private(XdgShellPopup *q) : XdgShellPopup::Private(q) { } void XdgShellPopupStable::Private::setup(xdg_surface *s, xdg_popup *p) { Q_ASSERT(p); Q_ASSERT(!xdgsurface); Q_ASSERT(!xdgpopup); xdgsurface.setup(s); xdgpopup.setup(p); xdg_surface_add_listener(xdgsurface, &s_surfaceListener, this); xdg_popup_add_listener(xdgpopup, &s_popupListener, this); } void XdgShellPopupStable::Private::release() { xdgpopup.release(); } void XdgShellPopupStable::Private::destroy() { xdgpopup.destroy(); } bool XdgShellPopupStable::Private::isValid() const { return xdgpopup.isValid(); } void XdgShellPopupStable::Private::requestGrab(Seat *seat, quint32 serial) { xdg_popup_grab(xdgpopup, *seat, serial); } void XdgShellPopupStable::Private::ackConfigure(quint32 serial) { xdg_surface_ack_configure(xdgsurface, serial); } void XdgShellPopupStable::Private::setWindowGeometry(const QRect &windowGeometry) { xdg_surface_set_window_geometry(xdgsurface, windowGeometry.x(), windowGeometry.y(), windowGeometry.width(), windowGeometry.height()); } XdgShellPopupStable::XdgShellPopupStable(QObject *parent) : XdgShellPopup(new Private(this), parent) { } XdgShellPopupStable::~XdgShellPopupStable() = default; } } diff --git a/src/client/xdgshell_v5.cpp b/src/client/xdgshell_v5.cpp index 53d52df..046848a 100644 --- a/src/client/xdgshell_v5.cpp +++ b/src/client/xdgshell_v5.cpp @@ -1,419 +1,405 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_p.h" #include "event_queue.h" #include "output.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" #include "../compat/wayland-xdg-shell-v5-client-protocol.h" namespace KWayland { namespace Client { class XdgShellUnstableV5::Private : public XdgShell::Private { public: void setupV5(xdg_shell *shell) override; void release() override; void destroy() override; bool isValid() const override; XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) override; XdgShellPopup *getXdgPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) override; using XdgShell::Private::operator xdg_wm_base*; using XdgShell::Private::operator zxdg_shell_v6*; operator xdg_shell*() override { return xdgshellv5; } operator xdg_shell*() const override { return xdgshellv5; } static void pingCallback(void *data, struct xdg_shell *shell, uint32_t serial); WaylandPointer xdgshellv5; static const struct zxdg_shell_v5_listener s_shellListener; }; const struct zxdg_shell_v5_listener XdgShellUnstableV5::Private::s_shellListener = { pingCallback, }; void XdgShellUnstableV5::Private::pingCallback(void *data, struct xdg_shell *shell, uint32_t serial) { Q_UNUSED(data); zxdg_shell_v5_pong(shell, serial); } void XdgShellUnstableV5::Private::setupV5(xdg_shell *shell) { Q_ASSERT(shell); Q_ASSERT(!xdgshellv5); xdgshellv5.setup(shell); zxdg_shell_v5_use_unstable_version(xdgshellv5, 5); zxdg_shell_v5_add_listener(shell, &s_shellListener, this); } void XdgShellUnstableV5::Private::release() { xdgshellv5.release(); } void XdgShellUnstableV5::Private::destroy() { xdgshellv5.destroy(); } bool XdgShellUnstableV5::Private::isValid() const { return xdgshellv5.isValid(); } XdgShellSurface *XdgShellUnstableV5::Private::getXdgSurface(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); XdgShellSurface *s = new XdgShellSurfaceUnstableV5(parent); auto w = zxdg_shell_v5_get_xdg_surface(xdgshellv5, *surface); if (queue) { queue->addProxy(w); } s->setup(w); return s; } XdgShellPopup *XdgShellUnstableV5::Private::getXdgPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) { Q_ASSERT(isValid()); XdgShellPopup *s = new XdgShellPopupUnstableV5(parent); auto w = zxdg_shell_v5_get_xdg_popup(xdgshellv5, *surface, *parentSurface, *seat, serial, parentPos.x(), parentPos.y()); if (queue) { queue->addProxy(w); } s->setup(w); return s; } XdgShellUnstableV5::XdgShellUnstableV5(QObject *parent) : XdgShell(new Private, parent) { } XdgShellUnstableV5::~XdgShellUnstableV5() = default; class XdgShellSurfaceUnstableV5::Private : public XdgShellSurface::Private { public: Private(XdgShellSurface *q); WaylandPointer xdgsurfacev5; void setupV5(xdg_surface *surface) override; void release() override; void destroy() override; bool isValid() const override; using XdgShellSurface::Private::operator zxdg_surface_v6*; using XdgShellSurface::Private::operator zxdg_toplevel_v6*; using XdgShellSurface::Private::operator xdg_toplevel*; operator xdg_surface*() override { return xdgsurfacev5; } operator xdg_surface*() const override { return xdgsurfacev5; } void setTransientFor(XdgShellSurface *parent) override; void setTitle(const QString &title) override; void setAppId(const QByteArray &appId) override; void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) override; void move(Seat *seat, quint32 serial) override; void resize(Seat *seat, quint32 serial, Qt::Edges edges) override; void ackConfigure(quint32 serial) override; void setMaximized() override; void unsetMaximized() override; void setFullscreen(Output *output) override; void unsetFullscreen() override; void setMinimized() override; void setMaxSize(const QSize &size) override; void setMinSize(const QSize &size) override; private: static void configureCallback(void *data, xdg_surface *xdg_surface, int32_t width, int32_t height, wl_array *states, uint32_t serial); static void closeCallback(void *data, xdg_surface *xdg_surface); static const struct zxdg_surface_v5_listener s_listener; }; const struct zxdg_surface_v5_listener XdgShellSurfaceUnstableV5::Private::s_listener = { configureCallback, closeCallback }; void XdgShellSurfaceUnstableV5::Private::configureCallback(void *data, xdg_surface *xdg_surface, int32_t width, int32_t height, wl_array *wlStates, uint32_t serial) { auto s = reinterpret_cast(data); Q_ASSERT(s->xdgsurfacev5 == xdg_surface); uint32_t *state = reinterpret_cast(wlStates->data); size_t numStates = wlStates->size / sizeof(uint32_t); States states; for (size_t i = 0; i < numStates; i++) { switch (state[i]) { case ZXDG_SURFACE_V5_STATE_MAXIMIZED: states = states | XdgShellSurface::State::Maximized; break; case ZXDG_SURFACE_V5_STATE_FULLSCREEN: states = states | XdgShellSurface::State::Fullscreen; break; case ZXDG_SURFACE_V5_STATE_RESIZING: states = states | XdgShellSurface::State::Resizing; break; case ZXDG_SURFACE_V5_STATE_ACTIVATED: states = states | XdgShellSurface::State::Activated; break; } } const QSize size = QSize(width, height); emit s->q->configureRequested(size, states, serial); if (!size.isNull()) { s->q->setSize(size); } } void XdgShellSurfaceUnstableV5::Private::closeCallback(void *data, xdg_surface *xdg_surface) { auto s = reinterpret_cast(data); Q_ASSERT(s->xdgsurfacev5 == xdg_surface); emit s->q->closeRequested(); } XdgShellSurfaceUnstableV5::Private::Private(XdgShellSurface *q) : XdgShellSurface::Private(q) { } void XdgShellSurfaceUnstableV5::Private::setupV5(xdg_surface *surface) { Q_ASSERT(surface); Q_ASSERT(!xdgsurfacev5); xdgsurfacev5.setup(surface); zxdg_surface_v5_add_listener(xdgsurfacev5, &s_listener, this); } void XdgShellSurfaceUnstableV5::Private::release() { xdgsurfacev5.release(); } void XdgShellSurfaceUnstableV5::Private::destroy() { xdgsurfacev5.destroy(); } bool XdgShellSurfaceUnstableV5::Private::isValid() const { return xdgsurfacev5.isValid(); } void XdgShellSurfaceUnstableV5::Private::setTransientFor(XdgShellSurface *parent) { xdg_surface *parentSurface = nullptr; if (parent) { parentSurface = *parent; } zxdg_surface_v5_set_parent(xdgsurfacev5, parentSurface); } void XdgShellSurfaceUnstableV5::Private::setTitle(const QString & title) { zxdg_surface_v5_set_title(xdgsurfacev5, title.toUtf8().constData()); } void XdgShellSurfaceUnstableV5::Private::setAppId(const QByteArray & appId) { zxdg_surface_v5_set_app_id(xdgsurfacev5, appId.constData()); } void XdgShellSurfaceUnstableV5::Private::showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) { zxdg_surface_v5_show_window_menu(xdgsurfacev5, *seat, serial, x, y); } void XdgShellSurfaceUnstableV5::Private::move(Seat *seat, quint32 serial) { zxdg_surface_v5_move(xdgsurfacev5, *seat, serial); } void XdgShellSurfaceUnstableV5::Private::resize(Seat *seat, quint32 serial, Qt::Edges edges) { uint wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_NONE; if (edges.testFlag(Qt::TopEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT; } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP; } } else if (edges.testFlag(Qt::BottomEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT; } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM; } } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT; } else if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) { wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT; } zxdg_surface_v5_resize(xdgsurfacev5, *seat, serial, wlEdge); } void XdgShellSurfaceUnstableV5::Private::ackConfigure(quint32 serial) { zxdg_surface_v5_ack_configure(xdgsurfacev5, serial); } void XdgShellSurfaceUnstableV5::Private::setMaximized() { zxdg_surface_v5_set_maximized(xdgsurfacev5); } void XdgShellSurfaceUnstableV5::Private::unsetMaximized() { zxdg_surface_v5_unset_maximized(xdgsurfacev5); } void XdgShellSurfaceUnstableV5::Private::setFullscreen(Output *output) { wl_output *o = nullptr; if (output) { o = *output; } zxdg_surface_v5_set_fullscreen(xdgsurfacev5, o); } void XdgShellSurfaceUnstableV5::Private::unsetFullscreen() { zxdg_surface_v5_unset_fullscreen(xdgsurfacev5); } void XdgShellSurfaceUnstableV5::Private::setMinimized() { zxdg_surface_v5_set_minimized(xdgsurfacev5); } void XdgShellSurfaceUnstableV5::Private::setMaxSize(const QSize &size) { Q_UNUSED(size) //TODO: notify an error? } void XdgShellSurfaceUnstableV5::Private::setMinSize(const QSize &size) { Q_UNUSED(size) //TODO: notify an error? } XdgShellSurfaceUnstableV5::XdgShellSurfaceUnstableV5(QObject *parent) : XdgShellSurface(new Private(this), parent) { } XdgShellSurfaceUnstableV5::~XdgShellSurfaceUnstableV5() = default; class XdgShellPopupUnstableV5::Private : public XdgShellPopup::Private { public: Private(XdgShellPopup *q); void setupV5(xdg_popup *p) override; void release() override; void destroy() override; bool isValid() const override; using XdgShellPopup::Private::operator xdg_surface*; using XdgShellPopup::Private::operator zxdg_popup_v6*; using XdgShellPopup::Private::operator zxdg_surface_v6*; operator xdg_popup*() override { return xdgpopupv5; } operator xdg_popup*() const override { return xdgpopupv5; } WaylandPointer xdgpopupv5; private: static void popupDoneCallback(void *data, xdg_popup *xdg_popup); static const struct zxdg_popup_v5_listener s_listener; }; const struct zxdg_popup_v5_listener XdgShellPopupUnstableV5::Private::s_listener = { popupDoneCallback }; void XdgShellPopupUnstableV5::Private::popupDoneCallback(void *data, xdg_popup *xdg_popup) { auto s = reinterpret_cast(data); Q_ASSERT(s->xdgpopupv5 == xdg_popup); emit s->q->popupDone(); } XdgShellPopupUnstableV5::Private::Private(XdgShellPopup *q) : XdgShellPopup::Private(q) { } void XdgShellPopupUnstableV5::Private::setupV5(xdg_popup *p) { Q_ASSERT(p); Q_ASSERT(!xdgpopupv5); xdgpopupv5.setup(p); zxdg_popup_v5_add_listener(xdgpopupv5, &s_listener, this); } void XdgShellPopupUnstableV5::Private::release() { xdgpopupv5.release(); } void XdgShellPopupUnstableV5::Private::destroy() { xdgpopupv5.destroy(); } bool XdgShellPopupUnstableV5::Private::isValid() const { return xdgpopupv5.isValid(); } XdgShellPopupUnstableV5::XdgShellPopupUnstableV5(QObject *parent) : XdgShellPopup(new Private(this), parent) { } XdgShellPopupUnstableV5::~XdgShellPopupUnstableV5() = default; } } diff --git a/src/client/xdgshell_v6.cpp b/src/client/xdgshell_v6.cpp index 3d3aae4..ff06c81 100644 --- a/src/client/xdgshell_v6.cpp +++ b/src/client/xdgshell_v6.cpp @@ -1,597 +1,583 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_p.h" #include "event_queue.h" #include "output.h" #include "seat.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class XdgShellUnstableV6::Private : public XdgShell::Private { public: void setupV6(zxdg_shell_v6 *shell) override; void release() override; void destroy() override; bool isValid() const override; XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) override; XdgShellPopup *getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) override; XdgShellPopup *getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) override; using XdgShell::Private::operator xdg_wm_base*; using XdgShell::Private::operator xdg_shell*; operator zxdg_shell_v6*() override { return xdgshellv6; } operator zxdg_shell_v6*() const override { return xdgshellv6; } private: XdgShellPopup *internalGetXdgPopup(Surface *surface, zxdg_surface_v6 *parentSurface, const XdgPositioner &positioner, QObject *parent); static void pingCallback(void *data, struct zxdg_shell_v6 *shell, uint32_t serial); WaylandPointer xdgshellv6; static const struct zxdg_shell_v6_listener s_shellListener; }; const struct zxdg_shell_v6_listener XdgShellUnstableV6::Private::s_shellListener = { pingCallback, }; void XdgShellUnstableV6::Private::pingCallback(void *data, struct zxdg_shell_v6 *shell, uint32_t serial) { Q_UNUSED(data) zxdg_shell_v6_pong(shell, serial); } void XdgShellUnstableV6::Private::setupV6(zxdg_shell_v6 *shell) { Q_ASSERT(shell); Q_ASSERT(!xdgshellv6); xdgshellv6.setup(shell); zxdg_shell_v6_add_listener(shell, &s_shellListener, this); } void XdgShellUnstableV6::Private::release() { xdgshellv6.release(); } void XdgShellUnstableV6::Private::destroy() { xdgshellv6.destroy(); } bool XdgShellUnstableV6::Private::isValid() const { return xdgshellv6.isValid(); } XdgShellSurface *XdgShellUnstableV6::Private::getXdgSurface(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto ss = zxdg_shell_v6_get_xdg_surface(xdgshellv6, *surface); if (!ss) { return nullptr; } auto s = new XdgTopLevelUnstableV6(parent); auto toplevel = zxdg_surface_v6_get_toplevel(ss); if (queue) { queue->addProxy(ss); queue->addProxy(toplevel); } s->setup(ss, toplevel); return s; } XdgShellPopup *XdgShellUnstableV6::Private::getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) { return internalGetXdgPopup(surface, *parentSurface, positioner, parent); } XdgShellPopup *XdgShellUnstableV6::Private::getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) { return internalGetXdgPopup(surface, *parentSurface, positioner, parent); } XdgShellPopup *XdgShellUnstableV6::Private::internalGetXdgPopup(Surface *surface, zxdg_surface_v6 *parentSurface, const XdgPositioner &positioner, QObject *parent) { Q_ASSERT(isValid()); auto ss = zxdg_shell_v6_get_xdg_surface(xdgshellv6, *surface); if (!ss) { return nullptr; } auto p = zxdg_shell_v6_create_positioner(xdgshellv6); auto anchorRect = positioner.anchorRect(); zxdg_positioner_v6_set_anchor_rect(p, anchorRect.x(), anchorRect.y(), anchorRect.width(), anchorRect.height()); QSize initialSize = positioner.initialSize(); zxdg_positioner_v6_set_size(p, initialSize.width(), initialSize.height()); QPoint anchorOffset = positioner.anchorOffset(); if (!anchorOffset.isNull()) { zxdg_positioner_v6_set_offset(p, anchorOffset.x(), anchorOffset.y()); } uint32_t anchor = 0; if (positioner.anchorEdge().testFlag(Qt::LeftEdge)) { anchor |= ZXDG_POSITIONER_V6_ANCHOR_LEFT; } if (positioner.anchorEdge().testFlag(Qt::TopEdge)) { anchor |= ZXDG_POSITIONER_V6_ANCHOR_TOP; } if (positioner.anchorEdge().testFlag(Qt::RightEdge)) { anchor |= ZXDG_POSITIONER_V6_ANCHOR_RIGHT; } if (positioner.anchorEdge().testFlag(Qt::BottomEdge)) { anchor |= ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; } if (anchor != 0) { zxdg_positioner_v6_set_anchor(p, anchor); } uint32_t gravity = 0; if (positioner.gravity().testFlag(Qt::LeftEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_LEFT; } if (positioner.gravity().testFlag(Qt::TopEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_TOP; } if (positioner.gravity().testFlag(Qt::RightEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT; } if (positioner.gravity().testFlag(Qt::BottomEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; } if (gravity != 0) { zxdg_positioner_v6_set_gravity(p, gravity); } uint32_t constraint = 0; if (positioner.constraints().testFlag(XdgPositioner::Constraint::SlideX)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::SlideY)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::FlipX)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::FlipY)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeX)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeY)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; } if (constraint != 0) { zxdg_positioner_v6_set_constraint_adjustment(p, constraint); } XdgShellPopup *s = new XdgShellPopupUnstableV6(parent); auto popup = zxdg_surface_v6_get_popup(ss, parentSurface, p); if (queue) { //deliberately not adding the positioner because the positioner has no events sent to it queue->addProxy(ss); queue->addProxy(popup); } s->setup(ss, popup); zxdg_positioner_v6_destroy(p); return s; } XdgShellUnstableV6::XdgShellUnstableV6(QObject *parent) : XdgShell(new Private, parent) { } XdgShellUnstableV6::~XdgShellUnstableV6() = default; //A top level wraps both xdg_surface_v6 and xdg_top_level into the public API XdgShelllSurface class XdgTopLevelUnstableV6::Private : public XdgShellSurface::Private { public: Private(XdgShellSurface *q); WaylandPointer xdgtoplevelv6; WaylandPointer xdgsurfacev6; void setupV6(zxdg_surface_v6 *surface, zxdg_toplevel_v6 *toplevel) override; void release() override; void destroy() override; bool isValid() const override; using XdgShellSurface::Private::operator xdg_surface*; using XdgShellSurface::Private::operator xdg_toplevel*; operator zxdg_surface_v6*() override { return xdgsurfacev6; } operator zxdg_surface_v6*() const override { return xdgsurfacev6; } operator zxdg_toplevel_v6*() override { return xdgtoplevelv6; } operator zxdg_toplevel_v6*() const override { return xdgtoplevelv6; } void setTransientFor(XdgShellSurface *parent) override; void setTitle(const QString &title) override; void setAppId(const QByteArray &appId) override; void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) override; void move(Seat *seat, quint32 serial) override; void resize(Seat *seat, quint32 serial, Qt::Edges edges) override; void ackConfigure(quint32 serial) override; void setMaximized() override; void unsetMaximized() override; void setFullscreen(Output *output) override; void unsetFullscreen() override; void setMinimized() override; void setMaxSize(const QSize &size) override; void setMinSize(const QSize &size) override; private: QSize pendingSize; States pendingState; static void configureCallback(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state); static void closeCallback(void *data, zxdg_toplevel_v6 *xdg_toplevel); static void surfaceConfigureCallback(void *data, zxdg_surface_v6 *xdg_surface, uint32_t serial); static const struct zxdg_toplevel_v6_listener s_toplevelListener; static const struct zxdg_surface_v6_listener s_surfaceListener; }; const struct zxdg_toplevel_v6_listener XdgTopLevelUnstableV6::Private::s_toplevelListener = { configureCallback, closeCallback }; const struct zxdg_surface_v6_listener XdgTopLevelUnstableV6::Private::s_surfaceListener = { surfaceConfigureCallback }; void XdgTopLevelUnstableV6::Private::surfaceConfigureCallback(void *data, struct zxdg_surface_v6 *surface, uint32_t serial) { Q_UNUSED(surface) auto s = reinterpret_cast(data); s->q->configureRequested(s->pendingSize, s->pendingState, serial); if (!s->pendingSize.isNull()) { s->q->setSize(s->pendingSize); s->pendingSize = QSize(); } s->pendingState = {}; } void XdgTopLevelUnstableV6::Private::configureCallback(void *data, struct zxdg_toplevel_v6 *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state) { Q_UNUSED(xdg_toplevel) auto s = reinterpret_cast(data); States states; uint32_t *statePtr = reinterpret_cast(state->data); for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) { switch (statePtr[i]) { case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: states = states | XdgShellSurface::State::Maximized; break; case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: states = states | XdgShellSurface::State::Fullscreen; break; case ZXDG_TOPLEVEL_V6_STATE_RESIZING: states = states | XdgShellSurface::State::Resizing; break; case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: states = states | XdgShellSurface::State::Activated; break; } } s->pendingSize = QSize(width, height); s->pendingState = states; } void XdgTopLevelUnstableV6::Private::closeCallback(void *data, zxdg_toplevel_v6 *xdg_toplevel) { auto s = reinterpret_cast(data); Q_ASSERT(s->xdgtoplevelv6 == xdg_toplevel); emit s->q->closeRequested(); } XdgTopLevelUnstableV6::Private::Private(XdgShellSurface *q) : XdgShellSurface::Private(q) { } void XdgTopLevelUnstableV6::Private::setupV6(zxdg_surface_v6 *surface, zxdg_toplevel_v6 *topLevel) { Q_ASSERT(surface); Q_ASSERT(!xdgtoplevelv6); xdgsurfacev6.setup(surface); xdgtoplevelv6.setup(topLevel); zxdg_surface_v6_add_listener(xdgsurfacev6, &s_surfaceListener, this); zxdg_toplevel_v6_add_listener(xdgtoplevelv6, &s_toplevelListener, this); } void XdgTopLevelUnstableV6::Private::release() { xdgtoplevelv6.release(); xdgsurfacev6.release(); } void XdgTopLevelUnstableV6::Private::destroy() { xdgtoplevelv6.destroy(); xdgsurfacev6.destroy(); } bool XdgTopLevelUnstableV6::Private::isValid() const { return xdgtoplevelv6.isValid() && xdgsurfacev6.isValid(); } void XdgTopLevelUnstableV6::Private::setTransientFor(XdgShellSurface *parent) { zxdg_toplevel_v6 *parentSurface = nullptr; if (parent) { parentSurface = *parent; } zxdg_toplevel_v6_set_parent(xdgtoplevelv6, parentSurface); } void XdgTopLevelUnstableV6::Private::setTitle(const QString & title) { zxdg_toplevel_v6_set_title(xdgtoplevelv6, title.toUtf8().constData()); } void XdgTopLevelUnstableV6::Private::setAppId(const QByteArray & appId) { zxdg_toplevel_v6_set_app_id(xdgtoplevelv6, appId.constData()); } void XdgTopLevelUnstableV6::Private::showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) { zxdg_toplevel_v6_show_window_menu(xdgtoplevelv6, *seat, serial, x, y); } void XdgTopLevelUnstableV6::Private::move(Seat *seat, quint32 serial) { zxdg_toplevel_v6_move(xdgtoplevelv6, *seat, serial); } void XdgTopLevelUnstableV6::Private::resize(Seat *seat, quint32 serial, Qt::Edges edges) { uint wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE; if (edges.testFlag(Qt::TopEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; } } else if (edges.testFlag(Qt::BottomEdge)) { if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; } } else if (edges.testFlag(Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; } else if (edges.testFlag(Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) { wlEdge = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; } zxdg_toplevel_v6_resize(xdgtoplevelv6, *seat, serial, wlEdge); } void XdgTopLevelUnstableV6::Private::ackConfigure(quint32 serial) { zxdg_surface_v6_ack_configure(xdgsurfacev6, serial); } void XdgTopLevelUnstableV6::Private::setMaximized() { zxdg_toplevel_v6_set_maximized(xdgtoplevelv6); } void XdgTopLevelUnstableV6::Private::unsetMaximized() { zxdg_toplevel_v6_unset_maximized(xdgtoplevelv6); } void XdgTopLevelUnstableV6::Private::setFullscreen(Output *output) { wl_output *o = nullptr; if (output) { o = *output; } zxdg_toplevel_v6_set_fullscreen(xdgtoplevelv6, o); } void XdgTopLevelUnstableV6::Private::unsetFullscreen() { zxdg_toplevel_v6_unset_fullscreen(xdgtoplevelv6); } void XdgTopLevelUnstableV6::Private::setMinimized() { zxdg_toplevel_v6_set_minimized(xdgtoplevelv6); } void XdgTopLevelUnstableV6::Private::setMaxSize(const QSize &size) { zxdg_toplevel_v6_set_max_size(xdgtoplevelv6, size.width(), size.height()); } void XdgTopLevelUnstableV6::Private::setMinSize(const QSize &size) { zxdg_toplevel_v6_set_min_size(xdgtoplevelv6, size.width(), size.height()); } XdgTopLevelUnstableV6::XdgTopLevelUnstableV6(QObject *parent) : XdgShellSurface(new Private(this), parent) { } XdgTopLevelUnstableV6::~XdgTopLevelUnstableV6() = default; class XdgShellPopupUnstableV6::Private : public XdgShellPopup::Private { public: Private(XdgShellPopup *q); void setupV6(zxdg_surface_v6 *s, zxdg_popup_v6 *p) override; void release() override; void destroy() override; bool isValid() const override; void requestGrab(Seat *seat, quint32 serial) override; void ackConfigure(quint32 serial) override; using XdgShellPopup::Private::operator xdg_popup*; using XdgShellPopup::Private::operator xdg_surface*; operator zxdg_surface_v6*() override { return xdgsurfacev6; } operator zxdg_surface_v6*() const override { return xdgsurfacev6; } operator zxdg_popup_v6*() override { return xdgpopupv6; } operator zxdg_popup_v6*() const override { return xdgpopupv6; } WaylandPointer xdgsurfacev6; WaylandPointer xdgpopupv6; QRect pendingRect; private: static void configureCallback(void *data, zxdg_popup_v6 *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height); static void popupDoneCallback(void *data, zxdg_popup_v6 *xdg_popup); static void surfaceConfigureCallback(void *data, zxdg_surface_v6 *xdg_surface, uint32_t serial); static const struct zxdg_popup_v6_listener s_popupListener; static const struct zxdg_surface_v6_listener s_surfaceListener; }; const struct zxdg_popup_v6_listener XdgShellPopupUnstableV6::Private::s_popupListener = { configureCallback, popupDoneCallback }; const struct zxdg_surface_v6_listener XdgShellPopupUnstableV6::Private::s_surfaceListener = { surfaceConfigureCallback, }; void XdgShellPopupUnstableV6::Private::configureCallback(void *data, zxdg_popup_v6 *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(xdg_popup); auto s = reinterpret_cast(data); s->pendingRect = QRect(x, y, width, height); } void XdgShellPopupUnstableV6::Private::surfaceConfigureCallback(void *data, struct zxdg_surface_v6 *surface, uint32_t serial) { Q_UNUSED(surface); auto s = reinterpret_cast(data); s->q->configureRequested(s->pendingRect, serial); s->pendingRect = QRect(); } void XdgShellPopupUnstableV6::Private::popupDoneCallback(void *data, zxdg_popup_v6 *xdg_popup) { auto s = reinterpret_cast(data); Q_ASSERT(s->xdgpopupv6 == xdg_popup); emit s->q->popupDone(); } XdgShellPopupUnstableV6::Private::Private(XdgShellPopup *q) : XdgShellPopup::Private(q) { } void XdgShellPopupUnstableV6::Private::setupV6(zxdg_surface_v6 *s, zxdg_popup_v6 *p) { Q_ASSERT(p); Q_ASSERT(!xdgsurfacev6); Q_ASSERT(!xdgpopupv6); xdgsurfacev6.setup(s); xdgpopupv6.setup(p); zxdg_surface_v6_add_listener(xdgsurfacev6, &s_surfaceListener, this); zxdg_popup_v6_add_listener(xdgpopupv6, &s_popupListener, this); } void XdgShellPopupUnstableV6::Private::release() { xdgpopupv6.release(); } void XdgShellPopupUnstableV6::Private::destroy() { xdgpopupv6.destroy(); } bool XdgShellPopupUnstableV6::Private::isValid() const { return xdgpopupv6.isValid(); } void XdgShellPopupUnstableV6::Private::requestGrab(Seat *seat, quint32 serial) { zxdg_popup_v6_grab(xdgpopupv6, *seat, serial); } void XdgShellPopupUnstableV6::Private::ackConfigure(quint32 serial) { zxdg_surface_v6_ack_configure(xdgsurfacev6, serial); } XdgShellPopupUnstableV6::XdgShellPopupUnstableV6(QObject *parent) : XdgShellPopup(new Private(this), parent) { } XdgShellPopupUnstableV6::~XdgShellPopupUnstableV6() = default; } } diff --git a/src/compat/wayland-xdg-shell-v5-client-protocol.h b/src/compat/wayland-xdg-shell-v5-client-protocol.h index 1316847..e3e1492 100644 --- a/src/compat/wayland-xdg-shell-v5-client-protocol.h +++ b/src/compat/wayland-xdg-shell-v5-client-protocol.h @@ -1,1114 +1,1097 @@ /* * This file is *NOT* autogenerated. * It contains XDGShellV5 with method names modified to be not clashing with XDG stable * Strings inside the sent protocol remain the same */ #ifndef ZXDG_SHELL_V5_UNSTABLE_V5_CLIENT_PROTOCOL_H #define ZXDG_SHELL_V5_UNSTABLE_V5_CLIENT_PROTOCOL_H #include #include #include "wayland-client.h" #ifdef __cplusplus extern "C" { #endif /** * @page page_zxdg_shell_v5_unstable_v5 The zxdg_shell_v5_unstable_v5 protocol * @section page_ifaces_zxdg_shell_v5_unstable_v5 Interfaces * - @subpage page_iface_xdg_shell - create desktop-style surfaces * - @subpage page_iface_xdg_surface - A desktop window * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus * @section page_copyright_zxdg_shell_v5_unstable_v5 Copyright *
  *
- * Copyright © 2008-2013 Kristian Høgsberg
- * Copyright © 2013      Rafael Antognolli
- * Copyright © 2013      Jasper St. Pierre
- * Copyright © 2010-2013 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-FileCopyrightText: 2008-2013 Kristian Høgsberg
+ * SPDX-FileCopyrightText: 2013 Rafael Antognolli
+ * SPDX-FileCopyrightText: 2013 Jasper St. Pierre
+ * SPDX-FileCopyrightText: 2010-2013 Intel Corporation
+ *
+ * SPDX-License-Identifier: MIT
  * 
*/ struct wl_output; struct wl_seat; struct wl_surface; struct xdg_surface; struct xdg_popup; struct xdg_shell; /** * @page page_iface_xdg_shell zxdg_shell_v5 * @section page_iface_zxdg_shell_v5_desc Description * * xdg_shell allows clients to turn a wl_surface into a "real window" * which can be dragged, resized, stacked, and moved around by the * user. Everything about this interface is suited towards traditional * desktop environments. * @section page_iface_zxdg_shell_v5_api API * See @ref iface_xdg_shell. */ /** * @defgroup iface_xdg_shell The xdg_shell interface * * xdg_shell allows clients to turn a wl_surface into a "real window" * which can be dragged, resized, stacked, and moved around by the * user. Everything about this interface is suited towards traditional * desktop environments. */ extern const struct wl_interface zxdg_shell_v5_interface; /** * @page page_iface_xdg_surface zxdg_surface_v5 * @section page_iface_zxdg_surface_v5_desc Description * * An interface that may be implemented by a wl_surface, for * implementations that provide a desktop-style user interface. * * It provides requests to treat surfaces like windows, allowing to set * properties like maximized, fullscreen, minimized, and to move and resize * them, and associate metadata like title and app id. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_surface state to take effect. Prior to committing the new * state, it can set up initial configuration, such as maximizing or setting * a window geometry. * * Even without attaching a buffer the compositor must respond to initial * committed configuration, for instance sending a configure event with * expected window geometry if the client maximized its surface during * initialization. * * For a surface to be mapped by the compositor the client must have * committed both an xdg_surface state and a buffer. * @section page_iface_zxdg_surface_v5_api API * See @ref iface_xdg_surface. */ /** * @defgroup iface_xdg_surface The xdg_surface interface * * An interface that may be implemented by a wl_surface, for * implementations that provide a desktop-style user interface. * * It provides requests to treat surfaces like windows, allowing to set * properties like maximized, fullscreen, minimized, and to move and resize * them, and associate metadata like title and app id. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_surface state to take effect. Prior to committing the new * state, it can set up initial configuration, such as maximizing or setting * a window geometry. * * Even without attaching a buffer the compositor must respond to initial * committed configuration, for instance sending a configure event with * expected window geometry if the client maximized its surface during * initialization. * * For a surface to be mapped by the compositor the client must have * committed both an xdg_surface state and a buffer. */ extern const struct wl_interface zxdg_surface_v5_interface; /** * @page page_iface_xdg_popup zxdg_popup_v5 * @section page_iface_zxdg_popup_v5_desc Description * * A popup surface is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * * When the popup is dismissed, a popup_done event will be sent out, * and at the same time the surface will be unmapped. The zxdg_popup_v5 * object is now inert and cannot be reactivated, so clients should * destroy it. Explicitly destroying the xdg_popup object will also * dismiss the popup and unmap the surface. * * Clients will receive events for all their surfaces during this * grab (which is an "owner-events" grab in X11 parlance). This is * done so that users can navigate through submenus and other * "nested" popup windows without having to dismiss the topmost * popup. * * Clients that want to dismiss the popup when another surface of * their own is clicked should dismiss the popup using the destroy * request. * * The parent surface must have either an xdg_surface or zxdg_popup_v5 * role. * * Specifying an xdg_popup for the parent means that the popups are * nested, with this popup now being the topmost popup. Nested * popups must be destroyed in the reverse order they were created * in, e.g. the only popup you are allowed to destroy at all times * is the topmost one. * * If there is an existing popup when creating a new popup, the * parent must be the current topmost popup. * * A parent surface must be mapped before the new popup is mapped. * * When compositors choose to dismiss a popup, they will likely * dismiss every nested popup as well. When a compositor dismisses * popups, it will follow the same dismissing order as required * from the client. * * The x and y arguments passed when creating the popup object specify * where the top left of the popup should be placed, relative to the * local surface coordinates of the parent surface. See * xdg_shell.get_xdg_popup. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * * For a surface to be mapped by the compositor the client must have * committed both the xdg_popup state and a buffer. * @section page_iface_zxdg_popup_v5_api API * See @ref iface_xdg_popup. */ /** * @defgroup iface_xdg_popup The xdg_popup interface * * A popup surface is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * * When the popup is dismissed, a popup_done event will be sent out, * and at the same time the surface will be unmapped. The zxdg_popup_v5 * object is now inert and cannot be reactivated, so clients should * destroy it. Explicitly destroying the xdg_popup object will also * dismiss the popup and unmap the surface. * * Clients will receive events for all their surfaces during this * grab (which is an "owner-events" grab in X11 parlance). This is * done so that users can navigate through submenus and other * "nested" popup windows without having to dismiss the topmost * popup. * * Clients that want to dismiss the popup when another surface of * their own is clicked should dismiss the popup using the destroy * request. * * The parent surface must have either an xdg_surface or zxdg_popup_v5 * role. * * Specifying an xdg_popup for the parent means that the popups are * nested, with this popup now being the topmost popup. Nested * popups must be destroyed in the reverse order they were created * in, e.g. the only popup you are allowed to destroy at all times * is the topmost one. * * If there is an existing popup when creating a new popup, the * parent must be the current topmost popup. * * A parent surface must be mapped before the new popup is mapped. * * When compositors choose to dismiss a popup, they will likely * dismiss every nested popup as well. When a compositor dismisses * popups, it will follow the same dismissing order as required * from the client. * * The x and y arguments passed when creating the popup object specify * where the top left of the popup should be placed, relative to the * local surface coordinates of the parent surface. See * xdg_shell.get_xdg_popup. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * * For a surface to be mapped by the compositor the client must have * committed both the xdg_popup state and a buffer. */ extern const struct wl_interface zxdg_popup_v5_interface; #ifndef ZXDG_SHELL_V5_VERSION_ENUM #define ZXDG_SHELL_V5_VERSION_ENUM /** * @ingroup iface_zxdg_shell_v5 * latest protocol version * * The 'current' member of this enum gives the version of the * protocol. Implementations can compare this to the version * they implement using static_assert to ensure the protocol and * implementation versions match. */ enum zxdg_shell_v5_version { /** * Always the latest version */ ZXDG_SHELL_V5_VERSION_CURRENT = 5, }; #endif /* ZXDG_SHELL_V5_VERSION_ENUM */ #ifndef ZXDG_SHELL_V5_ERROR_ENUM #define ZXDG_SHELL_V5_ERROR_ENUM enum zxdg_shell_v5_error { /** * given wl_surface has another role */ ZXDG_SHELL_V5_ERROR_ROLE = 0, /** * xdg_shell was destroyed before children */ ZXDG_SHELL_V5_ERROR_DEFUNCT_SURFACES = 1, /** * the client tried to map or destroy a non-topmost popup */ ZXDG_SHELL_V5_ERROR_NOT_THE_TOPMOST_POPUP = 2, /** * the client specified an invalid popup parent surface */ ZXDG_SHELL_V5_ERROR_INVALID_POPUP_PARENT = 3, }; #endif /* ZXDG_SHELL_V5_ERROR_ENUM */ /** * @ingroup iface_zxdg_shell_v5 * @struct zxdg_shell_v5_listener */ struct zxdg_shell_v5_listener { /** * check if the client is alive * * The ping event asks the client if it's still alive. Pass the * serial specified in the event back to the compositor by sending * a "pong" request back with the specified serial. * * Compositors can use this to determine if the client is still * alive. It's unspecified what will happen if the client doesn't * respond to the ping request, or in what timeframe. Clients * should try to respond in a reasonable amount of time. * * A compositor is free to ping in any way it wants, but a client * must always respond to any xdg_shell object it created. * @param serial pass this to the pong request */ void (*ping)(void *data, struct xdg_shell *xdg_shell, uint32_t serial); }; /** * @ingroup iface_zxdg_shell_v5 */ static inline int zxdg_shell_v5_add_listener(struct xdg_shell *xdg_shell, const struct zxdg_shell_v5_listener *listener, void *data) { return wl_proxy_add_listener((struct wl_proxy *) xdg_shell, (void (**)(void)) listener, data); } #define ZXDG_SHELL_V5_DESTROY 0 #define ZXDG_SHELL_V5_USE_UNSTABLE_VERSION 1 #define ZXDG_SHELL_V5_GET_ZXDG_SURFACE_V5 2 #define ZXDG_SHELL_V5_GET_ZXDG_POPUP_V5 3 #define ZXDG_SHELL_V5_PONG 4 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_PING_SINCE_VERSION 1 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_USE_UNSTABLE_VERSION_SINCE_VERSION 1 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_GET_ZXDG_SURFACE_V5_SINCE_VERSION 1 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_GET_ZXDG_POPUP_V5_SINCE_VERSION 1 /** * @ingroup iface_zxdg_shell_v5 */ #define ZXDG_SHELL_V5_PONG_SINCE_VERSION 1 /** @ingroup iface_xdg_shell */ static inline void zxdg_shell_v5_set_user_data(struct xdg_shell *xdg_shell, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) xdg_shell, user_data); } /** @ingroup iface_xdg_shell */ static inline void * zxdg_shell_v5_get_user_data(struct xdg_shell *xdg_shell) { return wl_proxy_get_user_data((struct wl_proxy *) xdg_shell); } static inline uint32_t zxdg_shell_v5_get_version(struct xdg_shell *xdg_shell) { return wl_proxy_get_version((struct wl_proxy *) xdg_shell); } /** * @ingroup iface_zxdg_shell_v5 * * Destroy this xdg_shell object. * * Destroying a bound xdg_shell object while there are surfaces * still alive created by this xdg_shell object instance is illegal * and will result in a protocol error. */ static inline void zxdg_shell_v5_destroy(struct xdg_shell *xdg_shell) { wl_proxy_marshal((struct wl_proxy *) xdg_shell, ZXDG_SHELL_V5_DESTROY); wl_proxy_destroy((struct wl_proxy *) xdg_shell); } /** * @ingroup iface_zxdg_shell_v5 * * Negotiate the unstable version of the interface. This * mechanism is in place to ensure client and server agree on the * unstable versions of the protocol that they speak or exit * cleanly if they don't agree. This request will go away once * the xdg-shell protocol is stable. */ static inline void zxdg_shell_v5_use_unstable_version(struct xdg_shell *xdg_shell, int32_t version) { wl_proxy_marshal((struct wl_proxy *) xdg_shell, ZXDG_SHELL_V5_USE_UNSTABLE_VERSION, version); } /** * @ingroup iface_zxdg_shell_v5 * * This creates an xdg_surface for the given surface and gives it the * xdg_surface role. A wl_surface can only be given an xdg_surface role * once. If get_xdg_surface is called with a wl_surface that already has * an active xdg_surface associated with it, or if it had any other role, * an error is raised. * * See the documentation of xdg_surface for more details about what an * xdg_surface is and how it is used. */ static inline struct xdg_surface * zxdg_shell_v5_get_xdg_surface(struct xdg_shell *xdg_shell, struct wl_surface *surface) { struct wl_proxy *id; id = wl_proxy_marshal_constructor((struct wl_proxy *) xdg_shell, ZXDG_SHELL_V5_GET_ZXDG_SURFACE_V5, &zxdg_surface_v5_interface, NULL, surface); return (struct xdg_surface *) id; } /** * @ingroup iface_zxdg_shell_v5 * * This creates an xdg_popup for the given surface and gives it the * xdg_popup role. A wl_surface can only be given an xdg_popup role * once. If get_xdg_popup is called with a wl_surface that already has * an active xdg_popup associated with it, or if it had any other role, * an error is raised. * * This request must be used in response to some sort of user action * like a button press, key press, or touch down event. * * See the documentation of xdg_popup for more details about what an * xdg_popup is and how it is used. */ static inline struct xdg_popup * zxdg_shell_v5_get_xdg_popup(struct xdg_shell *xdg_shell, struct wl_surface *surface, struct wl_surface *parent, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) { struct wl_proxy *id; id = wl_proxy_marshal_constructor((struct wl_proxy *) xdg_shell, ZXDG_SHELL_V5_GET_ZXDG_POPUP_V5, &zxdg_popup_v5_interface, NULL, surface, parent, seat, serial, x, y); return (struct xdg_popup *) id; } /** * @ingroup iface_zxdg_shell_v5 * * A client must respond to a ping event with a pong request or * the client may be deemed unresponsive. */ static inline void zxdg_shell_v5_pong(struct xdg_shell *xdg_shell, uint32_t serial) { wl_proxy_marshal((struct wl_proxy *) xdg_shell, ZXDG_SHELL_V5_PONG, serial); } #ifndef ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM #define ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM /** * @ingroup iface_zxdg_surface_v5 * edge values for resizing * * These values are used to indicate which edge of a surface * is being dragged in a resize operation. */ enum zxdg_surface_v5_resize_edge { ZXDG_SURFACE_V5_RESIZE_EDGE_NONE = 0, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP = 1, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM = 2, ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT = 4, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT = 5, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT = 6, ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT = 8, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT = 9, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT = 10, }; #endif /* ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM */ #ifndef ZXDG_SURFACE_V5_STATE_ENUM #define ZXDG_SURFACE_V5_STATE_ENUM /** * @ingroup iface_zxdg_surface_v5 * the surface is now activated * * Client window decorations should be painted as if the window is * active. Do not assume this means that the window actually has * keyboard or pointer focus. */ enum zxdg_surface_v5_state { /** * the surface is maximized */ ZXDG_SURFACE_V5_STATE_MAXIMIZED = 1, /** * the surface is fullscreen */ ZXDG_SURFACE_V5_STATE_FULLSCREEN = 2, /** * the surface is being resized */ ZXDG_SURFACE_V5_STATE_RESIZING = 3, /** * the surface is now activated */ ZXDG_SURFACE_V5_STATE_ACTIVATED = 4, }; #endif /* ZXDG_SURFACE_V5_STATE_ENUM */ /** * @ingroup iface_zxdg_surface_v5 * @struct zxdg_surface_v5_listener */ struct zxdg_surface_v5_listener { /** * suggest a surface change * * The configure event asks the client to resize its surface or * to change its state. * * The width and height arguments specify a hint to the window * about how its surface should be resized in window geometry * coordinates. See set_window_geometry. * * If the width or height arguments are zero, it means the client * should decide its own window dimension. This may happen when the * compositor need to configure the state of the surface but * doesn't have any information about any previous or expected * dimension. * * The states listed in the event specify how the width/height * arguments should be interpreted, and possibly how it should be * drawn. * * Clients should arrange their surface for the new size and * states, and then send a ack_configure request with the serial * sent in this configure event at some point before committing the * new surface. * * If the client receives multiple configure events before it can * respond to one, it is free to discard all but the last event it * received. */ void (*configure)(void *data, struct xdg_surface *xdg_surface, int32_t width, int32_t height, struct wl_array *states, uint32_t serial); /** * surface wants to be closed * * The close event is sent by the compositor when the user wants * the surface to be closed. This should be equivalent to the user * clicking the close button in client-side decorations, if your * application has any... * * This is only a request that the user intends to close your * window. The client may choose to ignore this request, or show a * dialog to ask the user to save their data... */ void (*close)(void *data, struct xdg_surface *xdg_surface); }; /** * @ingroup iface_zxdg_surface_v5 */ static inline int zxdg_surface_v5_add_listener(struct xdg_surface *xdg_surface, const struct zxdg_surface_v5_listener *listener, void *data) { return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, (void (**)(void)) listener, data); } #define ZXDG_SURFACE_V5_DESTROY 0 #define ZXDG_SURFACE_V5_SET_PARENT 1 #define ZXDG_SURFACE_V5_SET_TITLE 2 #define ZXDG_SURFACE_V5_SET_APP_ID 3 #define ZXDG_SURFACE_V5_SHOW_WINDOW_MENU 4 #define ZXDG_SURFACE_V5_MOVE 5 #define ZXDG_SURFACE_V5_RESIZE 6 #define ZXDG_SURFACE_V5_ACK_CONFIGURE 7 #define ZXDG_SURFACE_V5_SET_WINDOW_GEOMETRY 8 #define ZXDG_SURFACE_V5_SET_MAXIMIZED 9 #define ZXDG_SURFACE_V5_UNSET_MAXIMIZED 10 #define ZXDG_SURFACE_V5_SET_FULLSCREEN 11 #define ZXDG_SURFACE_V5_UNSET_FULLSCREEN 12 #define ZXDG_SURFACE_V5_SET_MINIMIZED 13 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_CONFIGURE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_CLOSE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_PARENT_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_TITLE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_APP_ID_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SHOW_WINDOW_MENU_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_MOVE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_RESIZE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_ACK_CONFIGURE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_MAXIMIZED_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_UNSET_MAXIMIZED_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_FULLSCREEN_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_UNSET_FULLSCREEN_SINCE_VERSION 1 /** * @ingroup iface_zxdg_surface_v5 */ #define ZXDG_SURFACE_V5_SET_MINIMIZED_SINCE_VERSION 1 /** @ingroup iface_xdg_surface */ static inline void zxdg_surface_v5_set_user_data(struct xdg_surface *xdg_surface, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); } /** @ingroup iface_xdg_surface */ static inline void * zxdg_surface_v5_get_user_data(struct xdg_surface *xdg_surface) { return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); } static inline uint32_t zxdg_surface_v5_get_version(struct xdg_surface *xdg_surface) { return wl_proxy_get_version((struct wl_proxy *) xdg_surface); } /** * @ingroup iface_zxdg_surface_v5 * * Unmap and destroy the window. The window will be effectively * hidden from the user's point of view, and all state like * maximization, fullscreen, and so on, will be lost. */ static inline void zxdg_surface_v5_destroy(struct xdg_surface *xdg_surface) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_DESTROY); wl_proxy_destroy((struct wl_proxy *) xdg_surface); } /** * @ingroup iface_zxdg_surface_v5 * * Set the "parent" of this surface. This window should be stacked * above a parent. The parent surface must be mapped as long as this * surface is mapped. * * Parent windows should be set on dialogs, toolboxes, or other * "auxiliary" surfaces, so that the parent is raised when the dialog * is raised. */ static inline void zxdg_surface_v5_set_parent(struct xdg_surface *xdg_surface, struct xdg_surface *parent) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_PARENT, parent); } /** * @ingroup iface_zxdg_surface_v5 * * Set a short title for the surface. * * This string may be used to identify the surface in a task bar, * window list, or other user interface elements provided by the * compositor. * * The string must be encoded in UTF-8. */ static inline void zxdg_surface_v5_set_title(struct xdg_surface *xdg_surface, const char *title) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_TITLE, title); } /** * @ingroup iface_zxdg_surface_v5 * * Set an application identifier for the surface. * * The app ID identifies the general class of applications to which * the surface belongs. The compositor can use this to group multiple * surfaces together, or to determine how to launch a new application. * * For D-Bus activatable applications, the app ID is used as the D-Bus * service name. * * The compositor shell will try to group application surfaces together * by their app ID. As a best practice, it is suggested to select app * ID's that match the basename of the application's .desktop file. * For example, "org.freedesktop.FooViewer" where the .desktop file is * "org.freedesktop.FooViewer.desktop". * * See the desktop-entry specification [0] for more details on * application identifiers and how they relate to well-known D-Bus * names and .desktop files. * * [0] http://standards.freedesktop.org/desktop-entry-spec/ */ static inline void zxdg_surface_v5_set_app_id(struct xdg_surface *xdg_surface, const char *app_id) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_APP_ID, app_id); } /** * @ingroup iface_zxdg_surface_v5 * * Clients implementing client-side decorations might want to show * a context menu when right-clicking on the decorations, giving the * user a menu that they can use to maximize or minimize the window. * * This request asks the compositor to pop up such a window menu at * the given position, relative to the local surface coordinates of * the parent surface. There are no guarantees as to what menu items * the window menu contains. * * This request must be used in response to some sort of user action * like a button press, key press, or touch down event. */ static inline void zxdg_surface_v5_show_window_menu(struct xdg_surface *xdg_surface, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SHOW_WINDOW_MENU, seat, serial, x, y); } /** * @ingroup iface_zxdg_surface_v5 * * Start an interactive, user-driven move of the surface. * * This request must be used in response to some sort of user action * like a button press, key press, or touch down event. The passed * serial is used to determine the type of interactive move (touch, * pointer, etc). * * The server may ignore move requests depending on the state of * the surface (e.g. fullscreen or maximized), or if the passed serial * is no longer valid. * * If triggered, the surface will lose the focus of the device * (wl_pointer, wl_touch, etc) used for the move. It is up to the * compositor to visually indicate that the move is taking place, such as * updating a pointer cursor, during the move. There is no guarantee * that the device focus will return when the move is completed. */ static inline void zxdg_surface_v5_move(struct xdg_surface *xdg_surface, struct wl_seat *seat, uint32_t serial) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_MOVE, seat, serial); } /** * @ingroup iface_zxdg_surface_v5 * * Start a user-driven, interactive resize of the surface. * * This request must be used in response to some sort of user action * like a button press, key press, or touch down event. The passed * serial is used to determine the type of interactive resize (touch, * pointer, etc). * * The server may ignore resize requests depending on the state of * the surface (e.g. fullscreen or maximized). * * If triggered, the client will receive configure events with the * "resize" state enum value and the expected sizes. See the "resize" * enum value for more details about what is required. The client * must also acknowledge configure events using "ack_configure". After * the resize is completed, the client will receive another "configure" * event without the resize state. * * If triggered, the surface also will lose the focus of the device * (wl_pointer, wl_touch, etc) used for the resize. It is up to the * compositor to visually indicate that the resize is taking place, * such as updating a pointer cursor, during the resize. There is no * guarantee that the device focus will return when the resize is * completed. * * The edges parameter specifies how the surface should be resized, * and is one of the values of the resize_edge enum. The compositor * may use this information to update the surface position for * example when dragging the top left corner. The compositor may also * use this information to adapt its behavior, e.g. choose an * appropriate cursor image. */ static inline void zxdg_surface_v5_resize(struct xdg_surface *xdg_surface, struct wl_seat *seat, uint32_t serial, uint32_t edges) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_RESIZE, seat, serial, edges); } /** * @ingroup iface_zxdg_surface_v5 * * When a configure event is received, if a client commits the * surface in response to the configure event, then the client * must make an ack_configure request sometime before the commit * request, passing along the serial of the configure event. * * For instance, the compositor might use this information to move * a surface to the top left only when the client has drawn itself * for the maximized or fullscreen state. * * If the client receives multiple configure events before it * can respond to one, it only has to ack the last configure event. * * A client is not required to commit immediately after sending * an ack_configure request - it may even ack_configure several times * before its next surface commit. * * The compositor expects that the most recently received * ack_configure request at the time of a commit indicates which * configure event the client is responding to. */ static inline void zxdg_surface_v5_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_ACK_CONFIGURE, serial); } /** * @ingroup iface_zxdg_surface_v5 * * The window geometry of a window is its "visible bounds" from the * user's perspective. Client-side decorations often have invisible * portions like drop-shadows which should be ignored for the * purposes of aligning, placing and constraining windows. * * The window geometry is double buffered, and will be applied at the * time wl_surface.commit of the corresponding wl_surface is called. * * Once the window geometry of the surface is set once, it is not * possible to unset it, and it will remain the same until * set_window_geometry is called again, even if a new subsurface or * buffer is attached. * * If never set, the value is the full bounds of the surface, * including any subsurfaces. This updates dynamically on every * commit. This unset mode is meant for extremely simple clients. * * If responding to a configure event, the window geometry in here * must respect the sizing negotiations specified by the states in * the configure event. * * The arguments are given in the surface local coordinate space of * the wl_surface associated with this xdg_surface. * * The width and height must be greater than zero. */ static inline void zxdg_surface_v5_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_WINDOW_GEOMETRY, x, y, width, height); } /** * @ingroup iface_zxdg_surface_v5 * * Maximize the surface. * * After requesting that the surface should be maximized, the compositor * will respond by emitting a configure event with the "maximized" state * and the required window geometry. The client should then update its * content, drawing it in a maximized state, i.e. without shadow or other * decoration outside of the window geometry. The client must also * acknowledge the configure when committing the new content (see * ack_configure). * * It is up to the compositor to decide how and where to maximize the * surface, for example which output and what region of the screen should * be used. * * If the surface was already maximized, the compositor will still emit * a configure event with the "maximized" state. */ static inline void zxdg_surface_v5_set_maximized(struct xdg_surface *xdg_surface) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_MAXIMIZED); } /** * @ingroup iface_zxdg_surface_v5 * * Unmaximize the surface. * * After requesting that the surface should be unmaximized, the compositor * will respond by emitting a configure event without the "maximized" * state. If available, the compositor will include the window geometry * dimensions the window had prior to being maximized in the configure * request. The client must then update its content, drawing it in a * regular state, i.e. potentially with shadow, etc. The client must also * acknowledge the configure when committing the new content (see * ack_configure). * * It is up to the compositor to position the surface after it was * unmaximized; usually the position the surface had before maximizing, if * applicable. * * If the surface was already not maximized, the compositor will still * emit a configure event without the "maximized" state. */ static inline void zxdg_surface_v5_unset_maximized(struct xdg_surface *xdg_surface) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_UNSET_MAXIMIZED); } /** * @ingroup iface_zxdg_surface_v5 * * Make the surface fullscreen. * * You can specify an output that you would prefer to be fullscreen. * If this value is NULL, it's up to the compositor to choose which * display will be used to map this surface. * * If the surface doesn't cover the whole output, the compositor will * position the surface in the center of the output and compensate with * black borders filling the rest of the output. */ static inline void zxdg_surface_v5_set_fullscreen(struct xdg_surface *xdg_surface, struct wl_output *output) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_FULLSCREEN, output); } /** * @ingroup iface_zxdg_surface_v5 */ static inline void zxdg_surface_v5_unset_fullscreen(struct xdg_surface *xdg_surface) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_UNSET_FULLSCREEN); } /** * @ingroup iface_zxdg_surface_v5 * * Request that the compositor minimize your surface. There is no * way to know if the surface is currently minimized, nor is there * any way to unset minimization on this surface. * * If you are looking to throttle redrawing when minimized, please * instead use the wl_surface.frame event for this, as this will * also work with live previews on windows in Alt-Tab, Expose or * similar compositor features. */ static inline void zxdg_surface_v5_set_minimized(struct xdg_surface *xdg_surface) { wl_proxy_marshal((struct wl_proxy *) xdg_surface, ZXDG_SURFACE_V5_SET_MINIMIZED); } /** * @ingroup iface_zxdg_popup_v5 * @struct zxdg_popup_v5_listener */ struct zxdg_popup_v5_listener { /** * popup interaction is done * * The popup_done event is sent out when a popup is dismissed by * the compositor. The client should destroy the xdg_popup object * at this point. */ void (*popup_done)(void *data, struct xdg_popup *xdg_popup); }; /** * @ingroup iface_zxdg_popup_v5 */ static inline int zxdg_popup_v5_add_listener(struct xdg_popup *xdg_popup, const struct zxdg_popup_v5_listener *listener, void *data) { return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, (void (**)(void)) listener, data); } #define ZXDG_POPUP_V5_DESTROY 0 /** * @ingroup iface_zxdg_popup_v5 */ #define ZXDG_POPUP_V5_POPUP_DONE_SINCE_VERSION 1 /** * @ingroup iface_zxdg_popup_v5 */ #define ZXDG_POPUP_V5_DESTROY_SINCE_VERSION 1 /** @ingroup iface_xdg_popup */ static inline void zxdg_popup_v5_set_user_data(struct xdg_popup *xdg_popup, void *user_data) { wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); } /** @ingroup iface_xdg_popup */ static inline void * zxdg_popup_v5_get_user_data(struct xdg_popup *xdg_popup) { return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); } static inline uint32_t zxdg_popup_v5_get_version(struct xdg_popup *xdg_popup) { return wl_proxy_get_version((struct wl_proxy *) xdg_popup); } /** * @ingroup iface_zxdg_popup_v5 * * This destroys the popup. Explicitly destroying the zxdg_popup_v5 * object will also dismiss the popup, and unmap the surface. * * If this xdg_popup is not the "topmost" popup, a protocol error * will be sent. */ static inline void zxdg_popup_v5_destroy(struct xdg_popup *xdg_popup) { wl_proxy_marshal((struct wl_proxy *) xdg_popup, ZXDG_POPUP_V5_DESTROY); wl_proxy_destroy((struct wl_proxy *) xdg_popup); } #ifdef __cplusplus } #endif #endif diff --git a/src/compat/wayland-xdg-shell-v5-protocol.c b/src/compat/wayland-xdg-shell-v5-protocol.c index bb3f389..76556f9 100644 --- a/src/compat/wayland-xdg-shell-v5-protocol.c +++ b/src/compat/wayland-xdg-shell-v5-protocol.c @@ -1,128 +1,111 @@ /* * This file is *NOT* autogenerated. * It contains XDGShellV5 with method names modified to be not clashing with XDG stable * Strings inside the sent protocol remain the same */ /* - * Copyright © 2008-2013 Kristian Høgsberg - * Copyright © 2013 Rafael Antognolli - * Copyright © 2013 Jasper St. Pierre - * Copyright © 2010-2013 Intel Corporation + * SPDX-FileCopyrightText: 2008-2013 Kristian Høgsberg + * SPDX-FileCopyrightText: 2013 Rafael Antognolli + * SPDX-FileCopyrightText: 2013 Jasper St. Pierre + * SPDX-FileCopyrightText: 2010-2013 Intel Corporation * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. + * SPDX-License-Identifier: MIT */ #include #include #include "wayland-util.h" extern const struct wl_interface wl_output_interface; extern const struct wl_interface wl_seat_interface; extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zxdg_popup_v5_interface; extern const struct wl_interface zxdg_surface_v5_interface; static const struct wl_interface *types[] = { NULL, NULL, NULL, NULL, &zxdg_surface_v5_interface, &wl_surface_interface, &zxdg_popup_v5_interface, &wl_surface_interface, &wl_surface_interface, &wl_seat_interface, NULL, NULL, NULL, &zxdg_surface_v5_interface, &wl_seat_interface, NULL, NULL, NULL, &wl_seat_interface, NULL, &wl_seat_interface, NULL, NULL, &wl_output_interface, }; static const struct wl_message zxdg_shell_v5_requests[] = { { "destroy", "", types + 0 }, { "use_unstable_version", "i", types + 0 }, { "get_xdg_surface", "no", types + 4 }, { "get_xdg_popup", "nooouii", types + 6 }, { "pong", "u", types + 0 }, }; static const struct wl_message zxdg_shell_v5_events[] = { { "ping", "u", types + 0 }, }; WL_EXPORT const struct wl_interface zxdg_shell_v5_interface = { "xdg_shell", 1, 5, zxdg_shell_v5_requests, 1, zxdg_shell_v5_events, }; static const struct wl_message zxdg_surface_v5_requests[] = { { "destroy", "", types + 0 }, { "set_parent", "?o", types + 13 }, { "set_title", "s", types + 0 }, { "set_app_id", "s", types + 0 }, { "show_window_menu", "ouii", types + 14 }, { "move", "ou", types + 18 }, { "resize", "ouu", types + 20 }, { "ack_configure", "u", types + 0 }, { "set_window_geometry", "iiii", types + 0 }, { "set_maximized", "", types + 0 }, { "unset_maximized", "", types + 0 }, { "set_fullscreen", "?o", types + 23 }, { "unset_fullscreen", "", types + 0 }, { "set_minimized", "", types + 0 }, }; static const struct wl_message zxdg_surface_v5_events[] = { { "configure", "iiau", types + 0 }, { "close", "", types + 0 }, }; WL_EXPORT const struct wl_interface zxdg_surface_v5_interface = { "xdg_surface", 1, 14, zxdg_surface_v5_requests, 2, zxdg_surface_v5_events, }; static const struct wl_message zxdg_popup_v5_requests[] = { { "destroy", "", types + 0 }, }; static const struct wl_message zxdg_popup_v5_events[] = { { "popup_done", "", types + 0 }, }; WL_EXPORT const struct wl_interface zxdg_popup_v5_interface = { "xdg_popup", 1, 1, zxdg_popup_v5_requests, 1, zxdg_popup_v5_events, }; diff --git a/src/compat/wayland-xdg-shell-v5-server-protocol.h b/src/compat/wayland-xdg-shell-v5-server-protocol.h index e498f51..1fb3776 100644 --- a/src/compat/wayland-xdg-shell-v5-server-protocol.h +++ b/src/compat/wayland-xdg-shell-v5-server-protocol.h @@ -1,906 +1,889 @@ /* * This file is *NOT* autogenerated. * It contains XDGShellV5 with method names modified to be not clashing with XDG stable * Strings inside the sent protocol remain the same */ #ifndef ZXDG_SHELL_V5_UNSTABLE_V5_SERVER_PROTOCOL_H #define ZXDG_SHELL_V5_UNSTABLE_V5_SERVER_PROTOCOL_H #include #include #include "wayland-server.h" #ifdef __cplusplus extern "C" { #endif struct wl_client; struct wl_resource; /** * @page page_zxdg_shell_v5_unstable_v5 The zxdg_shell_v5_unstable_v5 protocol * @section page_ifaces_zxdg_shell_v5_unstable_v5 Interfaces * - @subpage page_iface_xdg_shell - create desktop-style surfaces * - @subpage page_iface_xdg_surface - A desktop window * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus * @section page_copyright_zxdg_shell_v5_unstable_v5 Copyright *
  *
- * Copyright © 2008-2013 Kristian Høgsberg
- * Copyright © 2013      Rafael Antognolli
- * Copyright © 2013      Jasper St. Pierre
- * Copyright © 2010-2013 Intel Corporation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * SPDX-FileCopyrightText: 2008-2013 Kristian Høgsberg
+ * SPDX-FileCopyrightText: 2013 Rafael Antognolli
+ * SPDX-FileCopyrightText: 2013 Jasper St. Pierre
+ * SPDX-FileCopyrightText: 2010-2013 Intel Corporation
+ *
+ * SPDX-License-Identifier: MIT
  * 
*/ struct wl_output; struct wl_seat; struct wl_surface; struct zxdg_popup_v5; struct zxdg_shell_v5; struct zxdg_surface_v5; /** * @page page_iface_zxdg_shell xdg_v5_shell * @section page_iface_zxdg_shell_v5_desc Description * * zxdg_shell allows clients to turn a wl_v5_surface into a "real window" * which can be dragged, resized, stacked, and moved around by the * user. Everything about this interface is suited towards traditional * desktop environments. * @section page_iface_zxdg_shell_v5_api API * See @ref iface_xdg_shell. */ /** * @defgroup iface_zxdg_shell The xdg_v5_shell interface * * zxdg_shell allows clients to turn a wl_v5_surface into a "real window" * which can be dragged, resized, stacked, and moved around by the * user. Everything about this interface is suited towards traditional * desktop environments. */ extern const struct wl_interface zxdg_shell_v5_interface; /** * @page page_iface_zxdg_surface xdg_v5_surface * @section page_iface_zxdg_surface_v5_desc Description * * An interface that may be implemented by a wl_surface, for * implementations that provide a desktop-style user interface. * * It provides requests to treat surfaces like windows, allowing to set * properties like maximized, fullscreen, minimized, and to move and resize * them, and associate metadata like title and app id. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_surface state to take effect. Prior to committing the new * state, it can set up initial configuration, such as maximizing or setting * a window geometry. * * Even without attaching a buffer the compositor must respond to initial * committed configuration, for instance sending a configure event with * expected window geometry if the client maximized its surface during * initialization. * * For a surface to be mapped by the compositor the client must have * committed both an xdg_surface state and a buffer. * @section page_iface_zxdg_surface_v5_api API * See @ref iface_xdg_surface. */ /** * @defgroup iface_zxdg_surface The xdg_v5_surface interface * * An interface that may be implemented by a wl_surface, for * implementations that provide a desktop-style user interface. * * It provides requests to treat surfaces like windows, allowing to set * properties like maximized, fullscreen, minimized, and to move and resize * them, and associate metadata like title and app id. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_surface state to take effect. Prior to committing the new * state, it can set up initial configuration, such as maximizing or setting * a window geometry. * * Even without attaching a buffer the compositor must respond to initial * committed configuration, for instance sending a configure event with * expected window geometry if the client maximized its surface during * initialization. * * For a surface to be mapped by the compositor the client must have * committed both an xdg_surface state and a buffer. */ extern const struct wl_interface zxdg_surface_v5_interface; /** * @page page_iface_zxdg_popup xdg_v5_popup * @section page_iface_zxdg_popup_v5_desc Description * * A popup surface is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * * When the popup is dismissed, a popup_done event will be sent out, * and at the same time the surface will be unmapped. The xdg_popup * object is now inert and cannot be reactivated, so clients should * destroy it. Explicitly destroying the xdg_popup object will also * dismiss the popup and unmap the surface. * * Clients will receive events for all their surfaces during this * grab (which is an "owner-events" grab in X11 parlance). This is * done so that users can navigate through submenus and other * "nested" popup windows without having to dismiss the topmost * popup. * * Clients that want to dismiss the popup when another surface of * their own is clicked should dismiss the popup using the destroy * request. * * The parent surface must have either an zxdg_surface or xdg_v5_popup * role. * * Specifying an xdg_popup for the parent means that the popups are * nested, with this popup now being the topmost popup. Nested * popups must be destroyed in the reverse order they were created * in, e.g. the only popup you are allowed to destroy at all times * is the topmost one. * * If there is an existing popup when creating a new popup, the * parent must be the current topmost popup. * * A parent surface must be mapped before the new popup is mapped. * * When compositors choose to dismiss a popup, they will likely * dismiss every nested popup as well. When a compositor dismisses * popups, it will follow the same dismissing order as required * from the client. * * The x and y arguments passed when creating the popup object specify * where the top left of the popup should be placed, relative to the * local surface coordinates of the parent surface. See * zxdg_shell.get_v5_xdg_popup. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * * For a surface to be mapped by the compositor the client must have * committed both the xdg_popup state and a buffer. * @section page_iface_zxdg_popup_v5_api API * See @ref iface_xdg_popup. */ /** * @defgroup iface_zxdg_popup The xdg_v5_popup interface * * A popup surface is a short-lived, temporary surface that can be * used to implement menus. It takes an explicit grab on the surface * that will be dismissed when the user dismisses the popup. This can * be done by the user clicking outside the surface, using the keyboard, * or even locking the screen through closing the lid or a timeout. * * When the popup is dismissed, a popup_done event will be sent out, * and at the same time the surface will be unmapped. The xdg_popup * object is now inert and cannot be reactivated, so clients should * destroy it. Explicitly destroying the xdg_popup object will also * dismiss the popup and unmap the surface. * * Clients will receive events for all their surfaces during this * grab (which is an "owner-events" grab in X11 parlance). This is * done so that users can navigate through submenus and other * "nested" popup windows without having to dismiss the topmost * popup. * * Clients that want to dismiss the popup when another surface of * their own is clicked should dismiss the popup using the destroy * request. * * The parent surface must have either an zxdg_surface or xdg_v5_popup * role. * * Specifying an xdg_popup for the parent means that the popups are * nested, with this popup now being the topmost popup. Nested * popups must be destroyed in the reverse order they were created * in, e.g. the only popup you are allowed to destroy at all times * is the topmost one. * * If there is an existing popup when creating a new popup, the * parent must be the current topmost popup. * * A parent surface must be mapped before the new popup is mapped. * * When compositors choose to dismiss a popup, they will likely * dismiss every nested popup as well. When a compositor dismisses * popups, it will follow the same dismissing order as required * from the client. * * The x and y arguments passed when creating the popup object specify * where the top left of the popup should be placed, relative to the * local surface coordinates of the parent surface. See * zxdg_shell.get_v5_xdg_popup. * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * * For a surface to be mapped by the compositor the client must have * committed both the xdg_popup state and a buffer. */ extern const struct wl_interface zxdg_popup_v5_interface; #ifndef ZXDG_SHELL_V5_VERSION_ENUM #define ZXDG_SHELL_V5_VERSION_ENUM /** * @ingroup iface_xdg_shell * latest protocol version * * The 'current' member of this enum gives the version of the * protocol. Implementations can compare this to the version * they implement using static_assert to ensure the protocol and * implementation versions match. */ enum zxdg_shell_v5_version { /** * Always the latest version */ ZXDG_SHELL_V5_VERSION_CURRENT = 5, }; #endif /* ZXDG_SHELL_V5_VERSION_ENUM */ #ifndef ZXDG_SHELL_V5_ERROR_ENUM #define ZXDG_SHELL_V5_ERROR_ENUM enum zxdg_shell_v5_error { /** * given wl_surface has another role */ ZXDG_SHELL_V5_ERROR_ROLE = 0, /** * xdg_shell was destroyed before children */ ZXDG_SHELL_V5_ERROR_DEFUNCT_SURFACES = 1, /** * the client tried to map or destroy a non-topmost popup */ ZXDG_SHELL_V5_ERROR_NOT_THE_TOPMOST_POPUP = 2, /** * the client specified an invalid popup parent surface */ ZXDG_SHELL_V5_ERROR_INVALID_POPUP_PARENT = 3, }; #endif /* ZXDG_SHELL_V5_ERROR_ENUM */ /** * @ingroup iface_xdg_shell * @struct zxdg_shell_v5_interface */ struct zxdg_shell_v5_interface { /** * destroy xdg_shell * * Destroy this xdg_shell object. * * Destroying a bound xdg_shell object while there are surfaces * still alive created by this xdg_shell object instance is illegal * and will result in a protocol error. */ void (*destroy)(struct wl_client *client, struct wl_resource *resource); /** * enable use of this unstable version * * Negotiate the unstable version of the interface. This * mechanism is in place to ensure client and server agree on the * unstable versions of the protocol that they speak or exit * cleanly if they don't agree. This request will go away once the * xdg-shell protocol is stable. */ void (*use_unstable_version)(struct wl_client *client, struct wl_resource *resource, int32_t version); /** * create a shell surface from a surface * * This creates an xdg_surface for the given surface and gives it * the zxdg_surface role. A wl_v5_surface can only be given an * zxdg_surface role once. If get_v5_xdg_surface is called with a * wl_surface that already has an active xdg_surface associated * with it, or if it had any other role, an error is raised. * * See the documentation of xdg_surface for more details about what * an xdg_surface is and how it is used. */ void (*get_zxdg_surface)(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface); /** * create a popup for a surface * * This creates an xdg_popup for the given surface and gives it * the zxdg_popup role. A wl_v5_surface can only be given an xdg_popup * role once. If get_zxdg_popup is called with a wl_v5_surface that * already has an active xdg_popup associated with it, or if it had * any other role, an error is raised. * * This request must be used in response to some sort of user * action like a button press, key press, or touch down event. * * See the documentation of xdg_popup for more details about what * an xdg_popup is and how it is used. * @param seat the wl_seat of the user event * @param serial the serial of the user event */ void (*get_zxdg_popup)(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface, struct wl_resource *parent, struct wl_resource *seat, uint32_t serial, int32_t x, int32_t y); /** * respond to a ping event * * A client must respond to a ping event with a pong request or * the client may be deemed unresponsive. * @param serial serial of the ping event */ void (*pong)(struct wl_client *client, struct wl_resource *resource, uint32_t serial); }; #define ZXDG_SHELL_V5_PING 0 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_PING_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_USE_UNSTABLE_VERSION_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_GET_ZXDG_SURFACE_V5_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_GET_ZXDG_POPUP_v5_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell */ #define ZXDG_SHELL_V5_PONG_SINCE_VERSION 1 /** * @ingroup iface_xdg_shell * Sends an ping event to the client owning the resource. * @param resource_ The client's resource * @param serial pass this to the pong request */ static inline void zxdg_shell_v5_send_ping(struct wl_resource *resource_, uint32_t serial) { wl_resource_post_event(resource_, ZXDG_SHELL_V5_PING, serial); } #ifndef ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM #define ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM /** * @ingroup iface_xdg_surface * edge values for resizing * * These values are used to indicate which edge of a surface * is being dragged in a resize operation. */ enum zxdg_surface_v5_resize_edge { ZXDG_SURFACE_V5_RESIZE_EDGE_NONE = 0, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP = 1, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM = 2, ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT = 4, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT = 5, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT = 6, ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT = 8, ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT = 9, ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT = 10, }; #endif /* ZXDG_SURFACE_V5_RESIZE_EDGE_ENUM */ #ifndef ZXDG_SURFACE_V5_STATE_ENUM #define ZXDG_SURFACE_V5_STATE_ENUM /** * @ingroup iface_xdg_surface * the surface is now activated * * Client window decorations should be painted as if the window is * active. Do not assume this means that the window actually has * keyboard or pointer focus. */ enum zxdg_surface_v5_state { /** * the surface is maximized */ ZXDG_SURFACE_V5_STATE_MAXIMIZED = 1, /** * the surface is fullscreen */ ZXDG_SURFACE_V5_STATE_FULLSCREEN = 2, /** * the surface is being resized */ ZXDG_SURFACE_V5_STATE_RESIZING = 3, /** * the surface is now activated */ ZXDG_SURFACE_V5_STATE_ACTIVATED = 4, }; #endif /* ZXDG_SURFACE_V5_STATE_ENUM */ /** * @ingroup iface_xdg_surface * @struct zxdg_surface_v5_interface */ struct zxdg_surface_v5_interface { /** * Destroy the xdg_surface * * Unmap and destroy the window. The window will be effectively * hidden from the user's point of view, and all state like * maximization, fullscreen, and so on, will be lost. */ void (*destroy)(struct wl_client *client, struct wl_resource *resource); /** * set the parent of this surface * * Set the "parent" of this surface. This window should be * stacked above a parent. The parent surface must be mapped as * long as this surface is mapped. * * Parent windows should be set on dialogs, toolboxes, or other * "auxiliary" surfaces, so that the parent is raised when the * dialog is raised. */ void (*set_parent)(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent); /** * set surface title * * Set a short title for the surface. * * This string may be used to identify the surface in a task bar, * window list, or other user interface elements provided by the * compositor. * * The string must be encoded in UTF-8. */ void (*set_title)(struct wl_client *client, struct wl_resource *resource, const char *title); /** * set application ID * * Set an application identifier for the surface. * * The app ID identifies the general class of applications to which * the surface belongs. The compositor can use this to group * multiple surfaces together, or to determine how to launch a new * application. * * For D-Bus activatable applications, the app ID is used as the * D-Bus service name. * * The compositor shell will try to group application surfaces * together by their app ID. As a best practice, it is suggested to * select app ID's that match the basename of the application's * .desktop file. For example, "org.freedesktop.FooViewer" where * the .desktop file is "org.freedesktop.FooViewer.desktop". * * See the desktop-entry specification [0] for more details on * application identifiers and how they relate to well-known D-Bus * names and .desktop files. * * [0] http://standards.freedesktop.org/desktop-entry-spec/ */ void (*set_app_id)(struct wl_client *client, struct wl_resource *resource, const char *app_id); /** * show the window menu * * Clients implementing client-side decorations might want to * show a context menu when right-clicking on the decorations, * giving the user a menu that they can use to maximize or minimize * the window. * * This request asks the compositor to pop up such a window menu at * the given position, relative to the local surface coordinates of * the parent surface. There are no guarantees as to what menu * items the window menu contains. * * This request must be used in response to some sort of user * action like a button press, key press, or touch down event. * @param seat the wl_seat of the user event * @param serial the serial of the user event * @param x the x position to pop up the window menu at * @param y the y position to pop up the window menu at */ void (*show_window_menu)(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, int32_t x, int32_t y); /** * start an interactive move * * Start an interactive, user-driven move of the surface. * * This request must be used in response to some sort of user * action like a button press, key press, or touch down event. The * passed serial is used to determine the type of interactive move * (touch, pointer, etc). * * The server may ignore move requests depending on the state of * the surface (e.g. fullscreen or maximized), or if the passed * serial is no longer valid. * * If triggered, the surface will lose the focus of the device * (wl_pointer, wl_touch, etc) used for the move. It is up to the * compositor to visually indicate that the move is taking place, * such as updating a pointer cursor, during the move. There is no * guarantee that the device focus will return when the move is * completed. * @param seat the wl_seat of the user event * @param serial the serial of the user event */ void (*move)(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial); /** * start an interactive resize * * Start a user-driven, interactive resize of the surface. * * This request must be used in response to some sort of user * action like a button press, key press, or touch down event. The * passed serial is used to determine the type of interactive * resize (touch, pointer, etc). * * The server may ignore resize requests depending on the state of * the surface (e.g. fullscreen or maximized). * * If triggered, the client will receive configure events with the * "resize" state enum value and the expected sizes. See the * "resize" enum value for more details about what is required. The * client must also acknowledge configure events using * "ack_configure". After the resize is completed, the client will * receive another "configure" event without the resize state. * * If triggered, the surface also will lose the focus of the device * (wl_pointer, wl_touch, etc) used for the resize. It is up to the * compositor to visually indicate that the resize is taking place, * such as updating a pointer cursor, during the resize. There is * no guarantee that the device focus will return when the resize * is completed. * * The edges parameter specifies how the surface should be resized, * and is one of the values of the resize_edge enum. The compositor * may use this information to update the surface position for * example when dragging the top left corner. The compositor may * also use this information to adapt its behavior, e.g. choose an * appropriate cursor image. * @param seat the wl_seat of the user event * @param serial the serial of the user event * @param edges which edge or corner is being dragged */ void (*resize)(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t serial, uint32_t edges); /** * ack a configure event * * When a configure event is received, if a client commits the * surface in response to the configure event, then the client must * make an ack_configure request sometime before the commit * request, passing along the serial of the configure event. * * For instance, the compositor might use this information to move * a surface to the top left only when the client has drawn itself * for the maximized or fullscreen state. * * If the client receives multiple configure events before it can * respond to one, it only has to ack the last configure event. * * A client is not required to commit immediately after sending an * ack_configure request - it may even ack_configure several times * before its next surface commit. * * The compositor expects that the most recently received * ack_configure request at the time of a commit indicates which * configure event the client is responding to. * @param serial the serial from the configure event */ void (*ack_configure)(struct wl_client *client, struct wl_resource *resource, uint32_t serial); /** * set the new window geometry * * The window geometry of a window is its "visible bounds" from * the user's perspective. Client-side decorations often have * invisible portions like drop-shadows which should be ignored for * the purposes of aligning, placing and constraining windows. * * The window geometry is double buffered, and will be applied at * the time wl_surface.commit of the corresponding wl_surface is * called. * * Once the window geometry of the surface is set once, it is not * possible to unset it, and it will remain the same until * set_window_geometry is called again, even if a new subsurface or * buffer is attached. * * If never set, the value is the full bounds of the surface, * including any subsurfaces. This updates dynamically on every * commit. This unset mode is meant for extremely simple clients. * * If responding to a configure event, the window geometry in here * must respect the sizing negotiations specified by the states in * the configure event. * * The arguments are given in the surface local coordinate space of * the wl_surface associated with this xdg_surface. * * The width and height must be greater than zero. */ void (*set_window_geometry)(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); /** * maximize the window * * Maximize the surface. * * After requesting that the surface should be maximized, the * compositor will respond by emitting a configure event with the * "maximized" state and the required window geometry. The client * should then update its content, drawing it in a maximized state, * i.e. without shadow or other decoration outside of the window * geometry. The client must also acknowledge the configure when * committing the new content (see ack_configure). * * It is up to the compositor to decide how and where to maximize * the surface, for example which output and what region of the * screen should be used. * * If the surface was already maximized, the compositor will still * emit a configure event with the "maximized" state. */ void (*set_maximized)(struct wl_client *client, struct wl_resource *resource); /** * unmaximize the window * * Unmaximize the surface. * * After requesting that the surface should be unmaximized, the * compositor will respond by emitting a configure event without * the "maximized" state. If available, the compositor will include * the window geometry dimensions the window had prior to being * maximized in the configure request. The client must then update * its content, drawing it in a regular state, i.e. potentially * with shadow, etc. The client must also acknowledge the configure * when committing the new content (see ack_configure). * * It is up to the compositor to position the surface after it was * unmaximized; usually the position the surface had before * maximizing, if applicable. * * If the surface was already not maximized, the compositor will * still emit a configure event without the "maximized" state. */ void (*unset_maximized)(struct wl_client *client, struct wl_resource *resource); /** * set the window as fullscreen on a monitor * * Make the surface fullscreen. * * You can specify an output that you would prefer to be * fullscreen. If this value is NULL, it's up to the compositor to * choose which display will be used to map this surface. * * If the surface doesn't cover the whole output, the compositor * will position the surface in the center of the output and * compensate with black borders filling the rest of the output. */ void (*set_fullscreen)(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output); /** */ void (*unset_fullscreen)(struct wl_client *client, struct wl_resource *resource); /** * set the window as minimized * * Request that the compositor minimize your surface. There is no * way to know if the surface is currently minimized, nor is there * any way to unset minimization on this surface. * * If you are looking to throttle redrawing when minimized, please * instead use the wl_surface.frame event for this, as this will * also work with live previews on windows in Alt-Tab, Expose or * similar compositor features. */ void (*set_minimized)(struct wl_client *client, struct wl_resource *resource); }; #define ZXDG_SURFACE_V5_CONFIGURE 0 #define ZXDG_SURFACE_V5_CLOSE 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_CONFIGURE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_CLOSE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_PARENT_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_TITLE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_APP_ID_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SHOW_WINDOW_MENU_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_MOVE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_RESIZE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_ACK_CONFIGURE_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_MAXIMIZED_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_UNSET_MAXIMIZED_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_FULLSCREEN_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_UNSET_FULLSCREEN_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface */ #define ZXDG_SURFACE_V5_SET_MINIMIZED_SINCE_VERSION 1 /** * @ingroup iface_xdg_surface * Sends an configure event to the client owning the resource. * @param resource_ The client's resource */ static inline void zxdg_surface_v5_send_configure(struct wl_resource *resource_, int32_t width, int32_t height, struct wl_array *states, uint32_t serial) { wl_resource_post_event(resource_, ZXDG_SURFACE_V5_CONFIGURE, width, height, states, serial); } /** * @ingroup iface_xdg_surface * Sends an close event to the client owning the resource. * @param resource_ The client's resource */ static inline void zxdg_surface_v5_send_close(struct wl_resource *resource_) { wl_resource_post_event(resource_, ZXDG_SURFACE_V5_CLOSE); } /** * @ingroup iface_xdg_popup * @struct zxdg_popup_v5_interface */ struct zxdg_popup_v5_interface { /** * remove xdg_popup interface * * This destroys the popup. Explicitly destroying the xdg_popup * object will also dismiss the popup, and unmap the surface. * * If this xdg_popup is not the "topmost" popup, a protocol error * will be sent. */ void (*destroy)(struct wl_client *client, struct wl_resource *resource); }; #define ZXDG_POPUP_v5_POPUP_DONE 0 /** * @ingroup iface_xdg_popup */ #define ZXDG_POPUP_v5_POPUP_DONE_SINCE_VERSION 1 /** * @ingroup iface_xdg_popup */ #define ZXDG_POPUP_v5_DESTROY_SINCE_VERSION 1 /** * @ingroup iface_xdg_popup * Sends an popup_done event to the client owning the resource. * @param resource_ The client's resource */ static inline void zxdg_popup_v5_send_popup_done(struct wl_resource *resource_) { wl_resource_post_event(resource_, ZXDG_POPUP_v5_POPUP_DONE); } #ifdef __cplusplus } #endif #endif diff --git a/src/server/appmenu_interface.cpp b/src/server/appmenu_interface.cpp index bdde4bd..f8907e6 100644 --- a/src/server/appmenu_interface.cpp +++ b/src/server/appmenu_interface.cpp @@ -1,216 +1,202 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "appmenu_interface.h" #include "display.h" #include "surface_interface.h" #include "global_p.h" #include "resource_p.h" #include "logging.h" #include #include namespace KWayland { namespace Server { class AppMenuManagerInterface::Private : public Global::Private { public: Private(AppMenuManagerInterface *q, Display *d); QVector appmenus; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); AppMenuManagerInterface *q; static const struct org_kde_kwin_appmenu_manager_interface s_interface; static const quint32 s_version; }; const quint32 AppMenuManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_appmenu_manager_interface AppMenuManagerInterface::Private::s_interface = { createCallback }; #endif void AppMenuManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { // TODO: send error? qCWarning(KWAYLAND_SERVER) << "ServerSideDecorationInterface requested for non existing SurfaceInterface"; return; } auto appmenu = new AppMenuInterface(p->q, s, resource); appmenu->create(p->display->getConnection(client), wl_resource_get_version(resource), id); if (!appmenu->resource()) { wl_resource_post_no_memory(resource); delete appmenu; return; } p->appmenus.append(appmenu); QObject::connect(appmenu, &QObject::destroyed, p->q, [=]() { p->appmenus.removeOne(appmenu); }); emit p->q->appMenuCreated(appmenu); } AppMenuManagerInterface::Private::Private(AppMenuManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_appmenu_manager_interface, s_version) , q(q) { } void AppMenuManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_appmenu_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); } void AppMenuManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } class AppMenuInterface::Private : public Resource::Private { public: Private(AppMenuInterface *q, AppMenuManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); SurfaceInterface *surface; InterfaceAddress address; private: static void setAddressCallback(wl_client *client, wl_resource *resource, const char * service_name, const char * object_path); AppMenuInterface *q_func() { return reinterpret_cast(q); } static AppMenuInterface *get(SurfaceInterface *s); static const struct org_kde_kwin_appmenu_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_appmenu_interface AppMenuInterface::Private::s_interface = { setAddressCallback, resourceDestroyedCallback }; #endif void AppMenuInterface::Private::setAddressCallback(wl_client *client, wl_resource *resource, const char * service_name, const char * object_path) { Q_UNUSED(client); auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); if (p->address.serviceName == QLatin1String(service_name) && p->address.objectPath == QLatin1String(object_path)) { return; } p->address.serviceName = QString::fromLatin1(service_name); p->address.objectPath = QString::fromLatin1(object_path); emit p->q_func()->addressChanged(p->address); } AppMenuInterface::Private::Private(AppMenuInterface *q, AppMenuManagerInterface *c, SurfaceInterface *s, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_appmenu_interface, &s_interface), surface(s) { } AppMenuInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } AppMenuManagerInterface::AppMenuManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } AppMenuManagerInterface::~AppMenuManagerInterface() { } AppMenuManagerInterface::Private *AppMenuManagerInterface::d_func() const { return reinterpret_cast(d.data()); } AppMenuInterface* AppMenuManagerInterface::appMenuForSurface(SurfaceInterface *surface) { Q_D(); for (AppMenuInterface* menu: d->appmenus) { if (menu->surface() == surface) { return menu; } } return nullptr; } AppMenuInterface::AppMenuInterface(AppMenuManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource): Resource(new Private(this, parent, s, parentResource)) { } AppMenuInterface::Private *AppMenuInterface::d_func() const { return reinterpret_cast(d.data()); } AppMenuInterface::~AppMenuInterface() {} AppMenuInterface::InterfaceAddress AppMenuInterface::address() const { Q_D(); return d->address; } SurfaceInterface* AppMenuInterface::surface() const { Q_D(); return d->surface; } }//namespace } diff --git a/src/server/appmenu_interface.h b/src/server/appmenu_interface.h index 5548ad6..0ae6b21 100644 --- a/src/server/appmenu_interface.h +++ b/src/server/appmenu_interface.h @@ -1,116 +1,102 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_APPMENU_INTERFACE_H #define KWAYLAND_SERVER_APPMENU_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class SurfaceInterface; class AppMenuInterface; /** * Provides the DBus service name and object path to a AppMenu DBus interface. * * This global can be used for clients to bind AppmenuInterface instances * and notifies when a new one is created * @since 5.42 */ class KWAYLANDSERVER_EXPORT AppMenuManagerInterface : public Global { Q_OBJECT public: virtual ~AppMenuManagerInterface(); /** * Returns any existing appMenu for a given surface * This returns a null pointer if no AppMenuInterface exists. */ AppMenuInterface* appMenuForSurface(SurfaceInterface *); Q_SIGNALS: /** * Emitted whenever a new AppmenuInterface is created. **/ void appMenuCreated(KWayland::Server::AppMenuInterface*); private: explicit AppMenuManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; /** * Provides the DBus service name and object path to a AppMenu DBus interface. * This interface is attached to a wl_surface and provides access to where * the AppMenu DBus interface is registered. * @since 5.42 */ class KWAYLANDSERVER_EXPORT AppMenuInterface : public Resource { Q_OBJECT public: /** * Structure containing DBus service name and path */ struct InterfaceAddress { /** Service name of host with the AppMenu object*/ QString serviceName; /** Object path of the AppMenu interface*/ QString objectPath; }; virtual ~AppMenuInterface(); /** * @returns the service name and object path or empty strings if unset */ InterfaceAddress address() const; /** * @returns The SurfaceInterface this AppmenuInterface references. **/ SurfaceInterface *surface() const; Q_SIGNALS: /** * Emitted when the address changes or is first received */ void addressChanged(KWayland::Server::AppMenuInterface::InterfaceAddress); private: explicit AppMenuInterface(AppMenuManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource); friend class AppMenuManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/blur_interface.cpp b/src/server/blur_interface.cpp index 6816269..e6a803a 100644 --- a/src/server/blur_interface.cpp +++ b/src/server/blur_interface.cpp @@ -1,215 +1,201 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "blur_interface.h" #include "region_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include #include namespace KWayland { namespace Server { class BlurManagerInterface::Private : public Global::Private { public: Private(BlurManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createBlur(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { auto blurManager = reinterpret_cast*>(wl_resource_get_user_data(r))->data(); if (blurManager) { return static_cast(blurManager->d.data()); } return nullptr; } BlurManagerInterface *q; static const struct org_kde_kwin_blur_manager_interface s_interface; static const quint32 s_version; }; const quint32 BlurManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_blur_manager_interface BlurManagerInterface::Private::s_interface = { createCallback, unsetCallback }; #endif BlurManagerInterface::Private::Private(BlurManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_blur_manager_interface, s_version) , q(q) { } void BlurManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_blur_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } auto ref = new QPointer(q);//deleted in unbind wl_resource_set_implementation(resource, &s_interface, ref, unbind); } void BlurManagerInterface::Private::unbind(wl_resource *r) { delete reinterpret_cast*>(wl_resource_get_user_data(r)); } void BlurManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto m = cast(resource); if (!m) { return;// will happen if global is deleted } m->createBlur(client, resource, id, surface); } void BlurManagerInterface::Private::createBlur(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } BlurInterface *blur = new BlurInterface(q, resource); blur->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!blur->resource()) { wl_resource_post_no_memory(resource); delete blur; return; } s->d_func()->setBlur(QPointer(blur)); } void BlurManagerInterface::Private::unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface) { Q_UNUSED(client) Q_UNUSED(resource) SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } s->d_func()->setBlur(QPointer()); } BlurManagerInterface::BlurManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } BlurManagerInterface::~BlurManagerInterface() = default; class BlurInterface::Private : public Resource::Private { public: Private(BlurInterface *q, BlurManagerInterface *c, wl_resource *parentResource); ~Private(); QRegion pendingRegion; QRegion currentRegion; private: void commit(); //TODO BlurInterface *q_func() { return reinterpret_cast(q); } static void commitCallback(wl_client *client, wl_resource *resource); static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static const struct org_kde_kwin_blur_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_blur_interface BlurInterface::Private::s_interface = { commitCallback, setRegionCallback, resourceDestroyedCallback }; #endif void BlurInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->commit(); } void BlurInterface::Private::commit() { currentRegion = pendingRegion; } void BlurInterface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { Q_UNUSED(client) Private *p = cast(resource); RegionInterface *r = RegionInterface::get(region); if (r) { p->pendingRegion = r->region(); } else { p->pendingRegion = QRegion(); } } BlurInterface::Private::Private(BlurInterface *q, BlurManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_blur_interface, &s_interface) { } BlurInterface::Private::~Private() = default; BlurInterface::BlurInterface(BlurManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } BlurInterface::~BlurInterface() = default; QRegion BlurInterface::region() { Q_D(); return d->currentRegion; } BlurInterface::Private *BlurInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/blur_interface.h b/src/server/blur_interface.h index 312db0c..6204a6f 100644 --- a/src/server/blur_interface.h +++ b/src/server/blur_interface.h @@ -1,94 +1,80 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_BLUR_INTERFACE_H #define KWAYLAND_SERVER_BLUR_INTERFACE_H #include "global.h" #include "resource.h" #include #include namespace KWayland { namespace Server { class Display; /** * @brief Represents the Global for org_kde_kwin_blur_manager interface. * * This class creates BlurInterfaces and attaches them to SurfaceInterfaces. * * @see BlurInterface * @see SurfaceInterface * @since 5.5 **/ class KWAYLANDSERVER_EXPORT BlurManagerInterface : public Global { Q_OBJECT public: virtual ~BlurManagerInterface(); private: explicit BlurManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * @brief Represents the Resource for the org_kde_kwin_blur interface. * * Instances of this class are only generated by the BlurManagerInterface. * The BlurInterface gets attached to a SurfaceInterface and can be assessed * from there using @link SurfaceInterface::blur() @endlink. Please note that * the BlurInterface is only available on the SurfaceInterface after it has been * committed. * * @see BlurManagerInterface * @see SurfaceInterface * @since 5.5 **/ class KWAYLANDSERVER_EXPORT BlurInterface : public Resource { Q_OBJECT public: virtual ~BlurInterface(); /** * @returns The region or the SurfaceInterface which should be blurred, null Region implies complete surface. **/ QRegion region(); private: explicit BlurInterface(BlurManagerInterface *parent, wl_resource *parentResource); friend class BlurManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp index 3f6aaaf..c21321c 100644 --- a/src/server/buffer_interface.cpp +++ b/src/server/buffer_interface.cpp @@ -1,353 +1,339 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "buffer_interface.h" #include "display.h" #include "logging.h" #include "surface_interface.h" #include "linuxdmabuf_v1_interface.h" // Wayland #include // EGL #include #include #include "drm_fourcc.h" namespace KWayland { namespace Server { namespace EGL { typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr; } class BufferInterface::Private { public: Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent); ~Private(); QImage::Format format() const; QImage createImage(); wl_resource *buffer; wl_shm_buffer *shmBuffer; LinuxDmabufBuffer *dmabufBuffer; SurfaceInterface *surface; int refCount; QSize size; bool alpha; static BufferInterface *get(wl_resource *r); private: static void destroyListenerCallback(wl_listener *listener, void *data); static Private *cast(wl_resource *r); static void imageBufferCleanupHandler(void *info); static QList s_buffers; static Private *s_accessedBuffer; static int s_accessCounter; BufferInterface *q; wl_listener listener; }; QList BufferInterface::Private::s_buffers; BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr; int BufferInterface::Private::s_accessCounter = 0; BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r) { auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) { return d->buffer == r; }); if (it == s_buffers.constEnd()) { return nullptr; } return *it; } BufferInterface *BufferInterface::Private::get(wl_resource *r) { Private *p = cast(r); if (!p) { return nullptr; } return p->q; } void BufferInterface::Private::imageBufferCleanupHandler(void *info) { Private *p = reinterpret_cast(info); Q_ASSERT(p == s_accessedBuffer); Q_ASSERT(s_accessCounter > 0); s_accessCounter--; if (s_accessCounter == 0) { s_accessedBuffer = nullptr; } wl_shm_buffer_end_access(p->shmBuffer); } BufferInterface::Private::Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent) : buffer(resource) , shmBuffer(wl_shm_buffer_get(resource)) , dmabufBuffer(nullptr) , surface(parent) , refCount(0) , alpha(false) , q(q) { if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) { dmabufBuffer = static_cast(wl_resource_get_user_data(resource)); } s_buffers << this; listener.notify = destroyListenerCallback; listener.link.prev = nullptr; listener.link.next = nullptr; wl_resource_add_destroy_listener(resource, &listener); if (shmBuffer) { size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer)); // check alpha switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: alpha = true; break; case WL_SHM_FORMAT_XRGB8888: default: alpha = false; break; } } else if (dmabufBuffer) { switch (dmabufBuffer->format()) { case DRM_FORMAT_ARGB4444: case DRM_FORMAT_ABGR4444: case DRM_FORMAT_RGBA4444: case DRM_FORMAT_BGRA4444: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ABGR1555: case DRM_FORMAT_RGBA5551: case DRM_FORMAT_BGRA5551: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_RGBA1010102: case DRM_FORMAT_BGRA1010102: case DRM_FORMAT_XRGB8888_A8: case DRM_FORMAT_XBGR8888_A8: case DRM_FORMAT_RGBX8888_A8: case DRM_FORMAT_BGRX8888_A8: case DRM_FORMAT_RGB888_A8: case DRM_FORMAT_BGR888_A8: case DRM_FORMAT_RGB565_A8: case DRM_FORMAT_BGR565_A8: alpha = true; break; default: alpha = false; break; } size = dmabufBuffer->size(); } else if (parent) { EGLDisplay eglDisplay = parent->global()->display()->eglDisplay(); static bool resolved = false; using namespace EGL; if (!resolved && eglDisplay != EGL_NO_DISPLAY) { eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL"); resolved = true; } if (eglQueryWaylandBufferWL) { EGLint width, height; bool valid = false; valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width); valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height); if (valid) { size = QSize(width, height); } // check alpha EGLint format; if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) { switch (format) { case EGL_TEXTURE_RGBA: alpha = true; break; case EGL_TEXTURE_RGB: default: alpha = false; break; } } } } } BufferInterface::Private::~Private() { wl_list_remove(&listener.link); s_buffers.removeAll(this); } BufferInterface *BufferInterface::get(wl_resource *r) { if (!r) { return nullptr; } // TODO: verify it's a buffer BufferInterface *b = Private::get(r); if (b) { return b; } return new BufferInterface(r, nullptr); } BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent) : QObject() , d(new Private(this, resource, parent)) { } BufferInterface::~BufferInterface() { if (d->refCount != 0) { qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount; } } void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data) { Q_UNUSED(listener); auto b = cast(reinterpret_cast(data)); b->buffer = nullptr; emit b->q->aboutToBeDestroyed(b->q); delete b->q; } void BufferInterface::ref() { d->refCount++; } void BufferInterface::unref() { Q_ASSERT(d->refCount > 0); d->refCount--; if (d->refCount == 0) { if (d->buffer) { wl_buffer_send_release(d->buffer); wl_client_flush(wl_resource_get_client(d->buffer)); } deleteLater(); } } QImage::Format BufferInterface::Private::format() const { if (!shmBuffer) { return QImage::Format_Invalid; } switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied; case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; default: return QImage::Format_Invalid; } } QImage BufferInterface::data() { return std::move(d->createImage()); } QImage BufferInterface::Private::createImage() { if (!shmBuffer) { return QImage(); } if (s_accessedBuffer != nullptr && s_accessedBuffer != this) { return QImage(); } const QImage::Format imageFormat = format(); if (imageFormat == QImage::Format_Invalid) { return QImage(); } s_accessedBuffer = this; s_accessCounter++; wl_shm_buffer_begin_access(shmBuffer); return std::move(QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer), size.width(), size.height(), wl_shm_buffer_get_stride(shmBuffer), imageFormat, &imageBufferCleanupHandler, this)); } bool BufferInterface::isReferenced() const { return d->refCount > 0; } SurfaceInterface *BufferInterface::surface() const { return d->surface; } wl_shm_buffer *BufferInterface::shmBuffer() { return d->shmBuffer; } LinuxDmabufBuffer *BufferInterface::linuxDmabufBuffer() { return d->dmabufBuffer; } wl_resource *BufferInterface::resource() const { return d->buffer; } QSize BufferInterface::size() const { return d->size; } void BufferInterface::setSize(const QSize &size) { if (d->shmBuffer || d->size == size) { return; } d->size = size; emit sizeChanged(); } bool BufferInterface::hasAlphaChannel() const { return d->alpha; } } } diff --git a/src/server/buffer_interface.h b/src/server/buffer_interface.h index fbf2c35..f82cf43 100644 --- a/src/server/buffer_interface.h +++ b/src/server/buffer_interface.h @@ -1,193 +1,179 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_BUFFER_INTERFACE_H #define WAYLAND_SERVER_BUFFER_INTERFACE_H #include #include #include struct wl_resource; struct wl_shm_buffer; namespace KWayland { namespace Server { class SurfaceInterface; class LinuxDmabufBuffer; /** * @brief Reference counted representation of a Wayland buffer on Server side. * * This class encapsulates a rendering buffer which is normally attached to a SurfaceInterface. * A client should not render to a Wayland buffer as long as the buffer gets used by the server. * The server signals whether it's still used. This class provides a convenience access for this * functionality by performing reference counting and deleting the BufferInterface instance * automatically once it is no longer accessed. * * The BufferInterface is referenced as long as it is attached to a SurfaceInterface. If one wants * to keep access to the BufferInterface for a longer time ensure to call ref on first usage and * unref again once access to it is no longer needed. * * In Wayland the buffer is an abstract concept and a buffer might represent multiple different * concrete buffer techniques. This class has direct support for shared memory buffers built and * provides access to the native buffer for different (e.g. EGL/drm) buffers. * * If the EGL display has been registered in the Display the BufferInterface can also provide * some information about an EGL/drm buffer. * * For shared memory buffers a direct conversion to a memory-mapped QImage possible using the * data method. Please refer to the documentation for notes on the restrictions when using the * shared memory-mapped QImages. * * @see Display * @see SurfaceInterace **/ class KWAYLANDSERVER_EXPORT BufferInterface : public QObject { Q_OBJECT public: virtual ~BufferInterface(); /** * Reference the BufferInterface. * * As long as the reference counting has not reached @c 0 the BufferInterface is valid * and blocked for usage by the client. * * @see unref * @see isReferenced **/ void ref(); /** * Unreference the BufferInterface. * * If the reference counting reached @c 0 the BufferInterface is released, so that the * client can use it again. The instance of this BufferInterface will be automatically * deleted. * * @see ref * @see isReferenced **/ void unref(); /** * @returns whether the BufferInterface is currently referenced * * @see ref * @see unref **/ bool isReferenced() const; /** * @returns The SurfaceInterface this BufferInterface is attached to. **/ SurfaceInterface *surface() const; /** * @returns The native wl_shm_buffer if the BufferInterface represents a shared memory buffer, otherwise @c nullptr. **/ wl_shm_buffer *shmBuffer(); /** * Returns a pointer to the LinuxDmabufBuffer when the buffer is a dmabuf buffer, and nullptr otherwise. */ LinuxDmabufBuffer *linuxDmabufBuffer(); /** * @returns the native wl_resource wrapped by this BufferInterface. **/ wl_resource *resource() const; /** * Creates a QImage for the shared memory buffer. * * If the BufferInterface does not reference a shared memory buffer a null QImage is returned. * * The QImage shares the memory with the buffer and this constraints how the returned * QImage can be used and when this method can be invoked. * * It is not safe to have two shared memory QImages for different BufferInterfaces at * the same time. This method ensures that this does not happen and returns a null * QImage if a different BufferInterface's data is still mapped to a QImage. Please note * that this also applies to all implicitly data shared copies. * * In case it is needed to keep a copy, a deep copy has to be performed by using QImage::copy. * * As the underlying shared memory buffer is owned by a different client it is not safe to * write to the returned QImage. The image is a read-only buffer. If there is need to modify * the image, perform a deep copy. * **/ QImage data(); /** * Returns the size of this BufferInterface. * Note: only for shared memory buffers (shmBuffer) the size can be derived, * for other buffers it might be possible to derive the size if an EGL display * is set on Display otherwise the user of the BufferInterface has to use setSize to * provide the proper size. * @see setSize * @see Display::setEglDisplay * @since 5.3 **/ QSize size() const; /** * Sets the @p size for non shared memory buffers. * @see size * @see sizeChanged * @since 5.3 **/ void setSize(const QSize &size); /** * Returns whether the format of the BufferInterface has an alpha channel. * For shared memory buffers returns @c true for format @c WL_SHM_FORMAT_ARGB8888, * for all other formats returns @c false. * * For EGL buffers returns @c true for format @c EGL_TEXTURE_RGBA, for all other formats * returns @c false. * * If the format cannot be queried the default value (@c false) is returned. * * @since 5.4 **/ bool hasAlphaChannel() const; static BufferInterface *get(wl_resource *r); Q_SIGNALS: void aboutToBeDestroyed(KWayland::Server::BufferInterface*); /** * Emitted when the size of the Buffer changes. * @since 5.3 **/ void sizeChanged(); private: friend class SurfaceInterface; explicit BufferInterface(wl_resource *resource, SurfaceInterface *parent); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::BufferInterface*) #endif diff --git a/src/server/clientconnection.cpp b/src/server/clientconnection.cpp index 2d0b019..be138e2 100644 --- a/src/server/clientconnection.cpp +++ b/src/server/clientconnection.cpp @@ -1,174 +1,160 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "clientconnection.h" #include "display.h" // Qt #include #include // Wayland #include namespace KWayland { namespace Server { class ClientConnection::Private { public: explicit Private(wl_client *c, Display *display, ClientConnection *q); ~Private(); wl_client *client; Display *display; pid_t pid = 0; uid_t user = 0; gid_t group = 0; QString executablePath; private: static void destroyListenerCallback(wl_listener *listener, void *data); ClientConnection *q; wl_listener listener; static QVector s_allClients; }; QVector ClientConnection::Private::s_allClients; ClientConnection::Private::Private(wl_client *c, Display *display, ClientConnection *q) : client(c) , display(display) , q(q) { s_allClients << this; listener.notify = destroyListenerCallback; wl_client_add_destroy_listener(c, &listener); wl_client_get_credentials(client, &pid, &user, &group); executablePath = QFileInfo(QStringLiteral("/proc/%1/exe").arg(pid)).symLinkTarget(); } ClientConnection::Private::~Private() { if (client) { wl_list_remove(&listener.link); } s_allClients.removeAt(s_allClients.indexOf(this)); } void ClientConnection::Private::destroyListenerCallback(wl_listener *listener, void *data) { Q_UNUSED(listener) wl_client *client = reinterpret_cast(data); auto it = std::find_if(s_allClients.constBegin(), s_allClients.constEnd(), [client](Private *c) { return c->client == client; } ); Q_ASSERT(it != s_allClients.constEnd()); auto p = (*it); auto q = p->q; p->client = nullptr; wl_list_remove(&p->listener.link); emit q->disconnected(q); q->deleteLater(); } ClientConnection::ClientConnection(wl_client *c, Display *parent) : QObject(parent) , d(new Private(c, parent, this)) { } ClientConnection::~ClientConnection() = default; void ClientConnection::flush() { if (!d->client) { return; } wl_client_flush(d->client); } void ClientConnection::destroy() { if (!d->client) { return; } wl_client_destroy(d->client); } wl_resource *ClientConnection::createResource(const wl_interface *interface, quint32 version, quint32 id) { if (!d->client) { return nullptr; } return wl_resource_create(d->client, interface, version, id); } wl_resource *ClientConnection::getResource(quint32 id) { if (!d->client) { return nullptr; } return wl_client_get_object(d->client, id); } wl_client *ClientConnection::client() { return d->client; } ClientConnection::operator wl_client*() { return d->client; } ClientConnection::operator wl_client*() const { return d->client; } Display *ClientConnection::display() { return d->display; } gid_t ClientConnection::groupId() const { return d->group; } pid_t ClientConnection::processId() const { return d->pid; } uid_t ClientConnection::userId() const { return d->user; } QString ClientConnection::executablePath() const { return d->executablePath; } } } diff --git a/src/server/clientconnection.h b/src/server/clientconnection.h index 08f2321..3116ffa 100644 --- a/src/server/clientconnection.h +++ b/src/server/clientconnection.h @@ -1,162 +1,148 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_CLIENTCONNECTION_H #define KWAYLAND_SERVER_CLIENTCONNECTION_H #include #include #include struct wl_client; struct wl_interface; struct wl_resource; namespace KWayland { namespace Server { class Display; /** * @brief Convenient Class which represents a wl_client. * * The ClientConnection gets automatically created for a wl_client when a wl_client is * first used in the context of KWayland::Server. In particular the signal * @link Display::clientConnected @endlink will be emitted. * * @see Display **/ class KWAYLANDSERVER_EXPORT ClientConnection : public QObject { Q_OBJECT public: virtual ~ClientConnection(); /** * Flushes the connection to this client. Ensures that all events are pushed to the client. **/ void flush(); /** * Creates a new wl_resource for the provided @p interface. * * Thus a convenient wrapper around wl_resource_create * * @param interface * @param version * @param id * @returns the created native wl_resource **/ wl_resource *createResource(const wl_interface *interface, quint32 version, quint32 id); /** * Get the wl_resource associated with the given @p id. * @since 5.3 **/ wl_resource *getResource(quint32 id); /** * @returns the native wl_client this ClientConnection represents. **/ wl_client *client(); /** * @returns The Display this ClientConnection is connected to **/ Display *display(); /** * The pid of the ClientConnection endpoint. * * Please note: if the ClientConnection got created with @link Display::createClient @endlink * the pid will be identical to the process running the KWayland::Server::Display. * * @returns The pid of the connection. **/ pid_t processId() const; /** * The uid of the ClientConnection endpoint. * * Please note: if the ClientConnection got created with @link Display::createClient @endlink * the uid will be identical to the process running the KWayland::Server::Display. * * @returns The uid of the connection. **/ uid_t userId() const; /** * The gid of the ClientConnection endpoint. * * Please note: if the ClientConnection got created with @link Display::createClient @endlink * the gid will be identical to the process running the KWayland::Server::Display. * * @returns The gid of the connection. **/ gid_t groupId() const; /** * The absolute path to the executable. * * Please note: if the ClientConnection got created with @link Display::createClient @endlink * the executablePath will be identical to the process running the KWayland::Server::Display. * * If the executable path cannot be resolved an empty QString is returned. * * @see processId * @since 5.6 **/ QString executablePath() const; /** * Cast operator the native wl_client this ClientConnection represents. **/ operator wl_client*(); /** * Cast operator the native wl_client this ClientConnection represents. **/ operator wl_client*() const; /** * Destroys this ClientConnection. * This is a convenient wrapper around wl_client_destroy. The use case is in combination * with ClientConnections created through @link Display::createClient @endlink. E.g. once * the process for the ClientConnection exited, the ClientConnection needs to be destroyed, too. * @since 5.5 **/ void destroy(); Q_SIGNALS: /** * Signal emitted when the ClientConnection got disconnected from the server. **/ void disconnected(KWayland::Server::ClientConnection*); private: friend class Display; explicit ClientConnection(wl_client *c, Display *parent); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::ClientConnection*) #endif diff --git a/src/server/compositor_interface.cpp b/src/server/compositor_interface.cpp index 6e5baa0..a37468b 100644 --- a/src/server/compositor_interface.cpp +++ b/src/server/compositor_interface.cpp @@ -1,129 +1,115 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "compositor_interface.h" #include "display.h" #include "global_p.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class CompositorInterface::Private : public Global::Private { public: Private(CompositorInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSurface(wl_client *client, wl_resource *resource, uint32_t id); void createRegion(wl_client *client, wl_resource *resource, uint32_t id); static void unbind(wl_resource *resource); static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id); static void createRegionCallback(wl_client *client, wl_resource *resource, uint32_t id); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } CompositorInterface *q; static const struct wl_compositor_interface s_interface; static const quint32 s_version; }; const quint32 CompositorInterface::Private::s_version = 4; CompositorInterface::Private::Private(CompositorInterface *q, Display *d) : Global::Private(d, &wl_compositor_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct wl_compositor_interface CompositorInterface::Private::s_interface = { createSurfaceCallback, createRegionCallback }; #endif CompositorInterface::CompositorInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } CompositorInterface::~CompositorInterface() = default; void CompositorInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_compositor_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void CompositorInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } void CompositorInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->createSurface(client, resource, id); } void CompositorInterface::Private::createSurface(wl_client *client, wl_resource *resource, uint32_t id) { SurfaceInterface *surface = new SurfaceInterface(q, resource); surface->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!surface->resource()) { wl_resource_post_no_memory(resource); delete surface; return; } emit q->surfaceCreated(surface); } void CompositorInterface::Private::createRegionCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->createRegion(client, resource, id); } void CompositorInterface::Private::createRegion(wl_client *client, wl_resource *resource, uint32_t id) { RegionInterface *region = new RegionInterface(q, resource); region->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!region->resource()) { wl_resource_post_no_memory(resource); delete region; return; } emit q->regionCreated(region); } } } diff --git a/src/server/compositor_interface.h b/src/server/compositor_interface.h index 347d3fd..1aa85a0 100644 --- a/src/server/compositor_interface.h +++ b/src/server/compositor_interface.h @@ -1,68 +1,54 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_COMPOSITOR_INTERFACE_H #define WAYLAND_SERVER_COMPOSITOR_INTERFACE_H #include "global.h" #include "region_interface.h" #include "surface_interface.h" #include #include namespace KWayland { namespace Server { class Display; class SurfaceInterface; /** * @brief Represents the Global for wl_compositor interface. * **/ class KWAYLANDSERVER_EXPORT CompositorInterface : public Global { Q_OBJECT public: virtual ~CompositorInterface(); Q_SIGNALS: /** * Emitted whenever this CompositorInterface created a SurfaceInterface. **/ void surfaceCreated(KWayland::Server::SurfaceInterface*); /** * Emitted whenever this CompositorInterface created a RegionInterface. **/ void regionCreated(KWayland::Server::RegionInterface*); private: explicit CompositorInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } #endif diff --git a/src/server/contrast_interface.cpp b/src/server/contrast_interface.cpp index dac0578..f19d575 100644 --- a/src/server/contrast_interface.cpp +++ b/src/server/contrast_interface.cpp @@ -1,270 +1,256 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "contrast_interface.h" #include "region_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include #include namespace KWayland { namespace Server { class ContrastManagerInterface::Private : public Global::Private { public: Private(ContrastManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createContrast(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { auto contrastManager = reinterpret_cast*>(wl_resource_get_user_data(r))->data(); if (contrastManager) { return static_cast(contrastManager->d.data()); } return nullptr; } ContrastManagerInterface *q; static const struct org_kde_kwin_contrast_manager_interface s_interface; static const quint32 s_version; }; const quint32 ContrastManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_contrast_manager_interface ContrastManagerInterface::Private::s_interface = { createCallback, unsetCallback }; #endif ContrastManagerInterface::Private::Private(ContrastManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_contrast_manager_interface, s_version) , q(q) { } void ContrastManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_contrast_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } auto ref = new QPointer(q);//deleted in unbind wl_resource_set_implementation(resource, &s_interface, ref, unbind); } void ContrastManagerInterface::Private::unbind(wl_resource *resource) { delete reinterpret_cast*>(wl_resource_get_user_data(resource)); } void ContrastManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto m = cast(resource); if (!m) { return;// will happen if global is deleted } m->createContrast(client, resource, id, surface); } void ContrastManagerInterface::Private::createContrast(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } ContrastInterface *contrast = new ContrastInterface(q, resource); contrast->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!contrast->resource()) { wl_resource_post_no_memory(resource); delete contrast; return; } s->d_func()->setContrast(QPointer(contrast)); } void ContrastManagerInterface::Private::unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface) { Q_UNUSED(client) Q_UNUSED(resource) SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } s->d_func()->setContrast(QPointer()); } ContrastManagerInterface::ContrastManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } ContrastManagerInterface::~ContrastManagerInterface() = default; class ContrastInterface::Private : public Resource::Private { public: Private(ContrastInterface *q, ContrastManagerInterface *c, wl_resource *parentResource); ~Private(); QRegion pendingRegion; QRegion currentRegion; qreal pendingContrast; qreal currentContrast; qreal pendingIntensity; qreal currentIntensity; qreal pendingSaturation; qreal currentSaturation; private: void commit(); //TODO ContrastInterface *q_func() { return reinterpret_cast(q); } static void commitCallback(wl_client *client, wl_resource *resource); static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static void setContrastCallback(wl_client *client, wl_resource *resource, wl_fixed_t contrast); static void setIntensityCallback(wl_client *client, wl_resource *resource, wl_fixed_t intensity); static void setSaturationCallback(wl_client *client, wl_resource *resource, wl_fixed_t saturation); static const struct org_kde_kwin_contrast_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_contrast_interface ContrastInterface::Private::s_interface = { commitCallback, setRegionCallback, setContrastCallback, setIntensityCallback, setSaturationCallback, resourceDestroyedCallback }; #endif void ContrastInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->commit(); } void ContrastInterface::Private::commit() { currentRegion = pendingRegion; currentContrast = pendingContrast; currentIntensity = pendingIntensity; currentSaturation = pendingSaturation; } void ContrastInterface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { Q_UNUSED(client) Private *p = cast(resource); RegionInterface *r = RegionInterface::get(region); if (r) { p->pendingRegion = r->region(); } else { p->pendingRegion = QRegion(); } } void ContrastInterface::Private::setContrastCallback(wl_client *client, wl_resource *resource, wl_fixed_t contrast) { Q_UNUSED(client) Private *p = cast(resource); p->pendingContrast = wl_fixed_to_double(contrast); } void ContrastInterface::Private::setIntensityCallback(wl_client *client, wl_resource *resource, wl_fixed_t intensity) { Q_UNUSED(client) Private *p = cast(resource); p->pendingIntensity = wl_fixed_to_double(intensity); } void ContrastInterface::Private::setSaturationCallback(wl_client *client, wl_resource *resource, wl_fixed_t saturation) { Q_UNUSED(client) Private *p = cast(resource); p->pendingSaturation = wl_fixed_to_double(saturation); } ContrastInterface::Private::Private(ContrastInterface *q, ContrastManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_contrast_interface, &s_interface) { } ContrastInterface::Private::~Private() = default; ContrastInterface::ContrastInterface(ContrastManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } ContrastInterface::~ContrastInterface() = default; QRegion ContrastInterface::region() const { Q_D(); return d->currentRegion; } qreal ContrastInterface::contrast() const { Q_D(); return d->currentContrast; } qreal ContrastInterface::intensity() const { Q_D(); return d->currentIntensity; } qreal ContrastInterface::saturation() const { Q_D(); return d->currentSaturation; } ContrastInterface::Private *ContrastInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/contrast_interface.h b/src/server/contrast_interface.h index 3deaf4b..d65d769 100644 --- a/src/server/contrast_interface.h +++ b/src/server/contrast_interface.h @@ -1,94 +1,80 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin -Copyright 2015 Marco Martin +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Marco Martin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_CONTRAST_INTERFACE_H #define KWAYLAND_SERVER_CONTRAST_INTERFACE_H #include "global.h" #include "resource.h" #include #include namespace KWayland { namespace Server { class Display; /** * @brief Represents the Global for org_kde_kwin_contrast_manager interface. * * This class creates ContrastInterfaces and attaches them to SurfaceInterfaces. * * @see ContrastInterface * @see SurfaceInterface * @since 5.5 **/ class KWAYLANDSERVER_EXPORT ContrastManagerInterface : public Global { Q_OBJECT public: virtual ~ContrastManagerInterface(); private: explicit ContrastManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * @brief Represents the Resource for the org_kde_kwin_contrast interface. * * Instances of this class are only generated by the ContrastManagerInterface. * The ContrastInterface gets attached to a SurfaceInterface and can be assessed * from there using @link SurfaceInterface::contrast() @endlink. Please note that * the ContrastInterface is only available on the SurfaceInterface after it has been * committed. * * @see ContrastManagerInterface * @see SurfaceInterface * @since 5.5 **/ class KWAYLANDSERVER_EXPORT ContrastInterface : public Resource { Q_OBJECT public: virtual ~ContrastInterface(); QRegion region() const; qreal contrast() const; qreal intensity() const; qreal saturation() const; private: explicit ContrastInterface(ContrastManagerInterface *parent, wl_resource *parentResource); friend class ContrastManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/datadevice_interface.cpp b/src/server/datadevice_interface.cpp index 482b8c2..c0be27f 100644 --- a/src/server/datadevice_interface.cpp +++ b/src/server/datadevice_interface.cpp @@ -1,399 +1,385 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datadevice_interface.h" #include "datadevicemanager_interface.h" #include "dataoffer_interface_p.h" #include "datasource_interface.h" #include "display.h" #include "resource_p.h" #include "pointer_interface.h" #include "seat_interface.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class DataDeviceInterface::Private : public Resource::Private { public: Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource); ~Private(); DataOfferInterface *createDataOffer(DataSourceInterface *source); SeatInterface *seat; DataSourceInterface *source = nullptr; SurfaceInterface *surface = nullptr; SurfaceInterface *icon = nullptr; DataSourceInterface *selection = nullptr; QMetaObject::Connection selectionUnboundConnection; QMetaObject::Connection selectionDestroyedConnection; struct Drag { SurfaceInterface *surface = nullptr; QMetaObject::Connection destroyConnection; QMetaObject::Connection posConnection; QMetaObject::Connection sourceActionConnection; QMetaObject::Connection targetActionConnection; quint32 serial = 0; }; Drag drag; QPointer proxyRemoteSurface; private: DataDeviceInterface *q_func() { return reinterpret_cast(q); } void startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *icon, quint32 serial); void setSelection(DataSourceInterface *dataSource); static void startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial); static void setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial); static const struct wl_data_device_interface s_interface; }; #ifndef K_DOXYGEN const struct wl_data_device_interface DataDeviceInterface::Private::s_interface = { startDragCallback, setSelectionCallback, resourceDestroyedCallback }; #endif DataDeviceInterface::Private::Private(SeatInterface *seat, DataDeviceInterface *q, DataDeviceManagerInterface *manager, wl_resource *parentResource) : Resource::Private(q, manager, parentResource, &wl_data_device_interface, &s_interface) , seat(seat) { } DataDeviceInterface::Private::~Private() = default; void DataDeviceInterface::Private::startDragCallback(wl_client *client, wl_resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) { Q_UNUSED(client) Q_UNUSED(serial) // TODO: verify serial cast(resource)->startDrag(DataSourceInterface::get(source), SurfaceInterface::get(origin), SurfaceInterface::get(icon), serial); } void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial) { SurfaceInterface *focusSurface = origin; if (proxyRemoteSurface) { // origin is a proxy surface focusSurface = proxyRemoteSurface.data(); } const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface; if (!pointerGrab) { // Client doesn't have pointer grab. const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface; if (!touchGrab) { // Client neither has pointer nor touch grab. No drag start allowed. return; } } // TODO: source is allowed to be null, handled client internally! Q_Q(DataDeviceInterface); source = dataSource; if (dataSource) { QObject::connect(dataSource, &Resource::aboutToBeUnbound, q, [this] { source = nullptr; }); } surface = origin; icon = i; drag.serial = serial; emit q->dragStarted(); } void DataDeviceInterface::Private::setSelectionCallback(wl_client *client, wl_resource *resource, wl_resource *source, uint32_t serial) { Q_UNUSED(client) Q_UNUSED(serial) // TODO: verify serial cast(resource)->setSelection(DataSourceInterface::get(source)); } void DataDeviceInterface::Private::setSelection(DataSourceInterface *dataSource) { if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) { wl_resource_post_error(dataSource->resource(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop"); return; } if (selection == dataSource) { return; } Q_Q(DataDeviceInterface); QObject::disconnect(selectionUnboundConnection); QObject::disconnect(selectionDestroyedConnection); if (selection) { selection->cancel(); } selection = dataSource; if (selection) { auto clearSelection = [this] { setSelection(nullptr); }; selectionUnboundConnection = QObject::connect(selection, &Resource::unbound, q, clearSelection); selectionDestroyedConnection = QObject::connect(selection, &QObject::destroyed, q, clearSelection); emit q->selectionChanged(selection); } else { selectionUnboundConnection = QMetaObject::Connection(); selectionDestroyedConnection = QMetaObject::Connection(); emit q->selectionCleared(); } } DataOfferInterface *DataDeviceInterface::Private::createDataOffer(DataSourceInterface *source) { if (!resource) { return nullptr; } if (!source) { // a data offer can only exist together with a source return nullptr; } Q_Q(DataDeviceInterface); DataOfferInterface *offer = new DataOfferInterface(source, q, resource); auto c = q->global()->display()->getConnection(wl_resource_get_client(resource)); offer->create(c, wl_resource_get_version(resource), 0); if (!offer->resource()) { // TODO: send error? delete offer; return nullptr; } wl_data_device_send_data_offer(resource, offer->resource()); offer->sendAllOffers(); return offer; } DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(seat, this, parent, parentResource)) { } DataDeviceInterface::~DataDeviceInterface() = default; SeatInterface *DataDeviceInterface::seat() const { Q_D(); return d->seat; } DataSourceInterface *DataDeviceInterface::dragSource() const { Q_D(); return d->source; } SurfaceInterface *DataDeviceInterface::icon() const { Q_D(); return d->icon; } SurfaceInterface *DataDeviceInterface::origin() const { Q_D(); return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface; } DataSourceInterface *DataDeviceInterface::selection() const { Q_D(); return d->selection; } void DataDeviceInterface::sendSelection(DataDeviceInterface *other) { Q_D(); auto otherSelection = other->selection(); if (!otherSelection) { sendClearSelection(); return; } auto r = d->createDataOffer(otherSelection); if (!r) { return; } if (!d->resource) { return; } wl_data_device_send_selection(d->resource, r->resource()); } void DataDeviceInterface::sendClearSelection() { Q_D(); if (!d->resource) { return; } wl_data_device_send_selection(d->resource, nullptr); } void DataDeviceInterface::drop() { Q_D(); if (!d->resource) { return; } wl_data_device_send_drop(d->resource); if (d->drag.posConnection) { disconnect(d->drag.posConnection); d->drag.posConnection = QMetaObject::Connection(); } disconnect(d->drag.destroyConnection); d->drag.destroyConnection = QMetaObject::Connection(); d->drag.surface = nullptr; client()->flush(); } void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial) { Q_D(); if (d->drag.surface) { if (d->resource && d->drag.surface->resource()) { wl_data_device_send_leave(d->resource); } if (d->drag.posConnection) { disconnect(d->drag.posConnection); d->drag.posConnection = QMetaObject::Connection(); } disconnect(d->drag.destroyConnection); d->drag.destroyConnection = QMetaObject::Connection(); d->drag.surface = nullptr; if (d->drag.sourceActionConnection) { disconnect(d->drag.sourceActionConnection); d->drag.sourceActionConnection = QMetaObject::Connection(); } if (d->drag.targetActionConnection) { disconnect(d->drag.targetActionConnection); d->drag.targetActionConnection = QMetaObject::Connection(); } // don't update serial, we need it } if (!surface) { if (auto s = d->seat->dragSource()->dragSource()) { s->dndAction(DataDeviceManagerInterface::DnDAction::None); } return; } if (d->proxyRemoteSurface && d->proxyRemoteSurface == surface) { // A proxy can not have the remote surface as target. // TODO: do this for all client's surfaces? return; } auto *source = d->seat->dragSource()->dragSource(); DataOfferInterface *offer = d->createDataOffer(source); d->drag.surface = surface; if (d->seat->isDragPointer()) { d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); wl_data_device_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); client()->flush(); } ); } else if (d->seat->isDragTouch()) { d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, [this](qint32 id, quint32 serial, const QPointF &globalPosition) { Q_D(); Q_UNUSED(id); if (serial != d->drag.serial) { // different touch down has been moved return; } const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition); wl_data_device_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); client()->flush(); } ); } d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] { Q_D(); if (d->resource) { wl_data_device_send_leave(d->resource); } if (d->drag.posConnection) { disconnect(d->drag.posConnection); } d->drag = Private::Drag(); } ); // TODO: handle touch position const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); wl_data_device_send_enter(d->resource, serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr); if (offer) { offer->d_func()->sendSourceActions(); auto matchOffers = [source, offer] { DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None}; if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) { action = offer->preferredDragAndDropAction(); } else { if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) { action = DataDeviceManagerInterface::DnDAction::Copy; } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) { action = DataDeviceManagerInterface::DnDAction::Move; } else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask) && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) { action = DataDeviceManagerInterface::DnDAction::Ask; } } offer->dndAction(action); source->dndAction(action); }; d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, offer, matchOffers); d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers); } d->client->flush(); } quint32 DataDeviceInterface::dragImplicitGrabSerial() const { Q_D(); return d->drag.serial; } void DataDeviceInterface::updateProxy(SurfaceInterface *remote) { Q_D(); // TODO: connect destroy signal? d->proxyRemoteSurface = remote; } DataDeviceInterface::Private *DataDeviceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/datadevice_interface.h b/src/server/datadevice_interface.h index ca12f3b..de1383f 100644 --- a/src/server/datadevice_interface.h +++ b/src/server/datadevice_interface.h @@ -1,114 +1,100 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DATA_DEVICE_INTERFACE_H #define WAYLAND_SERVER_DATA_DEVICE_INTERFACE_H #include #include #include "resource.h" namespace KWayland { namespace Server { class DataDeviceManagerInterface; class DataOfferInterface; class DataSourceInterface; class SeatInterface; class SurfaceInterface; /** * @brief DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop. * * The data device is per seat. * Copy-and-paste use the selection functions. * * Represents the Resource for the wl_data_device interface. * * @see SeatInterface * @see DataSourceInterface **/ class KWAYLANDSERVER_EXPORT DataDeviceInterface : public Resource { Q_OBJECT public: virtual ~DataDeviceInterface(); SeatInterface *seat() const; DataSourceInterface *dragSource() const; SurfaceInterface *origin() const; SurfaceInterface *icon() const; /** * @returns the serial of the implicit grab which started the drag * @since 5.6 **/ quint32 dragImplicitGrabSerial() const; DataSourceInterface *selection() const; void sendSelection(DataDeviceInterface *other); void sendClearSelection(); /** * The event is sent when a drag-and-drop operation is ended because the implicit grab is removed. * @since 5.6 **/ void drop(); /** * Updates the SurfaceInterface to which drag motion events are sent. * * If a SurfaceInterface was registered in this DataDeviceInterface for drag motion events, it * will be sent a leave event. * * If @p surface is not null it will be sent a drag enter event. * * @param surface The SurfaceInterface which gets motion events * @param serial The serial to be used for enter/leave * @since 5.6 **/ void updateDragTarget(SurfaceInterface *surface, quint32 serial); /** * Mark this DataDeviceInterface as being a proxy device for @p remote. * @since 5.56 **/ void updateProxy(SurfaceInterface *remote); Q_SIGNALS: void dragStarted(); void selectionChanged(KWayland::Server::DataSourceInterface*); void selectionCleared(); private: friend class DataDeviceManagerInterface; explicit DataDeviceInterface(SeatInterface *seat, DataDeviceManagerInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::DataDeviceInterface*) #endif diff --git a/src/server/datadevicemanager_interface.cpp b/src/server/datadevicemanager_interface.cpp index 2ff96db..f57dda0 100644 --- a/src/server/datadevicemanager_interface.cpp +++ b/src/server/datadevicemanager_interface.cpp @@ -1,135 +1,121 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datadevicemanager_interface.h" #include "datasource_interface.h" #include "global_p.h" #include "display.h" #include "seat_interface_p.h" // Wayland #include namespace KWayland { namespace Server { class DataDeviceManagerInterface::Private : public Global::Private { public: Private(DataDeviceManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createDataSource(wl_client *client, wl_resource *resource, uint32_t id); void getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); static void unbind(wl_resource *resource); static void createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } DataDeviceManagerInterface *q; static const struct wl_data_device_manager_interface s_interface; static const quint32 s_version; static const qint32 s_dataDeviceVersion; static const qint32 s_dataSourceVersion; }; const quint32 DataDeviceManagerInterface::Private::s_version = 3; const qint32 DataDeviceManagerInterface::Private::s_dataDeviceVersion = 3; const qint32 DataDeviceManagerInterface::Private::s_dataSourceVersion = 3; #ifndef K_DOXYGEN const struct wl_data_device_manager_interface DataDeviceManagerInterface::Private::s_interface = { createDataSourceCallback, getDataDeviceCallback }; #endif DataDeviceManagerInterface::Private::Private(DataDeviceManagerInterface *q, Display *d) : Global::Private(d, &wl_data_device_manager_interface, s_version) , q(q) { } void DataDeviceManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_data_device_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void DataDeviceManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } void DataDeviceManagerInterface::Private::createDataSourceCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->createDataSource(client, resource, id); } void DataDeviceManagerInterface::Private::createDataSource(wl_client *client, wl_resource *resource, uint32_t id) { DataSourceInterface *dataSource = new DataSourceInterface(q, resource); dataSource->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataSourceVersion) , id); if (!dataSource->resource()) { wl_resource_post_no_memory(resource); delete dataSource; return; } emit q->dataSourceCreated(dataSource); } void DataDeviceManagerInterface::Private::getDataDeviceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) { cast(resource)->getDataDevice(client, resource, id, seat); } void DataDeviceManagerInterface::Private::getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat) { SeatInterface *s = SeatInterface::get(seat); Q_ASSERT(s); DataDeviceInterface *dataDevice = new DataDeviceInterface(s, q, resource); dataDevice->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_dataDeviceVersion), id); if (!dataDevice->resource()) { wl_resource_post_no_memory(resource); return; } s->d_func()->registerDataDevice(dataDevice); emit q->dataDeviceCreated(dataDevice); } DataDeviceManagerInterface::DataDeviceManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } DataDeviceManagerInterface::~DataDeviceManagerInterface() = default; } } diff --git a/src/server/datadevicemanager_interface.h b/src/server/datadevicemanager_interface.h index bc8d3c5..74cc932 100644 --- a/src/server/datadevicemanager_interface.h +++ b/src/server/datadevicemanager_interface.h @@ -1,74 +1,60 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H #define WAYLAND_SERVER_DATA_DEVICE_MANAGER_INTERFACE_H #include #include #include "global.h" #include "datadevice_interface.h" namespace KWayland { namespace Server { class Display; class DataSourceInterface; /** * @brief Represents the Global for wl_data_device_manager interface. * **/ class KWAYLANDSERVER_EXPORT DataDeviceManagerInterface : public Global { Q_OBJECT public: virtual ~DataDeviceManagerInterface(); /** * Drag and Drop actions supported by the DataSourceInterface. * @since 5.XX **/ enum class DnDAction { None = 0, Copy = 1 << 0, Move = 1 << 1, Ask = 1 << 2 }; Q_DECLARE_FLAGS(DnDActions, DnDAction) Q_SIGNALS: void dataSourceCreated(KWayland::Server::DataSourceInterface*); void dataDeviceCreated(KWayland::Server::DataDeviceInterface*); private: explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::DataDeviceManagerInterface::DnDActions) #endif diff --git a/src/server/dataoffer_interface.cpp b/src/server/dataoffer_interface.cpp index c033430..7b23715 100644 --- a/src/server/dataoffer_interface.cpp +++ b/src/server/dataoffer_interface.cpp @@ -1,222 +1,208 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "dataoffer_interface_p.h" #include "datadevice_interface.h" #include "datasource_interface.h" // Qt #include // Wayland #include // system #include namespace KWayland { namespace Server { #ifndef K_DOXYGEN const struct wl_data_offer_interface DataOfferInterface::Private::s_interface = { acceptCallback, receiveCallback, resourceDestroyedCallback, finishCallback, setActionsCallback }; #endif DataOfferInterface::Private::Private(DataSourceInterface *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource) : Resource::Private(q, nullptr, parentResource, &wl_data_offer_interface, &s_interface) , source(source) , dataDevice(parentInterface) { // TODO: connect to new selections } DataOfferInterface::Private::~Private() = default; void DataOfferInterface::Private::acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType) { Q_UNUSED(client) Q_UNUSED(serial) auto p = cast(resource); if (!p->source) { return; } p->source->accept(mimeType ? QString::fromUtf8(mimeType) : QString()); } void DataOfferInterface::Private::receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd) { Q_UNUSED(client) cast(resource)->receive(QString::fromUtf8(mimeType), fd); } void DataOfferInterface::Private::receive(const QString &mimeType, qint32 fd) { if (!source) { close(fd); return; } source->requestData(mimeType, fd); } void DataOfferInterface::Private::finishCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) auto p = cast(resource); if (!p->source) { return; } p->source->dndFinished(); // TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one } void DataOfferInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action) { // TODO: check it's drag and drop, otherwise send error Q_UNUSED(client) DataDeviceManagerInterface::DnDActions supportedActions; if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { supportedActions |= DataDeviceManagerInterface::DnDAction::Copy; } if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { supportedActions |= DataDeviceManagerInterface::DnDAction::Move; } if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { supportedActions |= DataDeviceManagerInterface::DnDAction::Ask; } // verify that the no other actions are sent if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) { wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, "Invalid action mask"); return; } if (preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY && preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE && preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK && preferred_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) { wl_resource_post_error(resource, WL_DATA_OFFER_ERROR_INVALID_ACTION, "Invalid preferred action"); return; } DataDeviceManagerInterface::DnDAction preferredAction = DataDeviceManagerInterface::DnDAction::None; if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { preferredAction = DataDeviceManagerInterface::DnDAction::Copy; } else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { preferredAction = DataDeviceManagerInterface::DnDAction::Move; } else if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { preferredAction = DataDeviceManagerInterface::DnDAction::Ask; } auto p = cast(resource); p->supportedDnDActions = supportedActions; p->preferredDnDAction = preferredAction; emit p->q_func()->dragAndDropActionsChanged(); } void DataOfferInterface::Private::sendSourceActions() { if (!source) { return; } if (wl_resource_get_version(resource) < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { return; } uint32_t wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; const auto actions = source->supportedDragAndDropActions(); if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Copy)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Move)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; } if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Ask)) { wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } wl_data_offer_send_source_actions(resource, wlActions); } DataOfferInterface::DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource) : Resource(new Private(source, parentInterface, this, parentResource)) { Q_ASSERT(source); connect(source, &DataSourceInterface::mimeTypeOffered, this, [this](const QString &mimeType) { Q_D(); if (!d->resource) { return; } wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData()); } ); QObject::connect(source, &QObject::destroyed, this, [this] { Q_D(); d->source = nullptr; } ); } DataOfferInterface::~DataOfferInterface() = default; void DataOfferInterface::sendAllOffers() { Q_D(); for (const QString &mimeType : d->source->mimeTypes()) { wl_data_offer_send_offer(d->resource, mimeType.toUtf8().constData()); } } DataOfferInterface::Private *DataOfferInterface::d_func() const { return reinterpret_cast(d.data()); } DataDeviceManagerInterface::DnDActions DataOfferInterface::supportedDragAndDropActions() const { Q_D(); return d->supportedDnDActions; } DataDeviceManagerInterface::DnDAction DataOfferInterface::preferredDragAndDropAction() const { Q_D(); return d->preferredDnDAction; } void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action) { Q_D(); if (wl_resource_get_version(d->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) { return; } uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; if (action == DataDeviceManagerInterface::DnDAction::Copy) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } else if (action == DataDeviceManagerInterface::DnDAction::Move ) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; } else if (action == DataDeviceManagerInterface::DnDAction::Ask) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } wl_data_offer_send_action(d->resource, wlAction); } } } diff --git a/src/server/dataoffer_interface.h b/src/server/dataoffer_interface.h index 8e415d9..80aa13a 100644 --- a/src/server/dataoffer_interface.h +++ b/src/server/dataoffer_interface.h @@ -1,89 +1,75 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DATA_OFFER_INTERFACE_H #define WAYLAND_SERVER_DATA_OFFER_INTERFACE_H #include #include #include "resource.h" #include "datadevicemanager_interface.h" namespace KWayland { namespace Server { class DataDeviceInterface; class DataSourceInterface; /** * @brief Represents the Resource for the wl_data_offer interface. * **/ class KWAYLANDSERVER_EXPORT DataOfferInterface : public Resource { Q_OBJECT public: virtual ~DataOfferInterface(); void sendAllOffers(); /** * @returns The Drag and Drop actions supported by this DataOfferInterface. * @since 5.42 **/ DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const; /** * @returns The preferred Drag and Drop action of this DataOfferInterface. * @since 5.42 **/ DataDeviceManagerInterface::DnDAction preferredDragAndDropAction() const; /** * This event indicates the @p action selected by the compositor after matching the * source/destination side actions. Only one action (or none) will be offered here. * @since 5.42 **/ void dndAction(DataDeviceManagerInterface::DnDAction action); Q_SIGNALS: /** * Emitted whenever the supported or preferred Drag and Drop actions changed. * @since 5.42 **/ void dragAndDropActionsChanged(); private: friend class DataDeviceInterface; explicit DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::DataOfferInterface*) #endif diff --git a/src/server/dataoffer_interface_p.h b/src/server/dataoffer_interface_p.h index e66c315..dec5c84 100644 --- a/src/server/dataoffer_interface_p.h +++ b/src/server/dataoffer_interface_p.h @@ -1,61 +1,47 @@ -/******************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H #define KWAYLAND_SERVER_DATAOFFERINTERFACE_P_H #include "dataoffer_interface.h" #include "datasource_interface.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN DataOfferInterface::Private : public Resource::Private { public: Private(DataSourceInterface *source, DataDeviceInterface *parentInterface, DataOfferInterface *q, wl_resource *parentResource); ~Private(); DataSourceInterface *source; DataDeviceInterface *dataDevice; // defaults are set to sensible values for < version 3 interfaces DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move; DataDeviceManagerInterface::DnDAction preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy; void sendSourceActions(); private: DataOfferInterface *q_func() { return reinterpret_cast(q); } void receive(const QString &mimeType, qint32 fd); static void acceptCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *mimeType); static void receiveCallback(wl_client *client, wl_resource *resource, const char *mimeType, int32_t fd); static void finishCallback(wl_client *client, wl_resource *resource); static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action); static const struct wl_data_offer_interface s_interface; }; } } #endif diff --git a/src/server/datasource_interface.cpp b/src/server/datasource_interface.cpp index dd594c5..aeb63dc 100644 --- a/src/server/datasource_interface.cpp +++ b/src/server/datasource_interface.cpp @@ -1,206 +1,192 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "datasource_interface.h" #include "datadevicemanager_interface.h" #include "clientconnection.h" #include "resource_p.h" // Qt #include // Wayland #include // system #include namespace KWayland { namespace Server { class DataSourceInterface::Private : public Resource::Private { public: Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource); ~Private(); QStringList mimeTypes; DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None; private: DataSourceInterface *q_func() { return reinterpret_cast(q); } void offer(const QString &mimeType); static void offerCallback(wl_client *client, wl_resource *resource, const char *mimeType); static void setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions); const static struct wl_data_source_interface s_interface; }; #ifndef K_DOXYGEN const struct wl_data_source_interface DataSourceInterface::Private::s_interface = { offerCallback, resourceDestroyedCallback, setActionsCallback }; #endif DataSourceInterface::Private::Private(DataSourceInterface *q, DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource::Private(q, parent, parentResource, &wl_data_source_interface, &s_interface) { } DataSourceInterface::Private::~Private() = default; void DataSourceInterface::Private::offerCallback(wl_client *client, wl_resource *resource, const char *mimeType) { Q_UNUSED(client) cast(resource)->offer(QString::fromUtf8(mimeType)); } void DataSourceInterface::Private::offer(const QString &mimeType) { mimeTypes << mimeType; Q_Q(DataSourceInterface); emit q->mimeTypeOffered(mimeType); } void DataSourceInterface::Private::setActionsCallback(wl_client *client, wl_resource *resource, uint32_t dnd_actions) { Q_UNUSED(client) DataDeviceManagerInterface::DnDActions supportedActions; if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { supportedActions |= DataDeviceManagerInterface::DnDAction::Copy; } if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { supportedActions |= DataDeviceManagerInterface::DnDAction::Move; } if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { supportedActions |= DataDeviceManagerInterface::DnDAction::Ask; } // verify that the no other actions are sent if (dnd_actions & ~(WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)) { wl_resource_post_error(resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "Invalid action mask"); return; } auto p = cast(resource); if (p->supportedDnDActions!= supportedActions) { p->supportedDnDActions = supportedActions; emit p->q_func()->supportedDragAndDropActionsChanged(); } } DataSourceInterface::DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { if (wl_resource_get_version(parentResource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) { Q_D(); d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy; } } DataSourceInterface::~DataSourceInterface() = default; void DataSourceInterface::accept(const QString &mimeType) { Q_D(); // TODO: does this require a sanity check on the possible mimeType? wl_data_source_send_target(d->resource, mimeType.isEmpty() ? nullptr : mimeType.toUtf8().constData()); } void DataSourceInterface::requestData(const QString &mimeType, qint32 fd) { Q_D(); // TODO: does this require a sanity check on the possible mimeType? if (d->resource) { wl_data_source_send_send(d->resource, mimeType.toUtf8().constData(), int32_t(fd)); } close(fd); } void DataSourceInterface::cancel() { Q_D(); if (!d->resource) { return; } wl_data_source_send_cancelled(d->resource); client()->flush(); } QStringList DataSourceInterface::mimeTypes() const { Q_D(); return d->mimeTypes; } DataSourceInterface *DataSourceInterface::get(wl_resource *native) { return Private::get(native); } DataSourceInterface::Private *DataSourceInterface::d_func() const { return reinterpret_cast(d.data()); } DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const { Q_D(); return d->supportedDnDActions; } void DataSourceInterface::dropPerformed() { Q_D(); if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { return; } wl_data_source_send_dnd_drop_performed(d->resource); } void DataSourceInterface::dndFinished() { Q_D(); if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { return; } wl_data_source_send_dnd_finished(d->resource); } void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action) { Q_D(); if (wl_resource_get_version(d->resource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) { return; } uint32_t wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; if (action == DataDeviceManagerInterface::DnDAction::Copy) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } else if (action == DataDeviceManagerInterface::DnDAction::Move ) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; } else if (action == DataDeviceManagerInterface::DnDAction::Ask) { wlAction = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } wl_data_source_send_action(d->resource, wlAction); } } } diff --git a/src/server/datasource_interface.h b/src/server/datasource_interface.h index 48c1340..2ad853d 100644 --- a/src/server/datasource_interface.h +++ b/src/server/datasource_interface.h @@ -1,96 +1,82 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H #define WAYLAND_SERVER_DATA_SOURCE_INTERFACE_H #include #include #include "resource.h" #include "datadevicemanager_interface.h" namespace KWayland { namespace Server { /** * @brief Represents the Resource for the wl_data_source interface. **/ class KWAYLANDSERVER_EXPORT DataSourceInterface : public Resource { Q_OBJECT public: virtual ~DataSourceInterface(); void accept(const QString &mimeType); void requestData(const QString &mimeType, qint32 fd); void cancel(); QStringList mimeTypes() const; static DataSourceInterface *get(wl_resource *native); /** * @returns The Drag and Drop actions supported by this DataSourceInterface. * @since 5.42 **/ DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const; /** * The user performed the drop action during a drag and drop operation. * @since 5.42 **/ void dropPerformed(); /** * The drop destination finished interoperating with this data source. * @since 5.42 **/ void dndFinished(); /** * This event indicates the @p action selected by the compositor after matching the * source/destination side actions. Only one action (or none) will be offered here. * @since 5.42 **/ void dndAction(DataDeviceManagerInterface::DnDAction action); Q_SIGNALS: void mimeTypeOffered(const QString&); /** * Emitted whenever this DataSourceInterface changes the supported drag and drop actions * @since 5.42 **/ void supportedDragAndDropActionsChanged(); private: friend class DataDeviceManagerInterface; explicit DataSourceInterface(DataDeviceManagerInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::DataSourceInterface*) #endif diff --git a/src/server/display.cpp b/src/server/display.cpp index 5a39f65..1d168cb 100644 --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -1,656 +1,642 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "display.h" #include "compositor_interface.h" #include "datadevicemanager_interface.h" #include "dpms_interface.h" #include "outputconfiguration_interface.h" #include "outputmanagement_interface.h" #include "outputdevice_interface.h" #include "idle_interface.h" #include "idleinhibit_interface_p.h" #include "remote_access_interface.h" #include "fakeinput_interface.h" #include "logging.h" #include "output_interface.h" #include "plasmashell_interface.h" #include "plasmawindowmanagement_interface.h" #include "pointerconstraints_interface_p.h" #include "pointergestures_interface_p.h" #include "qtsurfaceextension_interface.h" #include "seat_interface.h" #include "shadow_interface.h" #include "blur_interface.h" #include "contrast_interface.h" #include "relativepointer_interface_p.h" #include "server_decoration_interface.h" #include "slide_interface.h" #include "shell_interface.h" #include "subcompositor_interface.h" #include "textinput_interface_p.h" #include "xdgshell_v5_interface_p.h" #include "xdgforeign_interface.h" #include "xdgshell_v6_interface_p.h" #include "xdgshell_stable_interface_p.h" #include "appmenu_interface.h" #include "server_decoration_palette_interface.h" #include "plasmavirtualdesktop_interface.h" #include "xdgoutput_interface.h" #include "xdgdecoration_interface.h" #include "eglstream_controller_interface.h" #include "keystate_interface.h" #include "linuxdmabuf_v1_interface.h" #include #include #include #include #include #include #include namespace KWayland { namespace Server { class Display::Private { public: Private(Display *q); void flush(); void dispatch(); void setRunning(bool running); void installSocketNotifier(); wl_display *display = nullptr; wl_event_loop *loop = nullptr; QString socketName = QStringLiteral("wayland-0"); bool running = false; bool automaticSocketNaming = false; QList outputs; QList outputdevices; QVector seats; QVector clients; EGLDisplay eglDisplay = EGL_NO_DISPLAY; private: Display *q; }; Display::Private::Private(Display *q) : q(q) { } void Display::Private::installSocketNotifier() { if (!QThread::currentThread()) { return; } int fd = wl_event_loop_get_fd(loop); if (fd == -1) { qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop"; return; } QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); QObject::connect(m_notifier, &QSocketNotifier::activated, q, [this] { dispatch(); } ); QObject::connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, q, [this] { flush(); }); setRunning(true); } Display::Display(QObject *parent) : QObject(parent) , d(new Private(this)) { } Display::~Display() { terminate(); if (d->display) { wl_display_destroy(d->display); } } void Display::Private::flush() { if (!display || !loop) { return; } wl_display_flush_clients(display); } void Display::Private::dispatch() { if (!display || !loop) { return; } if (wl_event_loop_dispatch(loop, 0) != 0) { qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop"; } } void Display::setSocketName(const QString &name) { if (d->socketName == name) { return; } d->socketName = name; emit socketNameChanged(d->socketName); } QString Display::socketName() const { return d->socketName; } void Display::setAutomaticSocketNaming(bool automaticSocketNaming) { if (d->automaticSocketNaming == automaticSocketNaming) { return; } d->automaticSocketNaming = automaticSocketNaming; emit automaticSocketNamingChanged(automaticSocketNaming); } bool Display::automaticSocketNaming() const { return d->automaticSocketNaming; } void Display::start(StartMode mode) { Q_ASSERT(!d->running); Q_ASSERT(!d->display); d->display = wl_display_create(); if (mode == StartMode::ConnectToSocket) { if (d->automaticSocketNaming) { const char *socket = wl_display_add_socket_auto(d->display); if (socket == nullptr) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } const QString newEffectiveSocketName = QString::fromUtf8(socket); if (d->socketName != newEffectiveSocketName) { d->socketName = newEffectiveSocketName; emit socketNameChanged(d->socketName); } } else if (wl_display_add_socket(d->display, qPrintable(d->socketName)) != 0) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } } d->loop = wl_display_get_event_loop(d->display); d->installSocketNotifier(); } void Display::startLoop() { Q_ASSERT(!d->running); Q_ASSERT(d->display); d->installSocketNotifier(); } void Display::dispatchEvents(int msecTimeout) { Q_ASSERT(d->display); if (d->running) { d->dispatch(); } else if (d->loop) { wl_event_loop_dispatch(d->loop, msecTimeout); wl_display_flush_clients(d->display); } } void Display::terminate() { if (!d->running) { return; } emit aboutToTerminate(); wl_display_terminate(d->display); wl_display_destroy(d->display); d->display = nullptr; d->loop = nullptr; d->setRunning(false); } void Display::Private::setRunning(bool r) { Q_ASSERT(running != r); running = r; emit q->runningChanged(running); } OutputInterface *Display::createOutput(QObject *parent) { OutputInterface *output = new OutputInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputs.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); }); d->outputs << output; return output; } CompositorInterface *Display::createCompositor(QObject *parent) { CompositorInterface *compositor = new CompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; }); return compositor; } ShellInterface *Display::createShell(QObject *parent) { ShellInterface *shell = new ShellInterface(this, parent); connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; }); return shell; } OutputDeviceInterface *Display::createOutputDevice(QObject *parent) { OutputDeviceInterface *output = new OutputDeviceInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputdevices.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutputDevice(output); }); d->outputdevices << output; return output; } OutputManagementInterface *Display::createOutputManagement(QObject *parent) { OutputManagementInterface *om = new OutputManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, om, [this,om] { delete om; }); return om; } SeatInterface *Display::createSeat(QObject *parent) { SeatInterface *seat = new SeatInterface(this, parent); connect(seat, &QObject::destroyed, this, [this, seat] { d->seats.removeAll(seat); }); connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; }); d->seats << seat; return seat; } SubCompositorInterface *Display::createSubCompositor(QObject *parent) { auto c = new SubCompositorInterface(this, parent); connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; }); return c; } DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent) { auto m = new DataDeviceManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; }); return m; } PlasmaShellInterface *Display::createPlasmaShell(QObject* parent) { auto s = new PlasmaShellInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } PlasmaWindowManagementInterface *Display::createPlasmaWindowManagement(QObject *parent) { auto wm = new PlasmaWindowManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, wm, [this, wm] { delete wm; }); return wm; } QtSurfaceExtensionInterface *Display::createQtSurfaceExtension(QObject *parent) { auto s = new QtSurfaceExtensionInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } RemoteAccessManagerInterface *Display::createRemoteAccessManager(QObject *parent) { auto i = new RemoteAccessManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } IdleInterface *Display::createIdle(QObject *parent) { auto i = new IdleInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } FakeInputInterface *Display::createFakeInput(QObject *parent) { auto i = new FakeInputInterface(this, parent); connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); return i; } ShadowManagerInterface *Display::createShadowManager(QObject *parent) { auto s = new ShadowManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); return s; } BlurManagerInterface *Display::createBlurManager(QObject *parent) { auto b = new BlurManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } ContrastManagerInterface *Display::createContrastManager(QObject *parent) { auto b = new ContrastManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } SlideManagerInterface *Display::createSlideManager(QObject *parent) { auto b = new SlideManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } DpmsManagerInterface *Display::createDpmsManager(QObject *parent) { auto d = new DpmsManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [this, d] { delete d; }); return d; } ServerSideDecorationManagerInterface *Display::createServerSideDecorationManager(QObject *parent) { auto d = new ServerSideDecorationManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) { TextInputManagerInterface *t = nullptr; switch (version) { case TextInputInterfaceVersion::UnstableV0: t = new TextInputManagerUnstableV0Interface(this, parent); break; case TextInputInterfaceVersion::UnstableV1: // unsupported return nullptr; case TextInputInterfaceVersion::UnstableV2: t = new TextInputManagerUnstableV2Interface(this, parent); } connect(this, &Display::aboutToTerminate, t, [t] { delete t; }); return t; } XdgShellInterface *Display::createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent) { XdgShellInterface *x = nullptr; switch (version) { case XdgShellInterfaceVersion::UnstableV5: x = new XdgShellV5Interface(this, parent); break; case XdgShellInterfaceVersion::UnstableV6: x = new XdgShellV6Interface(this, parent); break; case XdgShellInterfaceVersion::Stable: x = new XdgShellStableInterface(this, parent); break; } connect(this, &Display::aboutToTerminate, x, [x] { delete x; }); return x; } RelativePointerManagerInterface *Display::createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent) { RelativePointerManagerInterface *r = nullptr; switch (version) { case RelativePointerInterfaceVersion::UnstableV1: r = new RelativePointerManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, r, [r] { delete r; }); return r; } PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent) { PointerGesturesInterface *p = nullptr; switch (version) { case PointerGesturesInterfaceVersion::UnstableV1: p = new PointerGesturesUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent) { PointerConstraintsInterface *p = nullptr; switch (version) { case PointerConstraintsInterfaceVersion::UnstableV1: p = new PointerConstraintsUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } XdgForeignInterface *Display::createXdgForeignInterface(QObject *parent) { XdgForeignInterface *foreign = new XdgForeignInterface(this, parent); connect(this, &Display::aboutToTerminate, foreign, [this,foreign] { delete foreign; }); return foreign; } IdleInhibitManagerInterface *Display::createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent) { IdleInhibitManagerInterface *i = nullptr; switch (version) { case IdleInhibitManagerInterfaceVersion::UnstableV1: i = new IdleInhibitManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, i, [this,i] { delete i; }); return i; } AppMenuManagerInterface *Display::createAppMenuManagerInterface(QObject *parent) { auto b = new AppMenuManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } ServerSideDecorationPaletteManagerInterface *Display::createServerSideDecorationPaletteManager(QObject *parent) { auto b = new ServerSideDecorationPaletteManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } LinuxDmabufUnstableV1Interface *Display::createLinuxDmabufInterface(QObject *parent) { auto b = new LinuxDmabufUnstableV1Interface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } PlasmaVirtualDesktopManagementInterface *Display::createPlasmaVirtualDesktopManagement(QObject *parent) { auto b = new PlasmaVirtualDesktopManagementInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } XdgOutputManagerInterface *Display::createXdgOutputManager(QObject *parent) { auto b = new XdgOutputManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); return b; } XdgDecorationManagerInterface *Display::createXdgDecorationManager(XdgShellInterface *shellInterface, QObject *parent) { auto d = new XdgDecorationManagerInterface(this, shellInterface, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } EglStreamControllerInterface *Display::createEglStreamControllerInterface(QObject *parent) { EglStreamControllerInterface *e = new EglStreamControllerInterface(this, parent); connect(this, &Display::aboutToTerminate, e, [e] { delete e; }); return e; } KeyStateInterface *Display::createKeyStateInterface(QObject *parent) { auto d = new KeyStateInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } void Display::createShm() { Q_ASSERT(d->display); wl_display_init_shm(d->display); } void Display::removeOutput(OutputInterface *output) { d->outputs.removeAll(output); delete output; } void Display::removeOutputDevice(OutputDeviceInterface *output) { d->outputdevices.removeAll(output); delete output; } quint32 Display::nextSerial() { return wl_display_next_serial(d->display); } quint32 Display::serial() { return wl_display_get_serial(d->display); } bool Display::isRunning() const { return d->running; } Display::operator wl_display*() { return d->display; } Display::operator wl_display*() const { return d->display; } QList< OutputInterface* > Display::outputs() const { return d->outputs; } QList< OutputDeviceInterface* > Display::outputDevices() const { return d->outputdevices; } QVector Display::seats() const { return d->seats; } ClientConnection *Display::getConnection(wl_client *client) { Q_ASSERT(client); auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) { return c->client() == client; } ); if (it != d->clients.constEnd()) { return *it; } // no ConnectionData yet, create it auto c = new ClientConnection(client, this); d->clients << c; connect(c, &ClientConnection::disconnected, this, [this] (ClientConnection *c) { const int index = d->clients.indexOf(c); Q_ASSERT(index != -1); d->clients.remove(index); Q_ASSERT(d->clients.indexOf(c) == -1); emit clientDisconnected(c); } ); emit clientConnected(c); return c; } QVector< ClientConnection* > Display::connections() const { return d->clients; } ClientConnection *Display::createClient(int fd) { Q_ASSERT(fd != -1); Q_ASSERT(d->display); wl_client *c = wl_client_create(d->display, fd); if (!c) { return nullptr; } return getConnection(c); } void Display::setEglDisplay(void *display) { if (d->eglDisplay != EGL_NO_DISPLAY) { qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed"; return; } d->eglDisplay = (EGLDisplay)display; } void *Display::eglDisplay() const { return d->eglDisplay; } } } diff --git a/src/server/display.h b/src/server/display.h index 2e52454..fa439e8 100644 --- a/src/server/display.h +++ b/src/server/display.h @@ -1,369 +1,355 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DISPLAY_H #define WAYLAND_SERVER_DISPLAY_H #include #include #include #include "clientconnection.h" struct wl_client; struct wl_display; struct wl_event_loop; namespace KWayland { /** * @short KWayland Server. * * This namespace groups all classes related to the Server module. * * The main entry point into the KWayland::Server API is the Display class. * It allows to create a Wayland server and create various global objects on it. * * KWayland::Server is an API to easily create a head-less Wayland server with a * Qt style API. * * @see Display **/ namespace Server { class CompositorInterface; class DataDeviceManagerInterface; class DpmsManagerInterface; class IdleInterface; enum class IdleInhibitManagerInterfaceVersion; class RemoteAccessManagerInterface; class IdleInhibitManagerInterface; class FakeInputInterface; class OutputInterface; class OutputDeviceInterface; class OutputConfigurationInterface; class OutputManagementInterface; class PlasmaShellInterface; class PlasmaWindowManagementInterface; class QtSurfaceExtensionInterface; class SeatInterface; class ShadowManagerInterface; class BlurManagerInterface; class ContrastManagerInterface; class ServerSideDecorationManagerInterface; class SlideManagerInterface; class ShellInterface; class SubCompositorInterface; enum class TextInputInterfaceVersion; class TextInputManagerInterface; class XdgShellV5Interface; enum class XdgShellInterfaceVersion; class XdgShellInterface; enum class RelativePointerInterfaceVersion; class RelativePointerManagerInterface; enum class PointerGesturesInterfaceVersion; class PointerGesturesInterface; enum class PointerConstraintsInterfaceVersion; class PointerConstraintsInterface; class XdgForeignInterface; class AppMenuManagerInterface; class ServerSideDecorationPaletteManagerInterface; class PlasmaVirtualDesktopManagementInterface; class XdgOutputManagerInterface; class XdgDecorationManagerInterface; class EglStreamControllerInterface; class KeyStateInterface; class LinuxDmabufUnstableV1Interface; /** * @brief Class holding the Wayland server display loop. * * @todo Improve documentation **/ class KWAYLANDSERVER_EXPORT Display : public QObject { Q_OBJECT Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged) Q_PROPERTY(bool automaticSocketNaming READ automaticSocketNaming WRITE setAutomaticSocketNaming NOTIFY automaticSocketNamingChanged) Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) public: explicit Display(QObject *parent = nullptr); virtual ~Display(); /** * Sets the basename of the socket to @p name. If @p name is empty, it will use * wl_display_add_socket_auto to get a free socket with a filename "wayland-%d". **/ void setSocketName(const QString &name); QString socketName() const; /** * If automaticSocketNaming is true, the manually set socketName is ignored * and it will use wl_display_add_socket_auto on start to get a free socket with * a filename "wayland-%d" instead. The effective socket is written into socketName. * @since 5.55 **/ void setAutomaticSocketNaming(bool automaticSocketNaming); bool automaticSocketNaming() const; quint32 serial(); quint32 nextSerial(); /** * How to setup the server connection. * @li ConnectToSocket: the server will open the socket identified by the socket name * @li ConnectClientsOnly: only connections through createClient are possible **/ enum class StartMode { ConnectToSocket, ConnectClientsOnly }; void start(StartMode mode = StartMode::ConnectToSocket); void terminate(); /** * Starts the event loop for the server socket. * This method should only be used if start() is used before creating the * QCoreApplication. In that case start() cannot fully setup the event processing * and the loop needs to be started after the QCoreApplication got created. * @see start * @see dispatchEvents **/ void startLoop(); /** * Dispatches pending events in a blocking way. May only be used if the Display is * created and started before the QCoreApplication is created. Once the QCoreApplication * is created and the event loop is started this method delegates to the normal dispatch * handling. * @see startLoop **/ void dispatchEvents(int msecTimeout = -1); /** * Create a client for the given file descriptor. * * The client is created as if it connected through the normal server * socket. This method can be used to create a connection bypassing the * normal socket connection. It's recommended to use together with * socketpair and pass the other side of the socket to the client. * * @param fd The file descriptor for the socket to the client * @returns The new ClientConnection or @c null on failure. **/ ClientConnection *createClient(int fd); operator wl_display*(); operator wl_display*() const; bool isRunning() const; OutputInterface *createOutput(QObject *parent = nullptr); void removeOutput(OutputInterface *output); QList outputs() const; OutputDeviceInterface *createOutputDevice(QObject *parent = nullptr); void removeOutputDevice(OutputDeviceInterface *output); QList outputDevices() const; CompositorInterface *createCompositor(QObject *parent = nullptr); void createShm(); ShellInterface *createShell(QObject *parent = nullptr); SeatInterface *createSeat(QObject *parent = nullptr); /** * @returns All SeatInterface currently managed on the Display. * @since 5.6 **/ QVector seats() const; SubCompositorInterface *createSubCompositor(QObject *parent = nullptr); DataDeviceManagerInterface *createDataDeviceManager(QObject *parent = nullptr); OutputManagementInterface *createOutputManagement(QObject *parent = nullptr); PlasmaShellInterface *createPlasmaShell(QObject *parent = nullptr); PlasmaWindowManagementInterface *createPlasmaWindowManagement(QObject *parent = nullptr); QtSurfaceExtensionInterface *createQtSurfaceExtension(QObject *parent = nullptr); IdleInterface *createIdle(QObject *parent = nullptr); RemoteAccessManagerInterface *createRemoteAccessManager(QObject *parent = nullptr); FakeInputInterface *createFakeInput(QObject *parent = nullptr); ShadowManagerInterface *createShadowManager(QObject *parent = nullptr); BlurManagerInterface *createBlurManager(QObject *parent = nullptr); ContrastManagerInterface *createContrastManager(QObject *parent = nullptr); SlideManagerInterface *createSlideManager(QObject *parent = nullptr); DpmsManagerInterface *createDpmsManager(QObject *parent = nullptr); /** @since 5.60 */ KeyStateInterface *createKeyStateInterface(QObject *parent = nullptr); /** * @since 5.6 **/ ServerSideDecorationManagerInterface *createServerSideDecorationManager(QObject *parent = nullptr); /** * Create the text input manager in interface @p version. * @returns The created manager object * @since 5.23 **/ TextInputManagerInterface *createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgShell in interface @p version. * * @since 5.25 **/ XdgShellInterface *createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the RelativePointerManagerInterface in interface @p version * * @returns The created manager object * @since 5.28 **/ RelativePointerManagerInterface *createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerGesturesInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerGesturesInterface *createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the PointerConstraintsInterface in interface @p version * * @returns The created manager object * @since 5.29 **/ PointerConstraintsInterface *createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the XdgForeignInterface in interface @p version * * @returns The created manager object * @since 5.40 **/ XdgForeignInterface *createXdgForeignInterface(QObject *parent = nullptr); /** * Creates the IdleInhibitManagerInterface in interface @p version. * * @returns The created manager object * @since 5.41 **/ IdleInhibitManagerInterface *createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent = nullptr); /** * Creates the AppMenuManagerInterface in interface @p version. * * @returns The created manager object * @since 5.42 **/ AppMenuManagerInterface *createAppMenuManagerInterface(QObject *parent = nullptr); /** * Creates the ServerSideDecorationPaletteManagerInterface in interface @p version. * * @returns The created manager object * @since 5.42 **/ ServerSideDecorationPaletteManagerInterface *createServerSideDecorationPaletteManager(QObject *parent = nullptr); /** * Creates the LinuxDmabufUnstableV1Interface in interface @p version. * * @returns A pointer to the created interface **/ LinuxDmabufUnstableV1Interface *createLinuxDmabufInterface(QObject *parent = nullptr); /** * Creates the XdgOutputManagerInterface * * @return the created manager * @since 5.47 */ XdgOutputManagerInterface *createXdgOutputManager(QObject *parent = nullptr); /** * Creates the PlasmaVirtualDesktopManagementInterface in interface @p version. * * @returns The created manager object * @since 5.52 **/ PlasmaVirtualDesktopManagementInterface *createPlasmaVirtualDesktopManagement(QObject *parent = nullptr); /** * Creates the XdgDecorationManagerInterface * @arg shellInterface A created XdgShellInterface based on XDG_WM_BASE * * @return the created manager * @since 5.54 */ XdgDecorationManagerInterface *createXdgDecorationManager(XdgShellInterface *shellInterface, QObject *parent = nullptr); /** * Creates the EglStreamControllerInterface * * @return the created EGL Stream controller * @since 5.58 */ EglStreamControllerInterface *createEglStreamControllerInterface(QObject *parent = nullptr); /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. * @param client The native client for which the ClientConnection is retrieved * @return The ClientConnection for the given native client **/ ClientConnection *getConnection(wl_client *client); QVector connections() const; /** * Set the EGL @p display for this Wayland display. * The EGLDisplay can only be set once and must be alive as long as the Wayland display * is alive. The user should have set up the binding between the EGLDisplay and the * Wayland display prior to calling this method. * * @see eglDisplay * @since 5.3 **/ void setEglDisplay(void *display); /** * @returns the EGLDisplay used for this Wayland display or EGL_NO_DISPLAY if not set. * @see setEglDisplay * @since 5.3 **/ void *eglDisplay() const; Q_SIGNALS: void socketNameChanged(const QString&); void automaticSocketNamingChanged(bool); void runningChanged(bool); void aboutToTerminate(); void clientConnected(KWayland::Server::ClientConnection*); void clientDisconnected(KWayland::Server::ClientConnection*); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/dpms_interface.cpp b/src/server/dpms_interface.cpp index aaa072b..6504ac3 100644 --- a/src/server/dpms_interface.cpp +++ b/src/server/dpms_interface.cpp @@ -1,176 +1,162 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "dpms_interface_p.h" #include "display.h" #include "output_interface.h" namespace KWayland { namespace Server { const quint32 DpmsManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_dpms_manager_interface DpmsManagerInterface::Private::s_interface = { getDpmsCallback }; #endif DpmsManagerInterface::Private::Private(DpmsManagerInterface *qptr, Display *d) : Global::Private(d, &org_kde_kwin_dpms_manager_interface, s_version) , q(qptr) { } void DpmsManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *dpms = c->createResource(&org_kde_kwin_dpms_manager_interface, qMin(version, s_version), id); if (!dpms) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(dpms, &s_interface, this, nullptr); } void DpmsManagerInterface::Private::getDpmsCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *output) { auto p = Private::cast(resource); auto c = p->display->getConnection(client); OutputInterface *o = OutputInterface::get(output); DpmsInterface *dpms = new DpmsInterface(o, resource, p->q); dpms->create(c, wl_resource_get_version(resource), id); if (!dpms->resource()) { wl_resource_post_no_memory(resource); return; } dpms->sendSupported(); dpms->sendMode(); dpms->sendDone(); } DpmsManagerInterface::DpmsManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } DpmsManagerInterface::~DpmsManagerInterface() = default; #ifndef K_DOXYGEN const struct org_kde_kwin_dpms_interface DpmsInterface::Private::s_interface = { setCallback, resourceDestroyedCallback }; #endif DpmsInterface::Private::Private(DpmsInterface *q, DpmsManagerInterface *g, wl_resource *parentResource, OutputInterface *outputInterface) : Resource::Private(q, g, parentResource, &org_kde_kwin_dpms_interface, &s_interface) , output(outputInterface) { } void DpmsInterface::Private::setCallback(wl_client *client, wl_resource *resource, uint32_t mode) { Q_UNUSED(client) OutputInterface::DpmsMode dpmsMode; switch (mode) { case ORG_KDE_KWIN_DPMS_MODE_ON: dpmsMode = OutputInterface::DpmsMode::On; break; case ORG_KDE_KWIN_DPMS_MODE_STANDBY: dpmsMode = OutputInterface::DpmsMode::Standby; break; case ORG_KDE_KWIN_DPMS_MODE_SUSPEND: dpmsMode = OutputInterface::DpmsMode::Suspend; break; case ORG_KDE_KWIN_DPMS_MODE_OFF: dpmsMode = OutputInterface::DpmsMode::Off; break; default: return; } emit cast(resource)->output->dpmsModeRequested(dpmsMode); } DpmsInterface::DpmsInterface(OutputInterface *output, wl_resource *parentResource, DpmsManagerInterface *manager) : Resource(new Private(this, manager, parentResource, output)) { connect(output, &OutputInterface::dpmsSupportedChanged, this, [this] { sendSupported(); sendDone(); } ); connect(output, &OutputInterface::dpmsModeChanged, this, [this] { sendMode(); sendDone(); } ); } DpmsInterface::~DpmsInterface() = default; void DpmsInterface::sendSupported() { Q_D(); org_kde_kwin_dpms_send_supported(d->resource, d->output->isDpmsSupported() ? 1 : 0); } void DpmsInterface::sendMode() { Q_D(); const auto mode = d->output->dpmsMode(); org_kde_kwin_dpms_mode wlMode; switch (mode) { case OutputInterface::DpmsMode::On: wlMode = ORG_KDE_KWIN_DPMS_MODE_ON; break; case OutputInterface::DpmsMode::Standby: wlMode = ORG_KDE_KWIN_DPMS_MODE_STANDBY; break; case OutputInterface::DpmsMode::Suspend: wlMode = ORG_KDE_KWIN_DPMS_MODE_SUSPEND; break; case OutputInterface::DpmsMode::Off: wlMode = ORG_KDE_KWIN_DPMS_MODE_OFF; break; default: Q_UNREACHABLE(); } org_kde_kwin_dpms_send_mode(d->resource, wlMode); } void DpmsInterface::sendDone() { Q_D(); org_kde_kwin_dpms_send_done(d->resource); client()->flush(); } DpmsInterface::Private *DpmsInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/dpms_interface.h b/src/server/dpms_interface.h index 79736f6..d4589cb 100644 --- a/src/server/dpms_interface.h +++ b/src/server/dpms_interface.h @@ -1,83 +1,69 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DPMS_INTERFACE_H #define WAYLAND_SERVER_DPMS_INTERFACE_H #include #include #include "global.h" namespace KWayland { namespace Server { class Display; /** * @brief Global for server side Display Power Management Signaling interface. * * A DpmsManagerInterface allows a client to query the DPMS state * on a given OutputInterface and request changes to it. * Server-side the interaction happens only via the OutputInterface, * for clients the Dpms class provides the API. * This global implements org_kde_kwin_dpms_manager. * * To create a DpmsManagerInterface use: * @code * auto manager = display->createDpmsManager(); * manager->create(); * @endcode * * To interact with Dpms use one needs to mark it as enabled and set the * proper mode on the OutputInterface. * @code * // We have our OutputInterface called output. * output->setDpmsSupported(true); * output->setDpmsMode(OutputInterface::DpmsMode::On); * @endcode * * To connect to Dpms change requests use: * @code * connect(output, &OutputInterface::dpmsModeRequested, * [] (KWayland::Server::OutputInterface::DpmsMode requestedMode) { qDebug() << "Mode change requested"; }); * @endcode * * @see Display * @see OutputInterface * @since 5.5 **/ class KWAYLANDSERVER_EXPORT DpmsManagerInterface : public Global { Q_OBJECT public: virtual ~DpmsManagerInterface(); private: explicit DpmsManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } #endif diff --git a/src/server/dpms_interface_p.h b/src/server/dpms_interface_p.h index 308562e..8f4ad04 100644 --- a/src/server/dpms_interface_p.h +++ b/src/server/dpms_interface_p.h @@ -1,84 +1,70 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_DPMS_INTERFACE_P_H #define WAYLAND_SERVER_DPMS_INTERFACE_P_H #include "dpms_interface.h" #include "global_p.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class OutputInterface; class DpmsManagerInterface::Private : public Global::Private { public: Private(DpmsManagerInterface *q, Display *d); private: static void getDpmsCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *output); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } void bind(wl_client *client, uint32_t version, uint32_t id) override; DpmsManagerInterface *q; static const struct org_kde_kwin_dpms_manager_interface s_interface; static const quint32 s_version; }; class DpmsInterface : public Resource { Q_OBJECT public: explicit DpmsInterface(OutputInterface *output, wl_resource *parentResource, DpmsManagerInterface *manager); virtual ~DpmsInterface(); void sendSupported(); void sendMode(); void sendDone(); private: class Private; Private *d_func() const; }; class DpmsInterface::Private : public Resource::Private { public: explicit Private(DpmsInterface *q, DpmsManagerInterface *g, wl_resource *parentResource, OutputInterface *output); OutputInterface *output; private: static void setCallback(wl_client *client, wl_resource *resource, uint32_t mode); static const struct org_kde_kwin_dpms_interface s_interface; }; } } #endif diff --git a/src/server/drm_fourcc.h b/src/server/drm_fourcc.h index 70d492a..6a8affc 100644 --- a/src/server/drm_fourcc.h +++ b/src/server/drm_fourcc.h @@ -1,414 +1,397 @@ /* - * Copyright 2011 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ + SPDX-FileCopyrightText: 2011 Intel Corporation + + SPDX-License-Identifier: MIT +*/ #ifndef DRM_FOURCC_H #define DRM_FOURCC_H //#include "drm.h" // These typedefs are copied from drm.h typedef uint32_t __u32; typedef uint64_t __u64; #if defined(__cplusplus) extern "C" { #endif #define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ ((__u32)(c) << 16) | ((__u32)(d) << 24)) #define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ /* color index */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ /* 8 bpp Red */ #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ /* 16 bpp Red */ #define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ /* 16 bpp RG */ #define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ #define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ /* 32 bpp RG */ #define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ #define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ /* 8 bpp RGB */ #define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ #define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ /* 16 bpp RGB */ #define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ #define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ #define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ #define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ #define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ #define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ #define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ #define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ #define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ #define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ #define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ #define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ #define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ #define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ #define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ #define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ #define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ #define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ /* 24 bpp RGB */ #define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ #define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ /* 32 bpp RGB */ #define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ #define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ #define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ #define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ #define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ #define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ #define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ #define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ #define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ #define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ #define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ #define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ #define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ #define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ #define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ /* packed YCbCr */ #define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ #define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ #define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ #define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ #define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ /* * 2 plane RGB + A * index 0 = RGB plane, same format as the corresponding non _A8 format has * index 1 = A plane, [7:0] A */ #define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') #define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') #define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') #define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') #define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') #define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') #define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') #define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') /* * 2 plane YCbCr * index 0 = Y plane, [7:0] Y * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian * or * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian */ #define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ #define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ #define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ #define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ #define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ #define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ /* * 3 plane YCbCr * index 0: Y plane, [7:0] Y * index 1: Cb plane, [7:0] Cb * index 2: Cr plane, [7:0] Cr * or * index 1: Cr plane, [7:0] Cr * index 2: Cb plane, [7:0] Cb */ #define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ #define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ #define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ #define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ #define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ #define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ /* * Format Modifiers: * * Format modifiers describe, typically, a re-ordering or modification * of the data in a plane of an FB. This can be used to express tiled/ * swizzled formats, or compression, or a combination of the two. * * The upper 8 bits of the format modifier are a vendor-id as assigned * below. The lower 56 bits are assigned as vendor sees fit. */ /* Vendor Ids: */ #define DRM_FORMAT_MOD_NONE 0 #define DRM_FORMAT_MOD_VENDOR_NONE 0 #define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 #define DRM_FORMAT_MOD_VENDOR_AMD 0x02 #define DRM_FORMAT_MOD_VENDOR_NV 0x03 #define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 #define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 #define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 #define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 /* add more to the end as needed */ #define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) #define fourcc_mod_code(vendor, val) \ ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL)) /* * Format Modifier tokens: * * When adding a new token please document the layout with a code comment, * similar to the fourcc codes above. drm_fourcc.h is considered the * authoritative source for all of these. */ /* * Invalid Modifier * * This modifier can be used as a sentinel to terminate the format modifiers * list, or to initialize a variable with an invalid modifier. It might also be * used to report an error back to userspace for certain APIs. */ #define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) /* * Linear Layout * * Just plain linear layout. Note that this is different from no specifying any * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), * which tells the driver to also take driver-internal information into account * and so might actually result in a tiled framebuffer. */ #define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) /* Intel framebuffer modifiers */ /* * Intel X-tiling layout * * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) * in row-major layout. Within the tile bytes are laid out row-major, with * a platform-dependent stride. On top of that the memory can apply * platform-depending swizzling of some higher address bits into bit6. * * This format is highly platforms specific and not useful for cross-driver * sharing. It exists since on a given platform it does uniquely identify the * layout in a simple way for i915-specific userspace. */ #define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) /* * Intel Y-tiling layout * * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) * chunks column-major, with a platform-dependent height. On top of that the * memory can apply platform-depending swizzling of some higher address bits * into bit6. * * This format is highly platforms specific and not useful for cross-driver * sharing. It exists since on a given platform it does uniquely identify the * layout in a simple way for i915-specific userspace. */ #define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) /* * Intel Yf-tiling layout * * This is a tiled layout using 4Kb tiles in row-major layout. * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which * are arranged in four groups (two wide, two high) with column-major layout. * Each group therefore consits out of four 256 byte units, which are also laid * out as 2x2 column-major. * 256 byte units are made out of four 64 byte blocks of pixels, producing * either a square block or a 2:1 unit. * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width * in pixel depends on the pixel depth. */ #define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) /* * Intel color control surface (CCS) for render compression * * The framebuffer format must be one of the 8:8:8:8 RGB formats. * The main surface will be plane index 0 and must be Y/Yf-tiled, * the CCS will be plane index 1. * * Each CCS tile matches a 1024x512 pixel area of the main surface. * To match certain aspects of the 3D hardware the CCS is * considered to be made up of normal 128Bx32 Y tiles, Thus * the CCS pitch must be specified in multiples of 128 bytes. * * In reality the CCS tile appears to be a 64Bx64 Y tile, composed * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. * But that fact is not relevant unless the memory is accessed * directly. */ #define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) #define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) /* * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks * * Macroblocks are laid in a Z-shape, and each pixel data is following the * standard NV12 style. * As for NV12, an image is the result of two frame buffers: one for Y, * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). * Alignment requirements are (for each buffer): * - multiple of 128 pixels for the width * - multiple of 32 pixels for the height * * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html */ #define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) /* Vivante framebuffer modifiers */ /* * Vivante 4x4 tiling layout * * This is a simple tiled layout using tiles of 4x4 pixels in a row-major * layout. */ #define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) /* * Vivante 64x64 super-tiling layout * * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- * major layout. * * For more information: see * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling */ #define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) /* * Vivante 4x4 tiling layout for dual-pipe * * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a * different base address. Offsets from the base addresses are therefore halved * compared to the non-split tiled layout. */ #define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) /* * Vivante 64x64 super-tiling layout for dual-pipe * * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile * starts at a different base address. Offsets from the base addresses are * therefore halved compared to the non-split super-tiled layout. */ #define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) /* NVIDIA Tegra frame buffer modifiers */ /* * Some modifiers take parameters, for example the number of vertical GOBs in * a block. Reserve the lower 32 bits for parameters */ #define __fourcc_mod_tegra_mode_shift 32 #define fourcc_mod_tegra_code(val, params) \ fourcc_mod_code(NV, ((((__u64)val) << __fourcc_mod_tegra_mode_shift) | params)) #define fourcc_mod_tegra_mod(m) \ (m & ~((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) #define fourcc_mod_tegra_param(m) \ (m & ((1ULL << __fourcc_mod_tegra_mode_shift) - 1)) /* * Tegra Tiled Layout, used by Tegra 2, 3 and 4. * * Pixels are arranged in simple tiles of 16 x 16 bytes. */ #define NV_FORMAT_MOD_TEGRA_TILED fourcc_mod_tegra_code(1, 0) /* * Tegra 16Bx2 Block Linear layout, used by TK1/TX1 * * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked * vertically by a power of 2 (1 to 32 GOBs) to form a block. * * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. * * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. * Valid values are: * * 0 == ONE_GOB * 1 == TWO_GOBS * 2 == FOUR_GOBS * 3 == EIGHT_GOBS * 4 == SIXTEEN_GOBS * 5 == THIRTYTWO_GOBS * * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format * in full detail. */ #define NV_FORMAT_MOD_TEGRA_16BX2_BLOCK(v) fourcc_mod_tegra_code(2, v) /* * Broadcom VC4 "T" format * * This is the primary layout that the V3D GPU can texture from (it * can't do linear). The T format has: * * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 * pixels at 32 bit depth. * * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually * 16x16 pixels). * * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows * they're (TR, BR, BL, TL), where bottom left is start of memory. * * - an image made of 4k tiles in rows either left-to-right (even rows of 4k * tiles) or right-to-left (odd rows of 4k tiles). */ #define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) #if defined(__cplusplus) } #endif #endif /* DRM_FOURCC_H */ diff --git a/src/server/eglstream_controller_interface.cpp b/src/server/eglstream_controller_interface.cpp index 6233a76..079f614 100644 --- a/src/server/eglstream_controller_interface.cpp +++ b/src/server/eglstream_controller_interface.cpp @@ -1,107 +1,93 @@ -/**************************************************************************** -Copyright 2019 NVIDIA Inc. - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2019 NVIDIA Inc. + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "eglstream_controller_interface_p.h" #include "clientconnection.h" #include "display.h" #include "logging.h" #include #include namespace KWayland { namespace Server { const quint32 EglStreamControllerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct wl_eglstream_controller_interface EglStreamControllerInterface::Private::s_interface = { attachStreamConsumer, attachStreamConsumerAttribs }; #endif void EglStreamControllerInterface::Private::attachStreamConsumer(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream) { wl_array noAttribs = { 0, 0, nullptr }; attachStreamConsumerAttribs(client, resource, surface, eglStream, &noAttribs); } void EglStreamControllerInterface::Private::attachStreamConsumerAttribs(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream, wl_array *attribs) { Q_UNUSED(client); Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); emit p->q->streamConsumerAttached(SurfaceInterface::get(surface), eglStream, attribs); } EglStreamControllerInterface::Private::Private(EglStreamControllerInterface *q, Display *display) // libnvidia-egl-wayland.so.1 may not be present on all systems, so we load it dynamically : Global::Private(display, (wl_interface *)QLibrary::resolve(QLatin1String("libnvidia-egl-wayland.so.1"), "wl_eglstream_controller_interface"), s_version) , q(q) { } void EglStreamControllerInterface::Private::create() { // bail out early if we were unable to load the interface if (m_interface == nullptr) { qCWarning(KWAYLAND_SERVER) << "failed to resolve wl_eglstream_controller_interface"; return; } - + Global::Private::create(); } void EglStreamControllerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { wl_resource *r = display->getConnection(client)->createResource(m_interface, version, id); if (r == nullptr) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(r, &s_interface, this, nullptr); } EglStreamControllerInterface::~EglStreamControllerInterface() = default; EglStreamControllerInterface::EglStreamControllerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } void EglStreamControllerInterface::create() { static_cast(*d).create(); } } } diff --git a/src/server/eglstream_controller_interface.h b/src/server/eglstream_controller_interface.h index 175114a..7c1ce3a 100644 --- a/src/server/eglstream_controller_interface.h +++ b/src/server/eglstream_controller_interface.h @@ -1,67 +1,53 @@ -/**************************************************************************** -Copyright 2019 NVIDIA Inc. +/* + SPDX-FileCopyrightText: 2019 NVIDIA Inc. -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_EGLSTREAM_CONTROLLER_INTERFACE_H #define WAYLAND_SERVER_EGLSTREAM_CONTROLLER_INTERFACE_H #include "global.h" #include "surface_interface.h" #include #include #include namespace KWayland { namespace Server { class Display; /** * @brief Represents the Global for the wl_eglstream_controller interface. * * This class handles requests (typically from the NVIDIA EGL driver) to attach * a newly created EGL Stream to a Wayland surface, facilitating the sharing * of buffer contents between client and compositor. * */ class KWAYLANDSERVER_EXPORT EglStreamControllerInterface : public Global { Q_OBJECT public: ~EglStreamControllerInterface() override; void create(); Q_SIGNALS: /** * Emitted when a new stream attach request is received. */ void streamConsumerAttached(SurfaceInterface *surface, void *eglStream, wl_array *attribs); private: explicit EglStreamControllerInterface(Display *display, QObject *parent = nullptr); - + class Private; friend class Display; }; } } #endif diff --git a/src/server/eglstream_controller_interface_p.h b/src/server/eglstream_controller_interface_p.h index 386f55d..d7faef2 100644 --- a/src/server/eglstream_controller_interface_p.h +++ b/src/server/eglstream_controller_interface_p.h @@ -1,58 +1,44 @@ -/**************************************************************************** -Copyright 2019 NVIDIA Inc. - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2019 NVIDIA Inc. + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_EGLSTREAM_CONTROLLER_INTERFACE_P_H #define WAYLAND_SERVER_EGLSTREAM_CONTROLLER_INTERFACE_P_H #include "eglstream_controller_interface.h" #include "global_p.h" #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN EglStreamControllerInterface::Private : public Global::Private { public: Private(EglStreamControllerInterface *controller, Display *display); void create(); private: static void attachStreamConsumer(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream); static void attachStreamConsumerAttribs(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream, wl_array *attribs); static const struct wl_eglstream_controller_interface s_interface; static const quint32 s_version; void bind(wl_client *client, uint32_t version, uint32_t id) override; EglStreamControllerInterface *q; }; } } #endif diff --git a/src/server/fakeinput_interface.cpp b/src/server/fakeinput_interface.cpp index 417db20..b2d9e7e 100644 --- a/src/server/fakeinput_interface.cpp +++ b/src/server/fakeinput_interface.cpp @@ -1,328 +1,314 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "fakeinput_interface.h" #include "display.h" #include "global_p.h" #include #include #include #include namespace KWayland { namespace Server { class FakeInputInterface::Private : public Global::Private { public: Private(FakeInputInterface *q, Display *d); QList devices; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason); static void pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y); static void pointerMotionAbsoluteCallback(wl_client *client, wl_resource *resource, wl_fixed_t x, wl_fixed_t y); static void buttonCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value); static void touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); static void touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); static void touchUpCallback(wl_client *client, wl_resource *resource, quint32 id); static void touchCancelCallback(wl_client *client, wl_resource *resource); static void touchFrameCallback(wl_client *client, wl_resource *resource); static void keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static FakeInputDevice *device(wl_resource *r); FakeInputInterface *q; static const struct org_kde_kwin_fake_input_interface s_interface; static const quint32 s_version; static QList touchIds; }; class FakeInputDevice::Private { public: Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q); wl_resource *resource; FakeInputInterface *interface; bool authenticated = false; private: FakeInputDevice *q; }; const quint32 FakeInputInterface::Private::s_version = 4; QList FakeInputInterface::Private::touchIds = QList(); #ifndef K_DOXYGEN const struct org_kde_kwin_fake_input_interface FakeInputInterface::Private::s_interface = { authenticateCallback, pointerMotionCallback, buttonCallback, axisCallback, touchDownCallback, touchMotionCallback, touchUpCallback, touchCancelCallback, touchFrameCallback, pointerMotionAbsoluteCallback, keyboardKeyCallback }; #endif FakeInputInterface::Private::Private(FakeInputInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_fake_input_interface, s_version) , q(q) { } void FakeInputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_fake_input_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); FakeInputDevice *device = new FakeInputDevice(resource, q); devices << device; QObject::connect(device, &FakeInputDevice::destroyed, q, [device, this] { devices.removeAll(device); }); emit q->deviceCreated(device); } void FakeInputInterface::Private::unbind(wl_resource *resource) { if (FakeInputDevice *d = device(resource)) { d->deleteLater(); } } FakeInputDevice *FakeInputInterface::Private::device(wl_resource *r) { Private *p = cast(r); auto it = std::find_if(p->devices.constBegin(), p->devices.constEnd(), [r] (FakeInputDevice *device) { return device->resource() == r; } ); if (it != p->devices.constEnd()) { return *it; } return nullptr; } void FakeInputInterface::Private::authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d) { return; } emit d->authenticationRequested(QString::fromUtf8(application), QString::fromUtf8(reason)); } void FakeInputInterface::Private::pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->pointerMotionRequested(QSizeF(wl_fixed_to_double(delta_x), wl_fixed_to_double(delta_y))); } void FakeInputInterface::Private::pointerMotionAbsoluteCallback(wl_client *client, wl_resource *resource, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->pointerMotionAbsoluteRequested(QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } Qt::Orientation orientation; switch (axis) { case WL_POINTER_AXIS_HORIZONTAL_SCROLL: orientation = Qt::Horizontal; break; case WL_POINTER_AXIS_VERTICAL_SCROLL: orientation = Qt::Vertical; break; default: // invalid return; } emit d->pointerAxisRequested(orientation, wl_fixed_to_double(value)); } void FakeInputInterface::Private::buttonCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: emit d->pointerButtonPressRequested(button); break; case WL_POINTER_BUTTON_STATE_RELEASED: emit d->pointerButtonReleaseRequested(button); break; default: // nothing break; } } void FakeInputInterface::Private::touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (touchIds.contains(id)) { return; } touchIds << id; emit d->touchDownRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (!touchIds.contains(id)) { return; } emit d->touchMotionRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::touchUpCallback(wl_client *client, wl_resource *resource, quint32 id) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (!touchIds.contains(id)) { return; } touchIds.removeOne(id); emit d->touchUpRequested(id); } void FakeInputInterface::Private::touchCancelCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } touchIds.clear(); emit d->touchCancelRequested(); } void FakeInputInterface::Private::touchFrameCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->touchFrameRequested(); } void FakeInputInterface::Private::keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } switch (state) { case WL_KEYBOARD_KEY_STATE_PRESSED: emit d->keyboardKeyPressRequested(button); break; case WL_KEYBOARD_KEY_STATE_RELEASED: emit d->keyboardKeyReleaseRequested(button); break; default: // nothing break; } } FakeInputInterface::FakeInputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } FakeInputInterface::~FakeInputInterface() = default; FakeInputDevice::Private::Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q) : resource(resource) , interface(interface) , q(q) { } FakeInputDevice::FakeInputDevice(wl_resource *resource, FakeInputInterface *parent) : QObject(parent) , d(new Private(resource, parent, this)) { } FakeInputDevice::~FakeInputDevice() = default; void FakeInputDevice::setAuthentication(bool authenticated) { d->authenticated = authenticated; } wl_resource *FakeInputDevice::resource() { return d->resource; } bool FakeInputDevice::isAuthenticated() const { return d->authenticated; } } } diff --git a/src/server/fakeinput_interface.h b/src/server/fakeinput_interface.h index 9921fc4..a3cdc24 100644 --- a/src/server/fakeinput_interface.h +++ b/src/server/fakeinput_interface.h @@ -1,189 +1,175 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_FAKEINPUT_INTERFACE_H #define KWAYLAND_SERVER_FAKEINPUT_INTERFACE_H #include #include "global.h" struct wl_resource; namespace KWayland { namespace Server { class Display; class FakeInputDevice; /** * @brief Represents the Global for org_kde_kwin_fake_input interface. * * The fake input interface allows clients to send fake input events to the * Wayland server. For the actual events it creates a FakeInputDevice. Whenever * the FakeInputInterface creates a device the signal deviceCreated gets emitted. * * Accepting fake input events is a security risk. The server should make a * dedicated decision about whether it wants to accept fake input events from a * device. Because of that by default no events are forwarded to the server. The * device needs to request authentication and the server must explicitly authenticate * the device. The recommendation is that the server only accepts input for in some * way trusted clients. * * @see FakeInputDevice * @since 5.4 **/ class KWAYLANDSERVER_EXPORT FakeInputInterface : public Global { Q_OBJECT public: virtual ~FakeInputInterface(); Q_SIGNALS: /** * Signal emitted whenever a client bound the fake input @p device. * @param device The created FakeInputDevice **/ void deviceCreated(KWayland::Server::FakeInputDevice *device); private: explicit FakeInputInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * @brief Represents the Resource for a org_kde_kwin_fake_input interface. * * @see FakeInputInterface * @since 5.4 **/ class KWAYLANDSERVER_EXPORT FakeInputDevice : public QObject { Q_OBJECT public: virtual ~FakeInputDevice(); /** * @returns the native wl_resource. **/ wl_resource *resource(); /** * Authenticate this device to send events. If @p authenticated is @c true events are * accepted, for @c false events are no longer accepted. * * @param authenticated Whether the FakeInputDevice should be considered authenticated **/ void setAuthentication(bool authenticated); /** * @returns whether the FakeInputDevice is authenticated and allowed to send events, default is @c false. **/ bool isAuthenticated() const; Q_SIGNALS: /** * Request for authentication. * * The server might use the provided information to make a decision on whether the * FakeInputDevice should get authenticated. It is recommended to not trust the data * and to combine it with information from ClientConnection. * * @param application A textual description of the application * @param reason A textual description of the reason why the application wants to send fake input events **/ void authenticationRequested(const QString &application, const QString &reason); /** * Request a pointer motion by @p delta. **/ void pointerMotionRequested(const QSizeF &delta); /** * Request an absolute pointer motion to @p pos. * * @since 5.54 **/ void pointerMotionAbsoluteRequested(const QPointF &pos); /** * Requests a pointer button pressed for @p button. **/ void pointerButtonPressRequested(quint32 button); /** * Requests a pointer button release for @p button. **/ void pointerButtonReleaseRequested(quint32 button); /** * Requests a pointer axis for the given @p orientation by @p delta. **/ void pointerAxisRequested(Qt::Orientation orientation, qreal delta); /** * Requests a touch down at @p pos and identified by @p id. * * @since 5.23 **/ void touchDownRequested(quint32 id, const QPointF &pos); /** * Requests a touch motion by @p pos and identified by @p id. * * @since 5.23 **/ void touchMotionRequested(quint32 id, const QPointF &pos); /** * Requests a touch up identified by @p id. * * @since 5.23 **/ void touchUpRequested(quint32 id); /** * Requests a touch cancel event. * * @since 5.23 **/ void touchCancelRequested(); /** * Requests a touch frame event. * * @since 5.23 **/ void touchFrameRequested(); /** * Requests a keyboard key pressed for @p key. * * @since 5.63 **/ void keyboardKeyPressRequested(quint32 key); /** * Requests a keyboard key release for @p key. * * @since 5.63 **/ void keyboardKeyReleaseRequested(quint32 key); private: friend class FakeInputInterface; FakeInputDevice(wl_resource *resource, FakeInputInterface *parent); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::FakeInputDevice*) #endif diff --git a/src/server/filtered_display.cpp b/src/server/filtered_display.cpp index c2e4c97..4efab1c 100644 --- a/src/server/filtered_display.cpp +++ b/src/server/filtered_display.cpp @@ -1,70 +1,56 @@ -/******************************************************************** -Copyright 2017 David Edmundson +/* + SPDX-FileCopyrightText: 2017 David Edmundson -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "filtered_display.h" #include "display.h" #include #include namespace KWayland { namespace Server { class FilteredDisplay::Private { public: Private(FilteredDisplay *_q); FilteredDisplay *q; static bool globalFilterCallback(const wl_client *client, const wl_global *global, void *data) { auto t = static_cast(data); auto clientConnection = t->q->getConnection(const_cast(client)); auto interface = wl_global_get_interface(global); auto name = QByteArray::fromRawData(interface->name, strlen(interface->name)); return t->q->allowInterface(clientConnection, name); }; }; FilteredDisplay::Private::Private(FilteredDisplay *_q): q(_q) {} FilteredDisplay::FilteredDisplay(QObject *parent): Display(parent), d(new Private(this)) { connect(this, &Display::runningChanged, [this](bool running) { if (!running) { return; } wl_display_set_global_filter(*this, Private::globalFilterCallback, d.data()); }); } FilteredDisplay::~FilteredDisplay() { } } } diff --git a/src/server/filtered_display.h b/src/server/filtered_display.h index 1a3babb..4a97379 100644 --- a/src/server/filtered_display.h +++ b/src/server/filtered_display.h @@ -1,65 +1,51 @@ -/******************************************************************** -Copyright 2017 David Edmundson +/* + SPDX-FileCopyrightText: 2017 David Edmundson -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_FILTERED_DISPLAY_H #define KWAYLAND_SERVER_FILTERED_DISPLAY_H #include "global.h" #include "display.h" #include namespace KWayland { namespace Server { /** * Server Implementation that allows one to restrict which globals are available to which clients * * Users of this class must implement the virtual @method allowInterface method. * * @since 5.FIXME */ class KWAYLANDSERVER_EXPORT FilteredDisplay : public Display { Q_OBJECT public: FilteredDisplay(QObject *parent); ~FilteredDisplay(); /** * Return whether the @arg client can see the interface with the given @arg interfaceName * * When false will not see these globals for a given interface in the registry, * and any manual attempts to bind will fail * * @return true if the client should be able to access the global with the following interfaceName */ virtual bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) = 0; private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/generic_shell_surface_p.h b/src/server/generic_shell_surface_p.h index 36b95b6..107b567 100644 --- a/src/server/generic_shell_surface_p.h +++ b/src/server/generic_shell_surface_p.h @@ -1,131 +1,117 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_GENERIC_SHELL_SURFACE_P_H #define KWAYLAND_SERVER_GENERIC_SHELL_SURFACE_P_H #include "seat_interface.h" #include "surface_interface.h" #include "surfacerole_p.h" #include namespace KWayland { namespace Server { template class GenericShellSurface : public SurfaceRole { public: GenericShellSurface(T *shellSurface, SurfaceInterface *surface) : SurfaceRole(surface) , surface(surface) , shellSurface(shellSurface) {} SurfaceInterface *surface; QString title; QByteArray windowClass; protected: void setTitle(const QString &title); void setWindowClass(const QByteArray &wc); static void moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); template static void resizeCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, uint32_t edges); static void setTitleCallback(wl_client *client, wl_resource *resource, const char *title); static void setAppIdCallback(wl_client *client, wl_resource *resource, const char *app_id); private: T *q_func() { return shellSurface; } static typename T::Private *userData(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } T *shellSurface; }; template void GenericShellSurface::setTitleCallback(wl_client *client, wl_resource *resource, const char *title) { auto s = userData(resource); Q_ASSERT(client == *s->client); s->setTitle(QString::fromUtf8(title)); } template void GenericShellSurface::setAppIdCallback(wl_client *client, wl_resource *resource, const char *app_id) { auto s = userData(resource); Q_ASSERT(client == *s->client); s->setWindowClass(QByteArray(app_id)); } template void GenericShellSurface::setTitle(const QString &t) { if (title == t) { return; } title = t; Q_Q(T); emit q->titleChanged(title); } template void GenericShellSurface::setWindowClass(const QByteArray &wc) { if (windowClass == wc) { return; } windowClass = wc; Q_Q(T); emit q->windowClassChanged(windowClass); } template void GenericShellSurface::moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) { auto s = userData(resource); Q_ASSERT(client == *s->client); emit s->q_func()->moveRequested(SeatInterface::get(seat), serial); } namespace { template Qt::Edges edgesToQtEdges(T edges); } template template void GenericShellSurface::resizeCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, uint32_t edges) { auto s = userData(resource); Q_ASSERT(client == *s->client); emit s->q_func()->resizeRequested(SeatInterface::get(seat), serial, edgesToQtEdges(U(edges))); } } } #endif diff --git a/src/server/global.cpp b/src/server/global.cpp index 6c765cd..2bbf79a 100644 --- a/src/server/global.cpp +++ b/src/server/global.cpp @@ -1,99 +1,85 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "global.h" #include "global_p.h" #include "display.h" // wayland #include namespace KWayland { namespace Server { Global::Private::Private(Display *d, const wl_interface *interface, quint32 version) : display(d) , m_interface(interface) , m_version(version) { } Global::Private::~Private() = default; void Global::Private::bind(wl_client *client, void *data, uint32_t version, uint32_t id) { auto d = reinterpret_cast(data); d->bind(client, version, id); } void Global::Private::create() { Q_ASSERT(!global); global = wl_global_create(*display, m_interface, m_version, this, bind); } Global::Global(Global::Private *d, QObject *parent) : QObject(parent) , d(d) { } Global::~Global() { destroy(); } void Global::create() { d->create(); } void Global::destroy() { if (!d->global) { return; } emit aboutToDestroyGlobal(); wl_global_destroy(d->global); d->global = nullptr; } bool Global::isValid() const { return d->global != nullptr; } Global::operator wl_global*() const { return d->global; } Global::operator wl_global*() { return d->global; } Display *Global::display() { return d->display; } } } diff --git a/src/server/global.h b/src/server/global.h index e13d6ba..e156ded 100644 --- a/src/server/global.h +++ b/src/server/global.h @@ -1,110 +1,96 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_GLOBAL_H #define WAYLAND_SERVER_GLOBAL_H #include #include struct wl_global; namespace KWayland { namespace Server { class Display; /** * @brief Base class for all Globals. * * Any class representing a Global should be derived from this base class. * This class provides common functionality for all globals. A global is an * object listed as an interface on the registry on client side. * * Normally a Global gets factored by the Display. For each Global-derived class there * is a dedicated factory method. After creating an instance through the factory method * it is not yet announced on the registry. One needs to call ::create on it. This allows * to setup the Global before it gets announced, ensuring that the client's state is correct * from the start. * * As an example shown for @link OutputInterface @endlink: * @code * Display *display; // The existing display * auto o = display->createOutput(); * o->setManufacturer(QStringLiteral("The KDE Community")); * // setup further data on the OutputInterface * o->create(); // announces OutputInterface * @endcode * * @see Display * **/ class KWAYLANDSERVER_EXPORT Global : public QObject { Q_OBJECT public: virtual ~Global(); /** * Creates the global by creating a native wl_global and by that announcing it * to the clients. **/ void create(); /** * Destroys the low level wl_global. Afterwards the Global is no longer shown to clients. **/ void destroy(); /** * @returns whether the Global got created **/ bool isValid() const; /** * @returns the Display the Global got created on. */ Display *display(); /** * Cast operator to the native wl_global this Global represents. **/ operator wl_global*(); /** * Cast operator to the native wl_global this Global represents. **/ operator wl_global*() const; Q_SIGNALS: /** * This signal is emitted when the client is in the process of removing the wl_global. * At the time the signal is emitted the global is still valid and allows to perform * cleanup tasks. */ void aboutToDestroyGlobal(); protected: class Private; explicit Global(Private *d, QObject *parent = nullptr); QScopedPointer d; }; } } #endif diff --git a/src/server/global_p.h b/src/server/global_p.h index 71f3736..2681e0d 100644 --- a/src/server/global_p.h +++ b/src/server/global_p.h @@ -1,56 +1,42 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_GLOBAL_P_H #define WAYLAND_SERVER_GLOBAL_P_H #include "global.h" struct wl_client; struct wl_interface; namespace KWayland { namespace Server { class Global::Private { public: static constexpr quint32 version = 0; virtual ~Private(); void create(); Display *display = nullptr; wl_global *global = nullptr; protected: Private(Display *d, const wl_interface *interface, quint32 version); virtual void bind(wl_client *client, uint32_t version, uint32_t id) = 0; static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); const wl_interface *const m_interface; const quint32 m_version; }; } } #endif diff --git a/src/server/idle_interface.cpp b/src/server/idle_interface.cpp index cada070..e695703 100644 --- a/src/server/idle_interface.cpp +++ b/src/server/idle_interface.cpp @@ -1,270 +1,256 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "idle_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "seat_interface.h" #include #include #include #include namespace KWayland { namespace Server { class IdleInterface::Private : public Global::Private { public: Private(IdleInterface *q, Display *d); int inhibitCount = 0; QVector idleTimeouts; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void getIdleTimeoutCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } IdleInterface *q; static const struct org_kde_kwin_idle_interface s_interface; static const quint32 s_version; }; class IdleTimeoutInterface::Private : public Resource::Private { public: Private(SeatInterface *seat, IdleTimeoutInterface *q, IdleInterface *manager, wl_resource *parentResource); ~Private(); void setup(quint32 timeout); void simulateUserActivity(); SeatInterface *seat; QTimer *timer = nullptr; private: static void simulateUserActivityCallback(wl_client *client, wl_resource *resource); IdleTimeoutInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_kwin_idle_timeout_interface s_interface; }; const quint32 IdleInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_idle_interface IdleInterface::Private::s_interface = { getIdleTimeoutCallback }; #endif IdleInterface::Private::Private(IdleInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_idle_interface, s_version) , q(q) { } void IdleInterface::Private::getIdleTimeoutCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout) { Private *p = cast(resource); SeatInterface *s = SeatInterface::get(seat); Q_ASSERT(s); IdleTimeoutInterface *idleTimeout = new IdleTimeoutInterface(s, p->q, resource); idleTimeout->create(p->display->getConnection(client), wl_resource_get_version(resource), id); if (!idleTimeout->resource()) { wl_resource_post_no_memory(resource); delete idleTimeout; return; } p->idleTimeouts << idleTimeout; QObject::connect(idleTimeout, &IdleTimeoutInterface::aboutToBeUnbound, p->q, [p, idleTimeout]() { p->idleTimeouts.removeOne(idleTimeout); }); idleTimeout->d_func()->setup(timeout); } void IdleInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_idle_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void IdleInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } IdleInterface::IdleInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } IdleInterface::~IdleInterface() = default; void IdleInterface::inhibit() { Q_D(); d->inhibitCount++; if (d->inhibitCount == 1) { emit inhibitedChanged(); } } void IdleInterface::uninhibit() { Q_D(); d->inhibitCount--; if (d->inhibitCount == 0) { emit inhibitedChanged(); } } bool IdleInterface::isInhibited() const { Q_D(); return d->inhibitCount > 0; } void IdleInterface::simulateUserActivity() { Q_D(); for (auto i : qAsConst(d->idleTimeouts)) { i->d_func()->simulateUserActivity(); } } IdleInterface::Private *IdleInterface::d_func() const { return reinterpret_cast(d.data()); } #ifndef K_DOXYGEN const struct org_kde_kwin_idle_timeout_interface IdleTimeoutInterface::Private::s_interface = { resourceDestroyedCallback, simulateUserActivityCallback }; #endif IdleTimeoutInterface::Private::Private(SeatInterface *seat, IdleTimeoutInterface *q, IdleInterface *manager, wl_resource *parentResource) : Resource::Private(q, manager, parentResource, &org_kde_kwin_idle_timeout_interface, &s_interface) , seat(seat) { } IdleTimeoutInterface::Private::~Private() = default; void IdleTimeoutInterface::Private::simulateUserActivityCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->simulateUserActivity(); } void IdleTimeoutInterface::Private::simulateUserActivity() { if (!timer) { // not yet configured return; } if (qobject_cast(global)->isInhibited()) { // ignored while inhibited return; } if (!timer->isActive() && resource) { org_kde_kwin_idle_timeout_send_resumed(resource); } timer->start(); } void IdleTimeoutInterface::Private::setup(quint32 timeout) { if (timer) { return; } timer = new QTimer(q); timer->setSingleShot(true); // less than 5 sec is not idle by definition timer->setInterval(qMax(timeout, 5000u)); QObject::connect(timer, &QTimer::timeout, q, [this] { if (resource) { org_kde_kwin_idle_timeout_send_idle(resource); } } ); if (qobject_cast(global)->isInhibited()) { // don't start if inhibited return; } timer->start(); } IdleTimeoutInterface::IdleTimeoutInterface(SeatInterface *seat, IdleInterface *parent, wl_resource *parentResource) : Resource(new Private(seat, this, parent, parentResource)) { connect(seat, &SeatInterface::timestampChanged, this, [this] { Q_D(); d->simulateUserActivity(); } ); connect(parent, &IdleInterface::inhibitedChanged, this, [this] { Q_D(); if (!d->timer) { // not yet configured return; } if (qobject_cast(d->global)->isInhibited()) { if (!d->timer->isActive() && d->resource) { org_kde_kwin_idle_timeout_send_resumed(d->resource); } d->timer->stop(); } else { d->timer->start(); } } ); } IdleTimeoutInterface::~IdleTimeoutInterface() = default; IdleTimeoutInterface::Private *IdleTimeoutInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/idle_interface.h b/src/server/idle_interface.h index c26acb1..fe9e012 100644 --- a/src/server/idle_interface.h +++ b/src/server/idle_interface.h @@ -1,140 +1,126 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_IDLE_INTERFACE_H #define KWAYLAND_SERVER_IDLE_INTERFACE_H #include #include "global.h" #include "resource.h" namespace KWayland { namespace Server { class Display; class SeatInterface; /** * @brief Global representing the org_kde_kwin_idle interface. * * The IdleInterface allows to register callbacks which are invoked if there has * not been any user activity (no input) for a specified time span on a seat. * * A client can bind an idle timeout for a SeatInterface and through that register * an idle timeout. The complete interaction is handled internally, thus the API * user only needs to create the IdleInterface in order to provide this feature. * * This interface is useful for clients as it allows them to perform power management, * chat applications might want to set to away after no user input for some time, etc. * * Of course this exposes the global input usage to all clients. Normally clients don't * know whether the input devices are used, only if their surfaces have focus. With this * interface it is possible to notice that there are input events. A server should consider * this to decide whether it wants to provide this feature! * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT IdleInterface : public Global { Q_OBJECT public: virtual ~IdleInterface(); /** * Inhibits the IdleInterface. While inhibited no IdleTimeoutInterface interface gets * notified about an idle timeout. * * This can be used to inhibit power management, screen locking, etc. directly from * Compositor side. * * To resume idle timeouts invoke @link{uninhibit}. It is possible to invoke inhibit several * times, in that case uninhibit needs to called the same amount as inhibit has been called. * @see uninhibit * @see isInhibited * @see inhibitedChanged * @since 5.41 **/ void inhibit(); /** * Inhibits the IdleInterface. The idle timeouts are only restarted if uninhibit has been * called the same amount as inhibit. * * @see inhibit * @see isInhibited * @see inhibitedChanged * @since 5.41 **/ void uninhibit(); /** * @returns Whether idle timeouts are currently inhibited * @see inhibit * @see uninhibit * @see inhibitedChanged * @since 5.41 **/ bool isInhibited() const; /** * Calling this method allows the Compositor to simulate user activity. * This means the same action is performed as if the user interacted with * an input device on the SeatInterface. * Idle timeouts are resumed and the idle time gets restarted. * @since 5.42 **/ void simulateUserActivity(); Q_SIGNALS: /** * Emitted when the system gets inhibited or uninhibited. * @see inhibit * @see uninhibit * @see isInhibited * @since 5.41 **/ void inhibitedChanged(); private: explicit IdleInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; // TODO: KF6 make private class class KWAYLANDSERVER_EXPORT IdleTimeoutInterface : public Resource { Q_OBJECT public: virtual ~IdleTimeoutInterface(); private: explicit IdleTimeoutInterface(SeatInterface *seat, IdleInterface *parent, wl_resource *parentResource); friend class IdleInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/idleinhibit_interface.cpp b/src/server/idleinhibit_interface.cpp index 97b46c2..c2622c9 100644 --- a/src/server/idleinhibit_interface.cpp +++ b/src/server/idleinhibit_interface.cpp @@ -1,53 +1,39 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "idleinhibit_interface_p.h" namespace KWayland { namespace Server { IdleInhibitManagerInterface::Private::Private(IdleInhibitManagerInterface *q, Display *d, const wl_interface *interface, quint32 version, IdleInhibitManagerInterfaceVersion interfaceVersion) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } IdleInhibitManagerInterface::IdleInhibitManagerInterface(Private *d, QObject *parent) : Global(d, parent) { } IdleInhibitManagerInterface::~IdleInhibitManagerInterface() = default; IdleInhibitManagerInterfaceVersion IdleInhibitManagerInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } IdleInhibitManagerInterface::Private *IdleInhibitManagerInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/idleinhibit_interface.h b/src/server/idleinhibit_interface.h index 6a5c7d9..dccc87f 100644 --- a/src/server/idleinhibit_interface.h +++ b/src/server/idleinhibit_interface.h @@ -1,77 +1,63 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser +/* + SPDX-FileCopyrightText: 2017 Martin Flöser -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_IDLEINHIBIT_INTERFACE_H #define KWAYLAND_SERVER_IDLEINHIBIT_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; /** * Enum describing the interface versions the IdleInhibitManagerInterface can support. * * @since 5.41 **/ enum class IdleInhibitManagerInterfaceVersion { /** * zwp_idle_inhibit_manager_v1 **/ UnstableV1 }; /** * The IdleInhibitorManagerInterface is used by clients to inhibit idle on a * SurfaceInterface. Whether a SurfaceInterface inhibits idle is exposes through * @link{SurfaceInterface::inhibitsIdle}. * * @since 5.41 **/ class KWAYLANDSERVER_EXPORT IdleInhibitManagerInterface : public Global { Q_OBJECT public: virtual ~IdleInhibitManagerInterface(); /** * @returns The interface version used by this IdleInhibitManagerInterface **/ IdleInhibitManagerInterfaceVersion interfaceVersion() const; protected: class Private; explicit IdleInhibitManagerInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; } } #endif diff --git a/src/server/idleinhibit_interface_p.h b/src/server/idleinhibit_interface_p.h index 3577ddc..f105e85 100644 --- a/src/server/idleinhibit_interface_p.h +++ b/src/server/idleinhibit_interface_p.h @@ -1,96 +1,82 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_IDLEINHIBIT_INTERFACE_P_H #define KWAYLAND_SERVER_IDLEINHIBIT_INTERFACE_P_H #include "idleinhibit_interface.h" #include "global_p.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN IdleInhibitManagerUnstableV1Interface : public IdleInhibitManagerInterface { Q_OBJECT public: explicit IdleInhibitManagerUnstableV1Interface(Display *display, QObject *parent = nullptr); ~IdleInhibitManagerUnstableV1Interface() override; private: class Private; }; class Q_DECL_HIDDEN IdleInhibitManagerInterface::Private : public Global::Private { public: IdleInhibitManagerInterfaceVersion interfaceVersion; protected: Private(IdleInhibitManagerInterface *q, Display *d, const wl_interface *interface, quint32 version, IdleInhibitManagerInterfaceVersion interfaceVersion); IdleInhibitManagerInterface *q; }; class Q_DECL_HIDDEN IdleInhibitorInterface : public Resource { Q_OBJECT public: explicit IdleInhibitorInterface(IdleInhibitManagerInterface *c, wl_resource *parentResource); virtual ~IdleInhibitorInterface(); /** * @returns The interface version used by this IdleInhibitorInterface **/ IdleInhibitManagerInterfaceVersion interfaceVersion() const; protected: class Private; private: Private *d_func() const; friend class IdleInhibitManagerUnstableV1Interface; }; class Q_DECL_HIDDEN IdleInhibitorInterface::Private : public Resource::Private { public: Private(IdleInhibitorInterface *q, IdleInhibitManagerInterface *m, wl_resource *parentResource); ~Private(); private: IdleInhibitorInterface *q_func() { return reinterpret_cast(q); } static const struct zwp_idle_inhibitor_v1_interface s_interface; }; } } #endif diff --git a/src/server/idleinhibit_interface_v1.cpp b/src/server/idleinhibit_interface_v1.cpp index a2d2f0c..5d7bb87 100644 --- a/src/server/idleinhibit_interface_v1.cpp +++ b/src/server/idleinhibit_interface_v1.cpp @@ -1,139 +1,125 @@ -/**************************************************************************** -Copyright 2017 Martin Flöser - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Martin Flöser + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "idleinhibit_interface_p.h" #include "display.h" #include "surface_interface_p.h" namespace KWayland { namespace Server { class Q_DECL_HIDDEN IdleInhibitManagerUnstableV1Interface::Private : public IdleInhibitManagerInterface::Private { public: Private(IdleInhibitManagerUnstableV1Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void createInhibitorCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static const struct zwp_idle_inhibit_manager_v1_interface s_interface; static const quint32 s_version; }; const quint32 IdleInhibitManagerUnstableV1Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zwp_idle_inhibit_manager_v1_interface IdleInhibitManagerUnstableV1Interface::Private::s_interface = { destroyCallback, createInhibitorCallback }; #endif IdleInhibitManagerUnstableV1Interface::Private::Private(IdleInhibitManagerUnstableV1Interface *q, Display *d) : IdleInhibitManagerInterface::Private(q, d, &zwp_idle_inhibit_manager_v1_interface, s_version, IdleInhibitManagerInterfaceVersion::UnstableV1) { } void IdleInhibitManagerUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void IdleInhibitManagerUnstableV1Interface::Private::createInhibitorCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto s = SurfaceInterface::get(surface); if (!s) { // send error? return; } auto q = cast(resource); auto inhibitor = new IdleInhibitorInterface(q->q, resource); inhibitor->d_func()->create(q->display->getConnection(client), version, id); s->d_func()->installIdleInhibitor(inhibitor); } void IdleInhibitManagerUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zwp_idle_inhibit_manager_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void IdleInhibitManagerUnstableV1Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } IdleInhibitManagerUnstableV1Interface::IdleInhibitManagerUnstableV1Interface(Display *display, QObject *parent) : IdleInhibitManagerInterface(new Private(this, display), parent) { } IdleInhibitManagerUnstableV1Interface::~IdleInhibitManagerUnstableV1Interface() = default; #ifndef K_DOXYGEN const struct zwp_idle_inhibitor_v1_interface IdleInhibitorInterface::Private::s_interface = { resourceDestroyedCallback }; #endif IdleInhibitorInterface::Private::Private(IdleInhibitorInterface *q, IdleInhibitManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zwp_idle_inhibitor_v1_interface, &s_interface) { } IdleInhibitorInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } IdleInhibitorInterface::IdleInhibitorInterface(IdleInhibitManagerInterface *m, wl_resource *parentResource) : Resource(new Private(this, m, parentResource)) { } IdleInhibitorInterface::~IdleInhibitorInterface() = default; IdleInhibitorInterface::Private *IdleInhibitorInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/keyboard_interface.cpp b/src/server/keyboard_interface.cpp index 8a9c02a..9dbd3e3 100644 --- a/src/server/keyboard_interface.cpp +++ b/src/server/keyboard_interface.cpp @@ -1,192 +1,178 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "keyboard_interface.h" #include "keyboard_interface_p.h" #include "display.h" #include "seat_interface.h" #include "surface_interface.h" // Qt #include // Wayland #include namespace KWayland { namespace Server { KeyboardInterface::Private::Private(SeatInterface *s, wl_resource *parentResource, KeyboardInterface *q) : Resource::Private(q, s, parentResource, &wl_keyboard_interface, &s_interface) , seat(s) { } void KeyboardInterface::Private::focusChildSurface(const QPointer &childSurface, quint32 serial) { if (focusedChildSurface == childSurface) { return; } sendLeave(focusedChildSurface.data(), serial); focusedChildSurface = childSurface; sendEnter(focusedChildSurface.data(), serial); } void KeyboardInterface::Private::sendLeave(SurfaceInterface *surface, quint32 serial) { if (surface && resource && surface->resource()) { wl_keyboard_send_leave(resource, serial, surface->resource()); } } void KeyboardInterface::Private::sendEnter(SurfaceInterface *surface, quint32 serial) { wl_array keys; wl_array_init(&keys); const auto states = seat->pressedKeys(); for (auto it = states.constBegin(); it != states.constEnd(); ++it) { uint32_t *k = reinterpret_cast(wl_array_add(&keys, sizeof(uint32_t))); *k = *it; } wl_keyboard_send_enter(resource, serial, surface->resource(), &keys); wl_array_release(&keys); sendModifiers(); } #ifndef K_DOXYGEN const struct wl_keyboard_interface KeyboardInterface::Private::s_interface { resourceDestroyedCallback }; #endif KeyboardInterface::KeyboardInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { } KeyboardInterface::~KeyboardInterface() = default; void KeyboardInterface::setKeymap(int fd, quint32 size) { Q_D(); d->sendKeymap(fd, size); } void KeyboardInterface::Private::sendKeymap(int fd, quint32 size) { if (!resource) { return; } wl_keyboard_send_keymap(resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fd, size); } void KeyboardInterface::Private::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial) { if (!resource) { return; } wl_keyboard_send_modifiers(resource, serial, depressed, latched, locked, group); } void KeyboardInterface::Private::sendModifiers() { sendModifiers(seat->depressedModifiers(), seat->latchedModifiers(), seat->lockedModifiers(), seat->groupModifiers(), seat->lastModifiersSerial()); } void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial) { Q_D(); d->sendLeave(d->focusedChildSurface, serial); disconnect(d->destroyConnection); d->focusedChildSurface.clear(); d->focusedSurface = surface; if (!d->focusedSurface) { return; } d->destroyConnection = connect(d->focusedSurface, &Resource::aboutToBeUnbound, this, [this] { Q_D(); if (d->resource) { wl_keyboard_send_leave(d->resource, d->global->display()->nextSerial(), d->focusedSurface->resource()); } d->focusedSurface = nullptr; d->focusedChildSurface.clear(); } ); d->focusedChildSurface = QPointer(surface); d->sendEnter(d->focusedSurface, serial); d->client->flush(); } void KeyboardInterface::keyPressed(quint32 key, quint32 serial) { Q_D(); if (!d->resource) { return; } Q_ASSERT(d->focusedSurface); wl_keyboard_send_key(d->resource, serial, d->seat->timestamp(), key, WL_KEYBOARD_KEY_STATE_PRESSED); } void KeyboardInterface::keyReleased(quint32 key, quint32 serial) { Q_D(); if (!d->resource) { return; } Q_ASSERT(d->focusedSurface); wl_keyboard_send_key(d->resource, serial, d->seat->timestamp(), key, WL_KEYBOARD_KEY_STATE_RELEASED); } void KeyboardInterface::updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); d->sendModifiers(depressed, latched, locked, group, serial); } void KeyboardInterface::repeatInfo(qint32 charactersPerSecond, qint32 delay) { Q_D(); if (!d->resource) { return; } if (wl_resource_get_version(d->resource) < WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { // only supported since version 4 return; } wl_keyboard_send_repeat_info(d->resource, charactersPerSecond, delay); } SurfaceInterface *KeyboardInterface::focusedSurface() const { Q_D(); return d->focusedSurface; } KeyboardInterface::Private *KeyboardInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/keyboard_interface.h b/src/server/keyboard_interface.h index 17d1d20..d6e9b91 100644 --- a/src/server/keyboard_interface.h +++ b/src/server/keyboard_interface.h @@ -1,69 +1,55 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_KEYBOARD_INTERFACE_H #define WAYLAND_SERVER_KEYBOARD_INTERFACE_H #include #include "resource.h" namespace KWayland { namespace Server { class SeatInterface; class SurfaceInterface; /** * @brief Resource for the wl_keyboard interface. * **/ class KWAYLANDSERVER_EXPORT KeyboardInterface : public Resource { Q_OBJECT public: virtual ~KeyboardInterface(); /** * @returns the focused SurfaceInterface on this keyboard resource, if any. **/ SurfaceInterface *focusedSurface() const; private: void setFocusedSurface(SurfaceInterface *surface, quint32 serial); void setKeymap(int fd, quint32 size); void updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial); void keyPressed(quint32 key, quint32 serial); void keyReleased(quint32 key, quint32 serial); void repeatInfo(qint32 charactersPerSecond, qint32 delay); friend class SeatInterface; explicit KeyboardInterface(SeatInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::KeyboardInterface*) #endif diff --git a/src/server/keyboard_interface_p.h b/src/server/keyboard_interface_p.h index c7fba23..cb22acf 100644 --- a/src/server/keyboard_interface_p.h +++ b/src/server/keyboard_interface_p.h @@ -1,56 +1,42 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_KEYBOARD_INTERFACE_P_H #define WAYLAND_SERVER_KEYBOARD_INTERFACE_P_H #include "keyboard_interface.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class KeyboardInterface::Private : public Resource::Private { public: Private(SeatInterface *s, wl_resource *parentResource, KeyboardInterface *q); void sendKeymap(int fd, quint32 size); void sendModifiers(); void sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial); void focusChildSurface(const QPointer &childSurface, quint32 serial); void sendLeave(SurfaceInterface *surface, quint32 serial); void sendEnter(SurfaceInterface *surface, quint32 serial); SeatInterface *seat; SurfaceInterface *focusedSurface = nullptr; QPointer focusedChildSurface; QMetaObject::Connection destroyConnection; private: static const struct wl_keyboard_interface s_interface; }; } } #endif diff --git a/src/server/keystate_interface.cpp b/src/server/keystate_interface.cpp index 2ad3a09..fb04fa7 100644 --- a/src/server/keystate_interface.cpp +++ b/src/server/keystate_interface.cpp @@ -1,96 +1,82 @@ -/******************************************************************** -Copyright 2019 Aleix Pol Gonzalez - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "keystate_interface.h" #include "global_p.h" #include "display.h" #include #include #include #include namespace KWayland { namespace Server { class KeyStateInterface::Private : public Global::Private { public: Private(Display *d) : Global::Private(d, &org_kde_kwin_keystate_interface, s_version) {} void bind(wl_client * client, uint32_t version, uint32_t id) override { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_keystate_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); m_resources << resource; } static void unbind(wl_resource *resource) { auto *d = reinterpret_cast(wl_resource_get_user_data(resource)); d->m_resources.removeAll(resource); } static void fetchStatesCallback(struct wl_client */*client*/, struct wl_resource *resource) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); for (int i=0; im_keyStates.count(); ++i) org_kde_kwin_keystate_send_stateChanged(resource, i, s->m_keyStates[i]); } static const quint32 s_version; QVector m_resources; QVector m_keyStates = QVector(3, Unlocked); static const struct org_kde_kwin_keystate_interface s_interface; }; const quint32 KeyStateInterface::Private::s_version = 1; KeyStateInterface::KeyStateInterface(Display* d, QObject* parent) : Global(new Private(d), parent) {} KeyStateInterface::~KeyStateInterface() = default; const struct org_kde_kwin_keystate_interface KeyStateInterface::Private::s_interface = { fetchStatesCallback }; void KeyStateInterface::setState(KeyStateInterface::Key key, KeyStateInterface::State state) { auto dptr = static_cast(d.data()); dptr->m_keyStates[int(key)] = state; for(auto r : qAsConst(dptr->m_resources)) { org_kde_kwin_keystate_send_stateChanged(r, int(key), int(state)); } } } } diff --git a/src/server/keystate_interface.h b/src/server/keystate_interface.h index 9ad1f3c..e2f3f94 100644 --- a/src/server/keystate_interface.h +++ b/src/server/keystate_interface.h @@ -1,71 +1,57 @@ -/******************************************************************** -Copyright 2019 Aleix Pol Gonzalez +/* + SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_KEYSTATE_INTERFACE_H #define KWAYLAND_KEYSTATE_INTERFACE_H #include #include "global.h" #include "resource.h" namespace KWayland { namespace Server { class Display; /** * @brief Exposes key states to wayland clients * * @since 5.58 **/ class KWAYLANDSERVER_EXPORT KeyStateInterface : public Global { Q_OBJECT public: virtual ~KeyStateInterface(); enum class Key { CapsLock = 0, NumLock = 1, ScrollLock = 2, }; Q_ENUM(Key); enum State { Unlocked = 0, Latched = 1, Locked = 2, }; Q_ENUM(State) void setState(Key k, State s); private: explicit KeyStateInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } #endif diff --git a/src/server/linuxdmabuf_v1_interface.cpp b/src/server/linuxdmabuf_v1_interface.cpp index 227cf1d..4d7c1aa 100644 --- a/src/server/linuxdmabuf_v1_interface.cpp +++ b/src/server/linuxdmabuf_v1_interface.cpp @@ -1,510 +1,496 @@ -/******************************************************************** -Copyright © 2019 Roman Gilg -Copyright © 2018 Fredrik Höglund - -Based on the libweston implementation, -Copyright © 2014, 2015 Collabora, Ltd. - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2019 Roman Gilg + SPDX-FileCopyrightText: 2018 Fredrik Höglund + + Based on the libweston implementation, + SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd. + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "linuxdmabuf_v1_interface.h" #include "drm_fourcc.h" #include "global_p.h" #include "wayland-linux-dmabuf-unstable-v1-server-protocol.h" #include "wayland-server-protocol.h" #include #include #include #include #include namespace KWayland { namespace Server { class LinuxDmabufBuffer::Private { public: Private(LinuxDmabufBuffer *_q) : q(_q) { q->d = this; } virtual ~Private() = default; virtual uint32_t format() const = 0; virtual QSize size() const = 0; LinuxDmabufBuffer *q; }; LinuxDmabufBuffer::LinuxDmabufBuffer() { } uint32_t LinuxDmabufBuffer::format() const { return d->format(); } QSize LinuxDmabufBuffer::size() const { return d->size(); } class LinuxDmabufUnstableV1Buffer::Private : LinuxDmabufBuffer::Private { public: Private(LinuxDmabufUnstableV1Buffer *_q) : LinuxDmabufBuffer::Private(_q) , q(_q) { } ~Private() override = default; uint32_t format() const override { return m_format; } QSize size() const override { return m_size; } uint32_t m_format; QSize m_size; LinuxDmabufUnstableV1Buffer *q; }; LinuxDmabufUnstableV1Buffer::LinuxDmabufUnstableV1Buffer(uint32_t format, const QSize &size) : LinuxDmabufBuffer() , d(new LinuxDmabufUnstableV1Buffer::Private(this)) { d->m_format = format; d->m_size = size; } typedef LinuxDmabufUnstableV1Interface V1Iface; class V1Iface::Private : public Global::Private { public: Private(V1Iface *q, Display *display); ~Private(); static const struct wl_buffer_interface *bufferImplementation() { return &s_bufferImplementation; } V1Iface::Impl *impl; QHash > supportedFormatsWithModifiers; V1Iface * const q; static const uint32_t s_version; void bind(wl_client *client, uint32_t version, uint32_t id) override final; void createParams(wl_client *client, wl_resource *resource, uint32_t id); static void unbind(wl_client *client, wl_resource *resource); static void createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id); private: class Params { public: Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id); ~Params(); void postNoMemory() { wl_resource_post_no_memory(m_resource); } wl_resource *resource() const { return m_resource; } void add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier); void create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags); static void destroy(wl_client *client, wl_resource *resource); static void add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo); static void create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags); static void createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags); private: static const struct zwp_linux_buffer_params_v1_interface s_interface; wl_resource *m_resource; V1Iface::Private *m_dmabufInterface; std::array m_planes; size_t m_planeCount = 0; bool m_createRequested = false; }; static const struct zwp_linux_dmabuf_v1_interface s_implementation; static const struct wl_buffer_interface s_bufferImplementation; }; void V1Iface::Private::Params::create(wl_client *client, wl_resource *resource, int width, int height, uint32_t format, uint32_t flags) { Q_UNUSED(client) V1Iface::Private::Params *params = static_cast(wl_resource_get_user_data(resource)); assert(params->m_resource == resource); params->create(client, 0, QSize(width, height), format, flags); } void V1Iface::Private::Params::createImmed(wl_client *client, wl_resource *resource, uint32_t new_id, int width, int height, uint32_t format, uint32_t flags) { Q_UNUSED(client) V1Iface::Private::Params *params = static_cast(wl_resource_get_user_data(resource)); assert(params->m_resource == resource); params->create(client, new_id, QSize(width, height), format, flags); } #ifndef K_DOXYGEN const struct zwp_linux_dmabuf_v1_interface V1Iface::Private::s_implementation = { [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); }, // unbind createParamsCallback }; const struct wl_buffer_interface V1Iface::Private::s_bufferImplementation = { [](wl_client *, wl_resource *resource) { wl_resource_destroy(resource); } // destroy }; #ifndef K_DOXYGEN const struct zwp_linux_buffer_params_v1_interface V1Iface::Private::Params::s_interface = { destroy, add, create, createImmed }; #endif V1Iface::Private::Params::Params(V1Iface::Private *dmabufInterface, wl_client *client, uint32_t version, uint32_t id) : m_dmabufInterface(dmabufInterface) { m_resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, id); if (!m_resource) { return; } wl_resource_set_implementation(m_resource, &s_interface, this, [](wl_resource *resource) { delete static_cast(wl_resource_get_user_data(resource)); }); for (auto &plane : m_planes) { plane.fd = -1; plane.offset = 0; plane.stride = 0; plane.modifier = 0; } } V1Iface::Private::Params::~Params() { // Close the file descriptors for (auto &plane : m_planes) { if (plane.fd != -1) { ::close(plane.fd); } } } void V1Iface::Private::Params::create(wl_client *client, uint32_t bufferId, const QSize &size, uint32_t format, uint32_t flags) { // Validate the parameters // ----------------------- const uint32_t width = size.width(); const uint32_t height = size.height(); if (m_createRequested) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer"); return; } m_createRequested = true; if (m_planeCount == 0) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added to the params"); return; } // Check for holes in the dmabufs set (e.g. [0, 1, 3]) for (uint32_t i = 0; i < m_planeCount; i++) { if (m_planes[i].fd != -1) continue; wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added for plane %i", i); return; } if (width < 1 || height < 1) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "invalid width %d or height %d", width, height); return; } for (uint32_t i = 0; i < m_planeCount; i++) { auto &plane = m_planes[i]; if (uint64_t(plane.offset) + plane.stride > UINT32_MAX) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %i", i); return; } if (i == 0 && uint64_t(plane.offset) + plane.stride * height > UINT32_MAX) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %i", i); return; } // Don't report an error as it might be caused by the kernel not supporting seeking on dmabuf off_t size = ::lseek(plane.fd, 0, SEEK_END); if (size == -1) continue; if (plane.offset >= size) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid offset %i for plane %i", plane.offset, i); return; } if (plane.offset + plane.stride > size) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid stride %i for plane %i", plane.stride, i); return; } // Only valid for first plane as other planes might be // sub-sampled according to fourcc format if (i == 0 && plane.offset + plane.stride * height > size) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid buffer stride or height for plane %i", i); return; } } // Import the buffer // ----------------- QVector planes; planes.reserve(m_planeCount); for (uint32_t i = 0; i < m_planeCount; i++) planes << m_planes[i]; LinuxDmabufUnstableV1Buffer *buffer = m_dmabufInterface->impl->importBuffer(planes, format, size, (V1Iface::Flags) flags); if (buffer) { // The buffer has ownership of the file descriptors now for (auto &plane : m_planes) { plane.fd = -1; } wl_resource *resource = wl_resource_create(client, &wl_buffer_interface, 1, bufferId); if (!resource ) { postNoMemory(); delete buffer; return; } wl_resource_set_implementation(resource, m_dmabufInterface->q->bufferImplementation(), buffer, [](wl_resource *resource) { // Destructor delete static_cast(wl_resource_get_user_data(resource)); }); // XXX Do we need this? //buffer->setResource(resource); // Send a 'created' event when the request is not for an immediate import, i.e. bufferId is zero if (bufferId == 0) { zwp_linux_buffer_params_v1_send_created(m_resource, resource); } } else { if (bufferId == 0) { zwp_linux_buffer_params_v1_send_failed(m_resource); } else { // since the behavior is left implementation defined by the // protocol in case of create_immed failure due to an unknown cause, // we choose to treat it as a fatal error and immediately kill the // client instead of creating an invalid handle and waiting for it // to be used. wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "importing the supplied dmabufs failed"); } } } void V1Iface::Private::Params::add(int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint64_t modifier) { if (m_createRequested) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer"); ::close(fd); return; } if (plane_idx >= m_planes.size()) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane index %u is too high", plane_idx); ::close(fd); return; } auto &plane = m_planes[plane_idx]; if (plane.fd != -1) { wl_resource_post_error(m_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, "a dmabuf has already been added for plane %u", plane_idx); ::close(fd); return; } plane.fd = fd; plane.offset = offset; plane.stride = stride; plane.modifier = modifier; m_planeCount++; } void V1Iface::Private::Params::destroy(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void V1Iface::Private::Params::add(wl_client *client, wl_resource *resource, int fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) { Q_UNUSED(client) V1Iface::Private::Params *params = static_cast(wl_resource_get_user_data(resource)); assert(params->m_resource == resource); params->add(fd, plane_idx, offset, stride, (uint64_t(modifier_hi) << 32) | modifier_lo); } const uint32_t V1Iface::Private::s_version = 3; #endif V1Iface::Private::Private(V1Iface *q, Display *display) : Global::Private(display, &zwp_linux_dmabuf_v1_interface, s_version), q(q) { } V1Iface::Private::~Private() = default; void V1Iface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, std::min(s_version, version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_implementation, this, nullptr); // Send formats & modifiers // ------------------------ QHash>::const_iterator it = supportedFormatsWithModifiers.constBegin(); while (it != supportedFormatsWithModifiers.constEnd()) { QSet modifiers = it.value(); if (modifiers.isEmpty()) { modifiers << DRM_FORMAT_MOD_INVALID; } for (uint64_t modifier : qAsConst(modifiers)) { if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { const uint32_t modifier_lo = modifier & 0xFFFFFFFF; const uint32_t modifier_hi = modifier >> 32; zwp_linux_dmabuf_v1_send_modifier(resource, it.key(), modifier_hi, modifier_lo); } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { zwp_linux_dmabuf_v1_send_format(resource, it.key()); } } it++; } } void V1Iface::Private::createParams(wl_client *client, wl_resource *resource, uint32_t id) { Params *params = new Params(this, client, wl_resource_get_version(resource), id); if (!params->resource()) { wl_resource_post_no_memory(resource); delete params; } } void V1Iface::Private::createParamsCallback(wl_client *client, wl_resource *resource, uint32_t id) { V1Iface::Private *global = static_cast(wl_resource_get_user_data(resource)); global->createParams(client, resource, id); } V1Iface::LinuxDmabufUnstableV1Interface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } V1Iface::~LinuxDmabufUnstableV1Interface() = default; void V1Iface::setImpl(V1Iface::Impl *impl) { d_func()->impl = impl; } void V1Iface::setSupportedFormatsWithModifiers(QHash > set) { d_func()->supportedFormatsWithModifiers = set; } const struct wl_buffer_interface *V1Iface::bufferImplementation() { return V1Iface::Private::bufferImplementation(); } V1Iface::Private *V1Iface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/linuxdmabuf_v1_interface.h b/src/server/linuxdmabuf_v1_interface.h index 5a6e47a..65fd5ea 100644 --- a/src/server/linuxdmabuf_v1_interface.h +++ b/src/server/linuxdmabuf_v1_interface.h @@ -1,185 +1,171 @@ -/******************************************************************** -Copyright © 2019 Roman Gilg -Copyright © 2018 Fredrik Höglund - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2019 Roman Gilg + SPDX-FileCopyrightText: 2018 Fredrik Höglund + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H #define WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H #include "global.h" #include "resource.h" #include #include #include #include struct wl_buffer_interface; namespace KWayland { namespace Server { class BufferInterface; /** * The base class for linux-dmabuf buffers * * Compositors should reimplement this class to store objects specific * to the underlying graphics stack. */ class KWAYLANDSERVER_EXPORT LinuxDmabufBuffer { public: LinuxDmabufBuffer(); virtual ~LinuxDmabufBuffer() = default; /** * Returns the DRM format code for the buffer. */ uint32_t format() const; /** * Returns the size of the buffer. */ QSize size() const; private: class Private; Private *d; friend class LinuxDmabufUnstableV1Buffer; }; class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Buffer : public LinuxDmabufBuffer { public: /** * Creates a new Buffer. */ LinuxDmabufUnstableV1Buffer(uint32_t format, const QSize &size); /** * Destroys the Buffer. */ virtual ~LinuxDmabufUnstableV1Buffer() = default; private: class Private; Private *d; }; /** * Represents the global zpw_linux_dmabuf_v1 interface. * * This interface provides a way for clients to create generic dmabuf based wl_buffers. */ class KWAYLANDSERVER_EXPORT LinuxDmabufUnstableV1Interface : public Global { Q_OBJECT public: enum Flag { YInverted = (1 << 0), /// Contents are y-inverted Interlaced = (1 << 1), /// Content is interlaced BottomFieldFirst = (1 << 2) /// Bottom field first }; Q_DECLARE_FLAGS(Flags, Flag) /** * Represents a plane in a buffer */ struct Plane { int fd; /// The dmabuf file descriptor uint32_t offset; /// The offset from the start of buffer uint32_t stride; /// The distance from the start of a row to the next row in bytes uint64_t modifier; /// The layout modifier }; /** * The Iface class provides an interface from the LinuxDmabufInterface into the compositor */ class Impl { public: Impl() = default; virtual ~Impl() = default; /** * Imports a linux-dmabuf buffer into the compositor. * * The parent LinuxDmabufUnstableV1Interface class takes ownership of returned * buffer objects. * * In return the returned buffer takes ownership of the file descriptor for each * plane. * * Note that it is the responsibility of the caller to close the file descriptors * when the import fails. * * @return The imported buffer on success, and nullptr otherwise. */ virtual LinuxDmabufUnstableV1Buffer *importBuffer(const QVector &planes, uint32_t format, const QSize &size, Flags flags) = 0; }; /** * Destroys the LinuxDmabufUnstableV1Interface. */ virtual ~LinuxDmabufUnstableV1Interface(); /** * Sets the compositor implementation for the dmabuf interface. * * The ownership is not transferred by this call. */ void setImpl(Impl *impl); void setSupportedFormatsWithModifiers(QHash > set); /** * Returns the LinuxDmabufInterface for the given resource. **/ static LinuxDmabufUnstableV1Interface *get(wl_resource *native); private: /** * @internal */ explicit LinuxDmabufUnstableV1Interface(Display *display, QObject *parent = nullptr); /** * Returns a pointer to the wl_buffer implementation for imported dmabufs. */ static const struct wl_buffer_interface *bufferImplementation(); friend class Display; friend class BufferInterface; class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::LinuxDmabufUnstableV1Interface*) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::LinuxDmabufUnstableV1Interface::Flags) #endif // WAYLAND_SERVER_LINUXDMABUF_INTERFACE_H diff --git a/src/server/output_interface.cpp b/src/server/output_interface.cpp index cf804f4..ddfccd1 100644 --- a/src/server/output_interface.cpp +++ b/src/server/output_interface.cpp @@ -1,546 +1,532 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "output_interface.h" #include "global_p.h" #include "display.h" #include #include namespace KWayland { namespace Server { class OutputInterface::Private : public Global::Private { public: struct ResourceData { wl_resource *resource; uint32_t version; }; Private(OutputInterface *q, Display *d); ~Private(); void sendMode(wl_resource *resource, const Mode &mode); void sendDone(const ResourceData &data); void updateGeometry(); void updateScale(); QSize physicalSize; QPoint globalPosition; QString manufacturer = QStringLiteral("org.kde.kwin"); QString model = QStringLiteral("none"); int scale = 1; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; QList modes; QList resources; struct { DpmsMode mode = DpmsMode::On; bool supported = false; } dpms; static OutputInterface *get(wl_resource *native); private: static Private *cast(wl_resource *native); static void releaseCallback(wl_client *client, wl_resource *resource); static void unbind(wl_resource *resource); void bind(wl_client *client, uint32_t version, uint32_t id) override; int32_t toTransform() const; int32_t toSubPixel() const; void sendGeometry(wl_resource *resource); void sendScale(const ResourceData &data); OutputInterface *q; static QVector s_privates; static const struct wl_output_interface s_interface; static const quint32 s_version; }; QVector OutputInterface::Private::s_privates; const quint32 OutputInterface::Private::s_version = 3; OutputInterface::Private::Private(OutputInterface *q, Display *d) : Global::Private(d, &wl_output_interface, s_version) , q(q) { s_privates << this; } OutputInterface::Private::~Private() { s_privates.removeAll(this); } #ifndef K_DOXYGEN const struct wl_output_interface OutputInterface::Private::s_interface = { releaseCallback }; #endif void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); unbind(resource); } OutputInterface *OutputInterface::Private::get(wl_resource *native) { if (Private *p = cast(native)) { return p->q; } return nullptr; } OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native) { for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) { const auto &resources = (*it)->resources; auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native] (const ResourceData &data) { return data.resource == native; }); if (rit != resources.constEnd()) { return (*it); } } return nullptr; } OutputInterface::OutputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &OutputInterface::currentModeChanged, this, [this, d] { auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); }); if (currentModeIt == d->modes.constEnd()) { return; } for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendMode((*it).resource, *currentModeIt); d->sendDone(*it); } wl_display_flush_clients(*(d->display)); } ); connect(this, &OutputInterface::subPixelChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputInterface::transformChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputInterface::globalPositionChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputInterface::modelChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputInterface::manufacturerChanged, this, [this, d] { d->updateGeometry(); }); connect(this, &OutputInterface::scaleChanged, this, [this, d] { d->updateScale(); }); } OutputInterface::~OutputInterface() = default; QSize OutputInterface::pixelSize() const { Q_D(); auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.constEnd()) { return QSize(); } return (*it).size; } int OutputInterface::refreshRate() const { Q_D(); auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.constEnd()) { return 60000; } return (*it).refreshRate; } void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate) { Q_ASSERT(!isValid()); Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) { // no mode with current flag - enforce flags |= ModeFlag::Current; } if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } if (flags.testFlag(ModeFlag::Preferred)) { // remove from existing Preferred mode auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Preferred); } ); if (preferredIt != d->modes.end()) { (*preferredIt).flags &= ~uint(ModeFlag::Preferred); } } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size,refreshRate](const Mode &mode) { return mode.size == size && mode.refreshRate == refreshRate; } ); auto emitChanges = [this,flags,size,refreshRate] { emit modesChanged(); if (flags.testFlag(ModeFlag::Current)) { emit refreshRateChanged(refreshRate); emit pixelSizeChanged(size); emit currentModeChanged(); } }; if (existingModeIt != d->modes.end()) { if ((*existingModeIt).flags == flags) { // nothing to do return; } (*existingModeIt).flags = flags; emitChanges(); return; } Mode mode; mode.size = size; mode.refreshRate = refreshRate; mode.flags = flags; d->modes << mode; emitChanges(); } void OutputInterface::setCurrentMode(const QSize &size, int refreshRate) { Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt != d->modes.end()) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size,refreshRate](const Mode &mode) { return mode.size == size && mode.refreshRate == refreshRate; } ); Q_ASSERT(existingModeIt != d->modes.end()); (*existingModeIt).flags |= ModeFlag::Current; emit modesChanged(); emit refreshRateChanged((*existingModeIt).refreshRate); emit pixelSizeChanged((*existingModeIt).size); emit currentModeChanged(); } int32_t OutputInterface::Private::toTransform() const { switch (transform) { case Transform::Normal: return WL_OUTPUT_TRANSFORM_NORMAL; case Transform::Rotated90: return WL_OUTPUT_TRANSFORM_90; case Transform::Rotated180: return WL_OUTPUT_TRANSFORM_180; case Transform::Rotated270: return WL_OUTPUT_TRANSFORM_270; case Transform::Flipped: return WL_OUTPUT_TRANSFORM_FLIPPED; case Transform::Flipped90: return WL_OUTPUT_TRANSFORM_FLIPPED_90; case Transform::Flipped180: return WL_OUTPUT_TRANSFORM_FLIPPED_180; case Transform::Flipped270: return WL_OUTPUT_TRANSFORM_FLIPPED_270; } abort(); } int32_t OutputInterface::Private::toSubPixel() const { switch (subPixel) { case SubPixel::Unknown: return WL_OUTPUT_SUBPIXEL_UNKNOWN; case SubPixel::None: return WL_OUTPUT_SUBPIXEL_NONE; case SubPixel::HorizontalRGB: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; case SubPixel::HorizontalBGR: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; case SubPixel::VerticalRGB: return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; case SubPixel::VerticalBGR: return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; } abort(); } void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_user_data(resource, this); wl_resource_set_implementation(resource, &s_interface, this, unbind); ResourceData r; r.resource = resource; r.version = version; resources << r; sendGeometry(resource); sendScale(r); auto currentModeIt = modes.constEnd(); for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) { const Mode &mode = *it; if (mode.flags.testFlag(ModeFlag::Current)) { // needs to be sent as last mode currentModeIt = it; continue; } sendMode(resource, mode); } if (currentModeIt != modes.constEnd()) { sendMode(resource, *currentModeIt); } sendDone(r); c->flush(); } void OutputInterface::Private::unbind(wl_resource *resource) { Private *o = cast(resource); if (!o) { return; } auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; }); if (it != o->resources.end()) { o->resources.erase(it); } } void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode) { int32_t flags = 0; if (mode.flags.testFlag(ModeFlag::Current)) { flags |= WL_OUTPUT_MODE_CURRENT; } if (mode.flags.testFlag(ModeFlag::Preferred)) { flags |= WL_OUTPUT_MODE_PREFERRED; } wl_output_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate); } void OutputInterface::Private::sendGeometry(wl_resource *resource) { wl_output_send_geometry(resource, globalPosition.x(), globalPosition.y(), physicalSize.width(), physicalSize.height(), toSubPixel(), qPrintable(manufacturer), qPrintable(model), toTransform()); } void OutputInterface::Private::sendScale(const ResourceData &data) { if (data.version < 2) { return; } wl_output_send_scale(data.resource, scale); } void OutputInterface::Private::sendDone(const ResourceData &data) { if (data.version < 2) { return; } wl_output_send_done(data.resource); } void OutputInterface::Private::updateGeometry() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendGeometry((*it).resource); sendDone(*it); } } void OutputInterface::Private::updateScale() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendScale(*it); sendDone(*it); } } #define SETTER(setterName, type, argumentName) \ void OutputInterface::setterName(type arg) \ { \ Q_D(); \ if (d->argumentName == arg) { \ return; \ } \ d->argumentName = arg; \ emit argumentName##Changed(d->argumentName); \ } SETTER(setPhysicalSize, const QSize&, physicalSize) SETTER(setGlobalPosition, const QPoint&, globalPosition) SETTER(setManufacturer, const QString&, manufacturer) SETTER(setModel, const QString&, model) SETTER(setScale, int, scale) SETTER(setSubPixel, SubPixel, subPixel) SETTER(setTransform, Transform, transform) #undef SETTER QSize OutputInterface::physicalSize() const { Q_D(); return d->physicalSize; } QPoint OutputInterface::globalPosition() const { Q_D(); return d->globalPosition; } QString OutputInterface::manufacturer() const { Q_D(); return d->manufacturer; } QString OutputInterface::model() const { Q_D(); return d->model; } int OutputInterface::scale() const { Q_D(); return d->scale; } OutputInterface::SubPixel OutputInterface::subPixel() const { Q_D(); return d->subPixel; } OutputInterface::Transform OutputInterface::transform() const { Q_D(); return d->transform; } QList< OutputInterface::Mode > OutputInterface::modes() const { Q_D(); return d->modes; } void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode) { Q_D(); if (d->dpms.mode == mode) { return; } d->dpms.mode = mode; emit dpmsModeChanged(); } void OutputInterface::setDpmsSupported(bool supported) { Q_D(); if (d->dpms.supported == supported) { return; } d->dpms.supported = supported; emit dpmsSupportedChanged(); } OutputInterface::DpmsMode OutputInterface::dpmsMode() const { Q_D(); return d->dpms.mode; } bool OutputInterface::isDpmsSupported() const { Q_D(); return d->dpms.supported; } QVector OutputInterface::clientResources(ClientConnection *client) const { Q_D(); QVector ret; for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) { if (wl_resource_get_client((*it).resource) == client->client()) { ret << (*it).resource; } } return ret; } OutputInterface *OutputInterface::get(wl_resource* native) { return Private::get(native); } OutputInterface::Private *OutputInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/output_interface.h b/src/server/output_interface.h index 0cf9d4f..d0d73b8 100644 --- a/src/server/output_interface.h +++ b/src/server/output_interface.h @@ -1,174 +1,160 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_OUTPUT_INTERFACE_H #define WAYLAND_SERVER_OUTPUT_INTERFACE_H #include #include #include #include #include "global.h" struct wl_global; struct wl_client; struct wl_resource; namespace KWayland { namespace Server { class ClientConnection; class Display; /** * @brief Global for the wl_output interface. * **/ class KWAYLANDSERVER_EXPORT OutputInterface : public Global { Q_OBJECT Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged) Q_PROPERTY(QPoint globalPosition READ globalPosition WRITE setGlobalPosition NOTIFY globalPositionChanged) Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged) Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged) Q_PROPERTY(int scale READ scale WRITE setScale NOTIFY scaleChanged) public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class ModeFlag { Current = 1, Preferred = 2 }; Q_DECLARE_FLAGS(ModeFlags, ModeFlag) struct Mode { QSize size = QSize(); int refreshRate = 60000; ModeFlags flags; }; enum class DpmsMode { On, Standby, Suspend, Off }; virtual ~OutputInterface(); QSize physicalSize() const; QPoint globalPosition() const; QString manufacturer() const; QString model() const; QSize pixelSize() const; int refreshRate() const; int scale() const; SubPixel subPixel() const; Transform transform() const; QList modes() const; bool isDpmsSupported() const; DpmsMode dpmsMode() const; void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setScale(int scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void addMode(const QSize &size, ModeFlags flags = ModeFlags(), int refreshRate = 60000); void setCurrentMode(const QSize &size, int refreshRate = 60000); /** * Sets whether Dpms is supported for this output. * Default is @c false. * @since 5.5 **/ void setDpmsSupported(bool supported); /** * Sets the currently used dpms mode. * Default is @c DpmsMode::On. * @since 5.5 **/ void setDpmsMode(DpmsMode mode); /** * @returns all wl_resources bound for the @p client * @since 5.27 **/ QVector clientResources(ClientConnection *client) const; static OutputInterface *get(wl_resource *native); Q_SIGNALS: void physicalSizeChanged(const QSize&); void globalPositionChanged(const QPoint&); void manufacturerChanged(const QString&); void modelChanged(const QString&); void pixelSizeChanged(const QSize&); void refreshRateChanged(int); void scaleChanged(int); void subPixelChanged(SubPixel); void transformChanged(Transform); void modesChanged(); void currentModeChanged(); void dpmsModeChanged(); void dpmsSupportedChanged(); /** * Change of dpms @p mode is requested. * A server is free to ignore this request. * @since 5.5 **/ void dpmsModeRequested(KWayland::Server::OutputInterface::DpmsMode mode); private: friend class Display; explicit OutputInterface(Display *display, QObject *parent = nullptr); class Private; Private *d_func() const; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::OutputInterface::ModeFlags) Q_DECLARE_METATYPE(KWayland::Server::OutputInterface::SubPixel) Q_DECLARE_METATYPE(KWayland::Server::OutputInterface::Transform) Q_DECLARE_METATYPE(KWayland::Server::OutputInterface::DpmsMode) #endif diff --git a/src/server/outputchangeset.cpp b/src/server/outputchangeset.cpp index 2e81868..afe0b73 100644 --- a/src/server/outputchangeset.cpp +++ b/src/server/outputchangeset.cpp @@ -1,134 +1,120 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputchangeset.h" #include "outputchangeset_p.h" namespace KWayland { namespace Server { OutputChangeSet::Private::Private(OutputDeviceInterface *outputdevice, OutputChangeSet *parent) : q(parent) , o(outputdevice) , enabled(o->enabled()) , modeId(o->currentModeId()) , transform(o->transform()) , position(o->globalPosition()) , scale(o->scaleF()) , colorCurves(o->colorCurves()) { } OutputChangeSet::Private::~Private() = default; OutputChangeSet::OutputChangeSet(OutputDeviceInterface *outputdevice, QObject *parent) : QObject(parent) , d(new Private(outputdevice, this)) { } OutputChangeSet::~OutputChangeSet() = default; OutputChangeSet::Private *OutputChangeSet::d_func() const { return reinterpret_cast(d.data()); } bool OutputChangeSet::enabledChanged() const { Q_D(); return d->enabled != d->o->enabled(); } OutputDeviceInterface::Enablement OutputChangeSet::enabled() const { Q_D(); return d->enabled; } bool OutputChangeSet::modeChanged() const { Q_D(); return d->modeId != d->o->currentModeId(); } int OutputChangeSet::mode() const { Q_D(); return d->modeId; } bool OutputChangeSet::transformChanged() const { Q_D(); return d->transform != d->o->transform(); } OutputDeviceInterface::Transform OutputChangeSet::transform() const { Q_D(); return d->transform; } bool OutputChangeSet::positionChanged() const { Q_D(); return d->position != d->o->globalPosition(); } QPoint OutputChangeSet::position() const { Q_D(); return d->position; } bool OutputChangeSet::scaleChanged() const { Q_D(); return !qFuzzyCompare(d->scale, d->o->scaleF()); } int OutputChangeSet::scale() const { Q_D(); return qRound(d->scale); } qreal OutputChangeSet::scaleF() const { Q_D(); return d->scale; } bool OutputChangeSet::colorCurvesChanged() const { Q_D(); return d->colorCurves != d->o->colorCurves(); } OutputDeviceInterface::ColorCurves OutputChangeSet::colorCurves() const { Q_D(); return d->colorCurves; } } } diff --git a/src/server/outputchangeset.h b/src/server/outputchangeset.h index 78663d2..53d983e 100644 --- a/src/server/outputchangeset.h +++ b/src/server/outputchangeset.h @@ -1,114 +1,100 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_OUTPUT_CHANGESET_H #define WAYLAND_SERVER_OUTPUT_CHANGESET_H #include #include "outputdevice_interface.h" #include namespace KWayland { namespace Server { /** * @brief Holds a set of changes to an OutputInterface or OutputDeviceInterface. * * This class implements a set of changes that the compositor can apply to an * OutputInterface after OutputConfiguration::apply has been called on the client * side. The changes are per-configuration. * * @see OutputConfiguration * @since 5.5 **/ class KWAYLANDSERVER_EXPORT OutputChangeSet : public QObject { Q_OBJECT public: virtual ~OutputChangeSet(); /** Whether the enabled() property of the outputdevice changed. * @returns @c true if the enabled property of the outputdevice has changed. */ bool enabledChanged() const; /** Whether the currentModeId() property of the outputdevice changed. * @returns @c true if the enabled property of the outputdevice has changed. * bool modeChanged() const; */ /** Whether the transform() property of the outputdevice changed. */ bool transformChanged() const; /** Whether the currentModeId() property of the outputdevice changed. * @returns @c true if the currentModeId() property of the outputdevice has changed. */ bool modeChanged() const; /** Whether the globalPosition() property of the outputdevice changed. * @returns @c true if the globalPosition() property of the outputdevice has changed. */ bool positionChanged() const; /** Whether the scale() property of the outputdevice changed. * @returns @c true if the scale() property of the outputdevice has changed. */ bool scaleChanged() const; /** Whether the colorCurves() property of the outputdevice changed. * @returns @c true if the colorCurves() property of the outputdevice has changed. */ bool colorCurvesChanged() const; /** The new value for enabled. */ OutputDeviceInterface::Enablement enabled() const; /** The new mode id.*/ int mode() const; /** The new value for transform. */ OutputDeviceInterface::Transform transform() const; /** The new value for globalPosition. */ QPoint position() const; #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) /** The new value for scale. @deprecated Since 5.50, use scaleF */ KWAYLANDSERVER_DEPRECATED_VERSION(5, 50, "Use OutputChangeSet::scaleF()") int scale() const; #endif /** The new value for scale. * @since 5.50 */ qreal scaleF() const; /** The new value for colorCurves. * @since 5.XX */ OutputDeviceInterface::ColorCurves colorCurves() const; private: friend class OutputConfigurationInterface; explicit OutputChangeSet(OutputDeviceInterface *outputdevice, QObject *parent = nullptr); class Private; QScopedPointer d; Private *d_func() const; }; } } #endif diff --git a/src/server/outputchangeset_p.h b/src/server/outputchangeset_p.h index c2bdf8c..24cf044 100644 --- a/src/server/outputchangeset_p.h +++ b/src/server/outputchangeset_p.h @@ -1,49 +1,35 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_OUTPUTCHANGESET_P_H #define KWAYLAND_SERVER_OUTPUTCHANGESET_P_H #include "outputchangeset.h" namespace KWayland { namespace Server { class OutputChangeSet::Private { public: Private(OutputDeviceInterface *outputdevice, OutputChangeSet *parent); ~Private(); OutputChangeSet *q; OutputDeviceInterface *o; OutputDeviceInterface::Enablement enabled; int modeId; OutputDeviceInterface::Transform transform; QPoint position; qreal scale; OutputDeviceInterface::ColorCurves colorCurves; }; } } #endif diff --git a/src/server/outputconfiguration_interface.cpp b/src/server/outputconfiguration_interface.cpp index 25b6b46..32b1665 100644 --- a/src/server/outputconfiguration_interface.cpp +++ b/src/server/outputconfiguration_interface.cpp @@ -1,340 +1,326 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputconfiguration_interface.h" #include "outputdevice_interface.h" #include "logging.h" #include "resource_p.h" #include "display.h" #include "outputchangeset_p.h" #include #include "wayland-output-management-server-protocol.h" #include "wayland-org_kde_kwin_outputdevice-server-protocol.h" #include #include namespace KWayland { namespace Server { class OutputConfigurationInterface::Private : public Resource::Private { public: Private(OutputConfigurationInterface *q, OutputManagementInterface *c, wl_resource *parentResource); ~Private(); void sendApplied(); void sendFailed(); void emitConfigurationChangeRequested() const; void clearPendingChanges(); bool hasPendingChanges(OutputDeviceInterface *outputdevice) const; OutputChangeSet* pendingChanges(OutputDeviceInterface *outputdevice); OutputManagementInterface *outputManagement; QHash changes; static const quint32 s_version = 2; private: static void enableCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t enable); static void modeCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t mode_id); static void transformCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t transform); static void positionCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t x, int32_t y); static void scaleCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t scale); static void applyCallback(wl_client *client, wl_resource *resource); static void scaleFCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, wl_fixed_t scale); static void colorcurvesCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, wl_array *red, wl_array *green, wl_array *blue); OutputConfigurationInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_kwin_outputconfiguration_interface s_interface; }; const struct org_kde_kwin_outputconfiguration_interface OutputConfigurationInterface::Private::s_interface = { enableCallback, modeCallback, transformCallback, positionCallback, scaleCallback, applyCallback, scaleFCallback, colorcurvesCallback, resourceDestroyedCallback }; OutputConfigurationInterface::OutputConfigurationInterface(OutputManagementInterface* parent, wl_resource* parentResource): Resource(new Private(this, parent, parentResource)) { Q_D(); d->outputManagement = parent; } OutputConfigurationInterface::~OutputConfigurationInterface() { Q_D(); d->clearPendingChanges(); } void OutputConfigurationInterface::Private::enableCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t enable) { Q_UNUSED(client); auto s = cast(resource); Q_ASSERT(s); auto _enable = (enable == ORG_KDE_KWIN_OUTPUTDEVICE_ENABLEMENT_ENABLED) ? OutputDeviceInterface::Enablement::Enabled : OutputDeviceInterface::Enablement::Disabled; OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); s->pendingChanges(o)->d_func()->enabled = _enable; } void OutputConfigurationInterface::Private::modeCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t mode_id) { Q_UNUSED(client); OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); bool modeValid = false; for (const auto &m: o->modes()) { if (m.id == mode_id) { modeValid = true; break; } } if (!modeValid) { qCWarning(KWAYLAND_SERVER) << "Set invalid mode id:" << mode_id; return; } auto s = cast(resource); Q_ASSERT(s); s->pendingChanges(o)->d_func()->modeId = mode_id; } void OutputConfigurationInterface::Private::transformCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t transform) { Q_UNUSED(client); auto toTransform = [transform]() { switch (transform) { case WL_OUTPUT_TRANSFORM_90: return OutputDeviceInterface::Transform::Rotated90; case WL_OUTPUT_TRANSFORM_180: return OutputDeviceInterface::Transform::Rotated180; case WL_OUTPUT_TRANSFORM_270: return OutputDeviceInterface::Transform::Rotated270; case WL_OUTPUT_TRANSFORM_FLIPPED: return OutputDeviceInterface::Transform::Flipped; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return OutputDeviceInterface::Transform::Flipped90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return OutputDeviceInterface::Transform::Flipped180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return OutputDeviceInterface::Transform::Flipped270; case WL_OUTPUT_TRANSFORM_NORMAL: default: return OutputDeviceInterface::Transform::Normal; } }; auto _transform = toTransform(); OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); auto s = cast(resource); Q_ASSERT(s); s->pendingChanges(o)->d_func()->transform = _transform; } void OutputConfigurationInterface::Private::positionCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t x, int32_t y) { Q_UNUSED(client); auto _pos = QPoint(x, y); OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); auto s = cast(resource); Q_ASSERT(s); s->pendingChanges(o)->d_func()->position = _pos; } void OutputConfigurationInterface::Private::scaleCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, int32_t scale) { Q_UNUSED(client); if (scale <= 0) { qCWarning(KWAYLAND_SERVER) << "Requested to scale output device to" << scale << ", but I can't do that."; return; } OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); auto s = cast(resource); Q_ASSERT(s); s->pendingChanges(o)->d_func()->scale = scale; } void OutputConfigurationInterface::Private::scaleFCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, wl_fixed_t scale_fixed) { Q_UNUSED(client); const qreal scale = wl_fixed_to_double(scale_fixed); if (scale <= 0) { qCWarning(KWAYLAND_SERVER) << "Requested to scale output device to" << scale << ", but I can't do that."; return; } OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); auto s = cast(resource); Q_ASSERT(s); s->pendingChanges(o)->d_func()->scale = scale; } void OutputConfigurationInterface::Private::applyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); auto s = cast(resource); Q_ASSERT(s); s->emitConfigurationChangeRequested(); } void OutputConfigurationInterface::Private::colorcurvesCallback(wl_client *client, wl_resource *resource, wl_resource * outputdevice, wl_array *red, wl_array *green, wl_array *blue) { Q_UNUSED(client); OutputDeviceInterface *o = OutputDeviceInterface::get(outputdevice); OutputDeviceInterface::ColorCurves oldCc = o->colorCurves(); auto checkArg = [](const wl_array *newColor, const QVector &oldColor) { return (newColor->size % sizeof(uint16_t) == 0) && (newColor->size / sizeof(uint16_t) == static_cast(oldColor.size())); }; if (!checkArg(red, oldCc.red) || !checkArg(green, oldCc.green) || !checkArg(blue, oldCc.blue)) { qCWarning(KWAYLAND_SERVER) << "Requested to change color curves, but have wrong size."; return; } auto s = cast(resource); Q_ASSERT(s); OutputDeviceInterface::ColorCurves cc; auto fillVector = [](const wl_array *array, QVector *v) { const uint16_t *pos = (uint16_t*)array->data; while((char*)pos < (char*)array->data + array->size) { v->append(*pos); pos++; } }; fillVector(red, &cc.red); fillVector(green, &cc.green); fillVector(blue, &cc.blue); s->pendingChanges(o)->d_func()->colorCurves = cc; } void OutputConfigurationInterface::Private::emitConfigurationChangeRequested() const { auto configinterface = reinterpret_cast(q); emit outputManagement->configurationChangeRequested(configinterface); } OutputConfigurationInterface::Private::Private(OutputConfigurationInterface *q, OutputManagementInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_outputconfiguration_interface, &s_interface) { } OutputConfigurationInterface::Private::~Private() = default; OutputConfigurationInterface::Private *OutputConfigurationInterface::d_func() const { return reinterpret_cast(d.data()); } QHash OutputConfigurationInterface::changes() const { Q_D(); return d->changes; } void OutputConfigurationInterface::setApplied() { Q_D(); d->clearPendingChanges(); d->sendApplied(); } void OutputConfigurationInterface::Private::sendApplied() { if (!resource) { return; } org_kde_kwin_outputconfiguration_send_applied(resource); } void OutputConfigurationInterface::setFailed() { Q_D(); d->clearPendingChanges(); d->sendFailed(); } void OutputConfigurationInterface::Private::sendFailed() { if (!resource) { return; } org_kde_kwin_outputconfiguration_send_failed(resource); } OutputChangeSet* OutputConfigurationInterface::Private::pendingChanges(OutputDeviceInterface *outputdevice) { if (!changes.keys().contains(outputdevice)) { changes[outputdevice] = new OutputChangeSet(outputdevice, q); } return changes[outputdevice]; } bool OutputConfigurationInterface::Private::hasPendingChanges(OutputDeviceInterface *outputdevice) const { if (!changes.keys().contains(outputdevice)) { return false; } auto c = changes[outputdevice]; return c->enabledChanged() || c->modeChanged() || c->transformChanged() || c->positionChanged() || c->scaleChanged(); } void OutputConfigurationInterface::Private::clearPendingChanges() { qDeleteAll(changes.begin(), changes.end()); changes.clear(); } } } diff --git a/src/server/outputconfiguration_interface.h b/src/server/outputconfiguration_interface.h index a7e2db2..5be6415 100644 --- a/src/server/outputconfiguration_interface.h +++ b/src/server/outputconfiguration_interface.h @@ -1,110 +1,96 @@ -/**************************************************************************** -* 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) 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 6 of version 3 of the license. -* -* 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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_OUTPUTCONFIGURATION_INTERFACE_H #define KWAYLAND_SERVER_OUTPUTCONFIGURATION_INTERFACE_H #include "resource.h" #include "outputmanagement_interface.h" #include "outputdevice_interface.h" #include "outputchangeset.h" #include namespace KWayland { namespace Server { /** @class OutputConfigurationInterface * * Holds a new configuration for the outputs. * * The overall mechanism is to get a new OutputConfiguration from the OutputManagement global and * apply changes through the OutputConfiguration::set* calls. When all changes are set, the client * calls apply, which asks the server to look at the changes and apply them. The server will then * signal back whether the changes have been applied successfully (@c setApplied()) or were rejected * or failed to apply (@c setFailed()). * * Once the client has called applied, the OutputManagementInterface send the configuration object * to the compositor through the OutputManagement::configurationChangeRequested(OutputConfiguration*) * signal, the compositor can then decide what to do with the changes. * * These KWayland classes will not apply changes to the OutputDevices, this is the compositor's * task. As such, the configuration set through this interface can be seen as a hint what the * compositor should set up, but whether or not the compositor does it (based on hardware or * rendering policies, for example), is up to the compositor. The mode setting is passed on to * the DRM subsystem through the compositor. The compositor also saves this configuration and reads * it on startup, this interface is not involved in that process. * * @see OutputManagementInterface * @see OutputConfiguration * @since 5.5 */ class KWAYLANDSERVER_EXPORT OutputConfigurationInterface : public Resource { Q_OBJECT public: virtual ~OutputConfigurationInterface(); /** * Accessor for the changes made to OutputDevices. The data returned from this call * will be deleted by the OutputConfigurationInterface when * OutputManagementInterface::setApplied() or OutputManagementInterface::setFailed() * is called, and on destruction of the OutputConfigurationInterface, so make sure you * do not keep these pointers around. * @returns A QHash of ChangeSets per outputdevice. * @see ChangeSet * @see OutputDeviceInterface * @see OutputManagement */ QHash changes() const; public Q_SLOTS: /** * Called by the compositor once the changes have successfully been applied. * The compositor is responsible for updating the OutputDevices. After having * done so, calling this function sends applied() through the client. * @see setFailed * @see OutputConfiguration::applied */ void setApplied(); /** * Called by the compositor when the changes as a whole are rejected or * failed to apply. This function results in the client OutputConfiguration emitting * failed(). * @see setApplied * @see OutputConfiguration::failed */ void setFailed(); private: explicit OutputConfigurationInterface(OutputManagementInterface *parent, wl_resource *parentResource); friend class OutputManagementInterface; class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::OutputConfigurationInterface*) #endif diff --git a/src/server/outputdevice_interface.cpp b/src/server/outputdevice_interface.cpp index 4f521a7..9556d44 100644 --- a/src/server/outputdevice_interface.cpp +++ b/src/server/outputdevice_interface.cpp @@ -1,742 +1,728 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputdevice_interface.h" #include "global_p.h" #include "display.h" #include "logging.h" #include #include "wayland-org_kde_kwin_outputdevice-server-protocol.h" #include namespace KWayland { namespace Server { class OutputDeviceInterface::Private : public Global::Private { public: struct ResourceData { wl_resource *resource; uint32_t version; }; Private(OutputDeviceInterface *q, Display *d); ~Private(); void updateGeometry(); void updateUuid(); void updateEdid(); void updateEnabled(); void updateScale(); void updateColorCurves(); void updateEisaId(); void updateSerialNumber(); void sendGeometry(wl_resource *resource); void sendMode(wl_resource *resource, const Mode &mode); void sendDone(const ResourceData &data); void sendUuid(const ResourceData &data); void sendEdid(const ResourceData &data); void sendEnabled(const ResourceData &data); void sendScale(const ResourceData &data); void sendColorCurves(const ResourceData &data); void sendEisaId(const ResourceData &data); void sendSerialNumber(const ResourceData &data); QSize physicalSize; QPoint globalPosition; QString manufacturer = QStringLiteral("org.kde.kwin"); QString model = QStringLiteral("none"); qreal scale = 1.0; QString serialNumber; QString eisaId; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; ColorCurves colorCurves; QList modes; Mode currentMode; QList resources; QByteArray edid; Enablement enabled = Enablement::Enabled; QByteArray uuid; static OutputDeviceInterface *get(wl_resource *native); private: static Private *cast(wl_resource *native); static void unbind(wl_resource *resource); void bind(wl_client *client, uint32_t version, uint32_t id) override; int32_t toTransform() const; int32_t toSubPixel() const; static const quint32 s_version; OutputDeviceInterface *q; static QVector s_privates; }; const quint32 OutputDeviceInterface::Private::s_version = 2; QVector OutputDeviceInterface::Private::s_privates; OutputDeviceInterface::Private::Private(OutputDeviceInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_outputdevice_interface, s_version) , q(q) { s_privates << this; } OutputDeviceInterface::Private::~Private() { s_privates.removeAll(this); } OutputDeviceInterface *OutputDeviceInterface::Private::get(wl_resource *native) { if (Private *p = cast(native)) { return p->q; } return nullptr; } OutputDeviceInterface::Private *OutputDeviceInterface::Private::cast(wl_resource *native) { for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) { const auto &resources = (*it)->resources; auto rit = std::find_if(resources.begin(), resources.end(), [native] (const ResourceData &data) { return data.resource == native; }); if (rit != resources.end()) { return (*it); } } return nullptr; } OutputDeviceInterface::OutputDeviceInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &OutputDeviceInterface::currentModeChanged, this, [d] { Q_ASSERT(d->currentMode.id >= 0); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendMode((*it).resource, d->currentMode); d->sendDone(*it); } wl_display_flush_clients(*(d->display)); } ); connect(this, &OutputDeviceInterface::subPixelChanged, this, [d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::transformChanged, this, [d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::globalPositionChanged, this, [d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::modelChanged, this, [d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::manufacturerChanged, this, [d] { d->updateGeometry(); }); connect(this, &OutputDeviceInterface::scaleFChanged, this, [d] { d->updateScale(); }); connect(this, &OutputDeviceInterface::scaleChanged, this, [d] { d->updateScale(); }); connect(this, &OutputDeviceInterface::colorCurvesChanged, this, [d] { d->updateColorCurves(); }); } OutputDeviceInterface::~OutputDeviceInterface() = default; QSize OutputDeviceInterface::pixelSize() const { Q_D(); if (d->currentMode.id == -1) { return QSize(); } return d->currentMode.size; } OutputDeviceInterface *OutputDeviceInterface::get(wl_resource* native) { return Private::get(native); } int OutputDeviceInterface::refreshRate() const { Q_D(); if (d->currentMode.id == -1) { return 60000; } return d->currentMode.refreshRate; } void OutputDeviceInterface::addMode(Mode &mode) { Q_ASSERT(!isValid()); Q_ASSERT(mode.id >= 0); Q_ASSERT(mode.size.isValid()); Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt == d->modes.end() && !mode.flags.testFlag(ModeFlag::Current)) { // no mode with current flag - enforce mode.flags |= ModeFlag::Current; } if (currentModeIt != d->modes.end() && mode.flags.testFlag(ModeFlag::Current)) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } if (mode.flags.testFlag(ModeFlag::Preferred)) { // remove from existing Preferred mode auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Preferred); } ); if (preferredIt != d->modes.end()) { (*preferredIt).flags &= ~uint(ModeFlag::Preferred); } } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [mode](const Mode &mode_it) { return mode.size == mode_it.size && mode.refreshRate == mode_it.refreshRate && mode.id == mode_it.id; } ); auto emitChanges = [this, d, mode] { emit modesChanged(); if (mode.flags.testFlag(ModeFlag::Current)) { d->currentMode = mode; emit refreshRateChanged(mode.refreshRate); emit pixelSizeChanged(mode.size); emit currentModeChanged(); } }; if (existingModeIt != d->modes.end()) { if ((*existingModeIt).flags == mode.flags) { // nothing to do return; } (*existingModeIt).flags = mode.flags; emitChanges(); return; } else { auto idIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [mode](const Mode &mode_it) { return mode.id == mode_it.id; } ); if (idIt != d->modes.constEnd()) { qCWarning(KWAYLAND_SERVER) << "Duplicate Mode id" << mode.id << ": not adding mode" << mode.size << mode.refreshRate; return; } } d->modes << mode; emitChanges(); } void OutputDeviceInterface::setCurrentMode(const int modeId) { Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt != d->modes.end()) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [modeId](const Mode &mode) { return mode.id == modeId; } ); Q_ASSERT(existingModeIt != d->modes.end()); (*existingModeIt).flags |= ModeFlag::Current; d->currentMode = *existingModeIt; emit modesChanged(); emit refreshRateChanged((*existingModeIt).refreshRate); emit pixelSizeChanged((*existingModeIt).size); emit currentModeChanged(); } int32_t OutputDeviceInterface::Private::toTransform() const { switch (transform) { case Transform::Normal: return WL_OUTPUT_TRANSFORM_NORMAL; case Transform::Rotated90: return WL_OUTPUT_TRANSFORM_90; case Transform::Rotated180: return WL_OUTPUT_TRANSFORM_180; case Transform::Rotated270: return WL_OUTPUT_TRANSFORM_270; case Transform::Flipped: return WL_OUTPUT_TRANSFORM_FLIPPED; case Transform::Flipped90: return WL_OUTPUT_TRANSFORM_FLIPPED_90; case Transform::Flipped180: return WL_OUTPUT_TRANSFORM_FLIPPED_180; case Transform::Flipped270: return WL_OUTPUT_TRANSFORM_FLIPPED_270; } abort(); } int32_t OutputDeviceInterface::Private::toSubPixel() const { switch (subPixel) { case SubPixel::Unknown: return WL_OUTPUT_SUBPIXEL_UNKNOWN; case SubPixel::None: return WL_OUTPUT_SUBPIXEL_NONE; case SubPixel::HorizontalRGB: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; case SubPixel::HorizontalBGR: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; case SubPixel::VerticalRGB: return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; case SubPixel::VerticalBGR: return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; } abort(); } void OutputDeviceInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_outputdevice_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_user_data(resource, this); wl_resource_set_destructor(resource, unbind); ResourceData r; r.resource = resource; r.version = version; resources << r; sendGeometry(resource); sendScale(r); sendColorCurves(r); sendEisaId(r); sendSerialNumber(r); auto currentModeIt = modes.constEnd(); for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) { const Mode &mode = *it; if (mode.flags.testFlag(ModeFlag::Current)) { // needs to be sent as last mode currentModeIt = it; continue; } sendMode(resource, mode); } if (currentModeIt != modes.constEnd()) { sendMode(resource, *currentModeIt); } sendUuid(r); sendEdid(r); sendEnabled(r); sendDone(r); c->flush(); } void OutputDeviceInterface::Private::unbind(wl_resource *resource) { Private *o = cast(resource); if (!o) { return; } auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; }); if (it != o->resources.end()) { o->resources.erase(it); } } void OutputDeviceInterface::Private::sendMode(wl_resource *resource, const Mode &mode) { int32_t flags = 0; if (mode.flags.testFlag(ModeFlag::Current)) { flags |= WL_OUTPUT_MODE_CURRENT; } if (mode.flags.testFlag(ModeFlag::Preferred)) { flags |= WL_OUTPUT_MODE_PREFERRED; } org_kde_kwin_outputdevice_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate, mode.id); } void OutputDeviceInterface::Private::sendGeometry(wl_resource *resource) { org_kde_kwin_outputdevice_send_geometry(resource, globalPosition.x(), globalPosition.y(), physicalSize.width(), physicalSize.height(), toSubPixel(), qPrintable(manufacturer), qPrintable(model), toTransform()); } void OutputDeviceInterface::Private::sendScale(const ResourceData &data) { if (wl_resource_get_version(data.resource) < ORG_KDE_KWIN_OUTPUTDEVICE_SCALEF_SINCE_VERSION) { org_kde_kwin_outputdevice_send_scale(data.resource, qRound(scale)); } else { org_kde_kwin_outputdevice_send_scalef(data.resource, wl_fixed_from_double(scale)); } } void OutputDeviceInterface::Private::sendColorCurves(const ResourceData &data) { if (data.version < ORG_KDE_KWIN_OUTPUTDEVICE_COLORCURVES_SINCE_VERSION) { return; } wl_array wlRed, wlGreen, wlBlue; auto fillArray = [](const QVector &origin, wl_array *dest) { wl_array_init(dest); const size_t memLength = sizeof(uint16_t) * origin.size(); void *s = wl_array_add(dest, memLength); memcpy(s, origin.data(), memLength); }; fillArray(colorCurves.red, &wlRed); fillArray(colorCurves.green, &wlGreen); fillArray(colorCurves.blue, &wlBlue); org_kde_kwin_outputdevice_send_colorcurves(data.resource, &wlRed, &wlGreen, &wlBlue); wl_array_release(&wlRed); wl_array_release(&wlGreen); wl_array_release(&wlBlue); } void KWayland::Server::OutputDeviceInterface::Private::sendSerialNumber(const ResourceData &data) { if (wl_resource_get_version(data.resource) >= ORG_KDE_KWIN_OUTPUTDEVICE_SERIAL_NUMBER_SINCE_VERSION) { org_kde_kwin_outputdevice_send_serial_number(data.resource, qPrintable(serialNumber)); } } void KWayland::Server::OutputDeviceInterface::Private::sendEisaId(const ResourceData &data) { if (wl_resource_get_version(data.resource) >= ORG_KDE_KWIN_OUTPUTDEVICE_EISA_ID_SINCE_VERSION) { org_kde_kwin_outputdevice_send_eisa_id(data.resource, qPrintable(eisaId)); } } void OutputDeviceInterface::Private::sendDone(const ResourceData &data) { org_kde_kwin_outputdevice_send_done(data.resource); } void OutputDeviceInterface::Private::updateGeometry() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendGeometry((*it).resource); sendDone(*it); } } void OutputDeviceInterface::Private::updateScale() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendScale(*it); sendDone(*it); } } void OutputDeviceInterface::Private::updateColorCurves() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendColorCurves(*it); sendDone(*it); } } bool OutputDeviceInterface::ColorCurves::operator==(const ColorCurves &cc) const { return red == cc.red && green == cc.green && blue == cc.blue; } bool OutputDeviceInterface::ColorCurves::operator!=(const ColorCurves &cc) const { return !operator==(cc); } #define SETTER(setterName, type, argumentName) \ void OutputDeviceInterface::setterName(type arg) \ { \ Q_D(); \ if (d->argumentName == arg) { \ return; \ } \ d->argumentName = arg; \ emit argumentName##Changed(d->argumentName); \ } SETTER(setPhysicalSize, const QSize&, physicalSize) SETTER(setGlobalPosition, const QPoint&, globalPosition) SETTER(setManufacturer, const QString&, manufacturer) SETTER(setModel, const QString&, model) SETTER(setSerialNumber, const QString&, serialNumber) SETTER(setEisaId, const QString&, eisaId) SETTER(setSubPixel, SubPixel, subPixel) SETTER(setTransform, Transform, transform) #undef SETTER void OutputDeviceInterface::setScale(int scale) { Q_D(); if (d->scale == scale) { return; } d->scale = scale; emit scaleChanged(d->scale); emit scaleFChanged(d->scale); } void OutputDeviceInterface::setScaleF(qreal scale) { Q_D(); if (qFuzzyCompare(d->scale, scale)) { return; } d->scale = scale; emit scaleChanged(qRound(d->scale)); emit scaleFChanged(d->scale); } QSize OutputDeviceInterface::physicalSize() const { Q_D(); return d->physicalSize; } QPoint OutputDeviceInterface::globalPosition() const { Q_D(); return d->globalPosition; } QString OutputDeviceInterface::manufacturer() const { Q_D(); return d->manufacturer; } QString OutputDeviceInterface::model() const { Q_D(); return d->model; } QString OutputDeviceInterface::serialNumber() const { Q_D(); return d->serialNumber; } QString OutputDeviceInterface::eisaId() const { Q_D(); return d->eisaId; } int OutputDeviceInterface::scale() const { Q_D(); return qRound(d->scale); } qreal OutputDeviceInterface::scaleF() const { Q_D(); return d->scale; } OutputDeviceInterface::SubPixel OutputDeviceInterface::subPixel() const { Q_D(); return d->subPixel; } OutputDeviceInterface::Transform OutputDeviceInterface::transform() const { Q_D(); return d->transform; } OutputDeviceInterface::ColorCurves OutputDeviceInterface::colorCurves() const { Q_D(); return d->colorCurves; } QList< OutputDeviceInterface::Mode > OutputDeviceInterface::modes() const { Q_D(); return d->modes; } int OutputDeviceInterface::currentModeId() const { Q_D(); for (const Mode &m: d->modes) { if (m.flags.testFlag(OutputDeviceInterface::ModeFlag::Current)) { return m.id; } } return -1; } OutputDeviceInterface::Private *OutputDeviceInterface::d_func() const { return reinterpret_cast(d.data()); } void OutputDeviceInterface::setColorCurves(const ColorCurves &colorCurves) { Q_D(); if (d->colorCurves == colorCurves) { return; } d->colorCurves = colorCurves; emit colorCurvesChanged(d->colorCurves); } void OutputDeviceInterface::setEdid(const QByteArray &edid) { Q_D(); d->edid = edid; d->updateEdid(); emit edidChanged(); } QByteArray OutputDeviceInterface::edid() const { Q_D(); return d->edid; } void OutputDeviceInterface::setEnabled(OutputDeviceInterface::Enablement enabled) { Q_D(); if (d->enabled != enabled) { d->enabled = enabled; d->updateEnabled(); emit enabledChanged(); } } OutputDeviceInterface::Enablement OutputDeviceInterface::enabled() const { Q_D(); return d->enabled; } void OutputDeviceInterface::setUuid(const QByteArray &uuid) { Q_D(); if (d->uuid != uuid) { d->uuid = uuid; d->updateUuid(); emit uuidChanged(); } } QByteArray OutputDeviceInterface::uuid() const { Q_D(); return d->uuid; } void KWayland::Server::OutputDeviceInterface::Private::sendEdid(const ResourceData &data) { org_kde_kwin_outputdevice_send_edid(data.resource, edid.toBase64().constData()); } void KWayland::Server::OutputDeviceInterface::Private::sendEnabled(const ResourceData &data) { int _enabled = 0; if (enabled == OutputDeviceInterface::Enablement::Enabled) { _enabled = 1; } org_kde_kwin_outputdevice_send_enabled(data.resource, _enabled); } void OutputDeviceInterface::Private::sendUuid(const ResourceData &data) { org_kde_kwin_outputdevice_send_uuid(data.resource, uuid.constData()); } void KWayland::Server::OutputDeviceInterface::Private::updateEnabled() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendEnabled(*it); } } void KWayland::Server::OutputDeviceInterface::Private::updateEdid() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendEdid(*it); } } void KWayland::Server::OutputDeviceInterface::Private::updateUuid() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendUuid(*it); } } void KWayland::Server::OutputDeviceInterface::Private::updateEisaId() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendEisaId(*it); } } } } diff --git a/src/server/outputdevice_interface.h b/src/server/outputdevice_interface.h index e702fb4..1d5e54c 100644 --- a/src/server/outputdevice_interface.h +++ b/src/server/outputdevice_interface.h @@ -1,204 +1,190 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_OUTPUTDEVICE_INTERFACE_H #define WAYLAND_SERVER_OUTPUTDEVICE_INTERFACE_H #include #include #include #include #include #include "global.h" struct wl_resource; namespace KWayland { namespace Server { class Display; /** @class OutputDeviceInterface * * Represents an output device, the difference to Output is that this output can be disabled, * so not currently used to display content. * * @see OutputManagementInterface * @since 5.5 */ class KWAYLANDSERVER_EXPORT OutputDeviceInterface : public Global { Q_OBJECT Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged) Q_PROPERTY(QPoint globalPosition READ globalPosition WRITE setGlobalPosition NOTIFY globalPositionChanged) Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(QString serialNumber READ serialNumber WRITE setSerialNumber NOTIFY serialNumberChanged) Q_PROPERTY(QString eisaId READ eisaId WRITE setEisaId NOTIFY eisaIdChanged) Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged) Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged) Q_PROPERTY(qreal scale READ scaleF WRITE setScaleF NOTIFY scaleFChanged) Q_PROPERTY(QByteArray edid READ edid WRITE setEdid NOTIFY edidChanged) Q_PROPERTY(OutputDeviceInterface::Enablement enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(QByteArray uuid READ uuid WRITE setUuid NOTIFY uuidChanged) public: enum class SubPixel { Unknown, None, HorizontalRGB, HorizontalBGR, VerticalRGB, VerticalBGR }; enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; enum class Enablement { Disabled = 0, Enabled = 1 }; enum class ModeFlag { Current = 1, Preferred = 2 }; Q_DECLARE_FLAGS(ModeFlags, ModeFlag) struct Mode { QSize size = QSize(); int refreshRate = 60000; ModeFlags flags; int id = -1; }; struct ColorCurves { QVector red, green, blue; bool operator==(const ColorCurves &cc) const; bool operator!=(const ColorCurves &cc) const; }; virtual ~OutputDeviceInterface(); QSize physicalSize() const; QPoint globalPosition() const; QString manufacturer() const; QString model() const; QString serialNumber() const; QString eisaId() const; QSize pixelSize() const; int refreshRate() const; #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) /// @deprecated Since 5.50, use scaleF() KWAYLANDSERVER_DEPRECATED_VERSION(5, 50, "Use OutputDeviceInterface::scaleF()") int scale() const; #endif /// @since 5.50 qreal scaleF() const; SubPixel subPixel() const; Transform transform() const; ColorCurves colorCurves() const; QList modes() const; int currentModeId() const; QByteArray edid() const; OutputDeviceInterface::Enablement enabled() const; QByteArray uuid() const; void setPhysicalSize(const QSize &size); void setGlobalPosition(const QPoint &pos); void setManufacturer(const QString &manufacturer); void setModel(const QString &model); void setSerialNumber(const QString &serialNumber); void setEisaId(const QString &eisaId); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) /// @deprecated Since 5.50, use setScale(qreal) KWAYLANDSERVER_DEPRECATED_VERSION(5, 50, "Use OutputDeviceInterface::setScale(qreal)") void setScale(int scale); #endif /// @since 5.50 void setScaleF(qreal scale); void setSubPixel(SubPixel subPixel); void setTransform(Transform transform); void setColorCurves(const ColorCurves &colorCurves); /** * Add an additional mode to this output device. This is only allowed before create() is called * on the object. * * @param mode must have a valid size and non-negative id. */ void addMode(Mode &mode); void setCurrentMode(const int modeId); void setEdid(const QByteArray &edid); void setEnabled(OutputDeviceInterface::Enablement enabled); void setUuid(const QByteArray &uuid); static OutputDeviceInterface *get(wl_resource *native); static QListlist(); Q_SIGNALS: void physicalSizeChanged(const QSize&); void globalPositionChanged(const QPoint&); void manufacturerChanged(const QString&); void modelChanged(const QString&); void serialNumberChanged(const QString&); void eisaIdChanged(const QString &); void pixelSizeChanged(const QSize&); void refreshRateChanged(int); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) /// @deprecated Since 5.50, use scaleFChanged(qreal) KWAYLANDSERVER_DEPRECATED_VERSION(5, 50, "Use OutputDeviceInterface::scaleFChanged(qreal)") void scaleChanged(int); #endif /// @since 5.50 void scaleFChanged(qreal); void subPixelChanged(SubPixel); void transformChanged(Transform); void colorCurvesChanged(ColorCurves); void modesChanged(); void currentModeChanged(); void edidChanged(); void enabledChanged(); void uuidChanged(); private: friend class Display; explicit OutputDeviceInterface(Display *display, QObject *parent = nullptr); class Private; Private *d_func() const; }; } } Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::OutputDeviceInterface::ModeFlags) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Enablement) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::SubPixel) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::Transform) Q_DECLARE_METATYPE(KWayland::Server::OutputDeviceInterface::ColorCurves) #endif diff --git a/src/server/outputmanagement_interface.cpp b/src/server/outputmanagement_interface.cpp index 7099716..311c76b 100644 --- a/src/server/outputmanagement_interface.cpp +++ b/src/server/outputmanagement_interface.cpp @@ -1,120 +1,106 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "outputmanagement_interface.h" #include "outputconfiguration_interface.h" #include "display.h" #include "global_p.h" #include #include "wayland-output-management-server-protocol.h" #include namespace KWayland { namespace Server { class OutputManagementInterface::Private : public Global::Private { public: Private(OutputManagementInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } void createConfiguration(wl_client *client, wl_resource *resource, uint32_t id); static void createConfigurationCallback(wl_client *client, wl_resource *resource, uint32_t id); OutputManagementInterface *q; static const struct org_kde_kwin_outputmanagement_interface s_interface; static const quint32 s_version; QHash configurationInterfaces; }; const quint32 OutputManagementInterface::Private::s_version = 2; const struct org_kde_kwin_outputmanagement_interface OutputManagementInterface::Private::s_interface = { createConfigurationCallback }; OutputManagementInterface::OutputManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } OutputManagementInterface::~OutputManagementInterface() { } void OutputManagementInterface::Private::createConfigurationCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->createConfiguration(client, resource, id); } void OutputManagementInterface::Private::createConfiguration(wl_client* client, wl_resource* resource, uint32_t id) { auto config = new OutputConfigurationInterface(q, resource); config->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!config->resource()) { wl_resource_post_no_memory(resource); delete config; return; } configurationInterfaces[resource] = config; connect(config, &QObject::destroyed, [this, resource] { configurationInterfaces.remove(resource); }); } OutputManagementInterface::Private::Private(OutputManagementInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_outputmanagement_interface, s_version) , q(q) { } void OutputManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_outputmanagement_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void OutputManagementInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } } } diff --git a/src/server/outputmanagement_interface.h b/src/server/outputmanagement_interface.h index 9498668..ce6da4a 100644 --- a/src/server/outputmanagement_interface.h +++ b/src/server/outputmanagement_interface.h @@ -1,78 +1,64 @@ -/**************************************************************************** - * 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) 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 6 of version 3 of the license. - * - * 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, see . - ****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Sebastian Kügler + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_OUTPUTMANAGEMENT_INTERFACE_H #define KWAYLAND_SERVER_OUTPUTMANAGEMENT_INTERFACE_H #include "global.h" #include namespace KWayland { namespace Server { class OutputConfigurationInterface; /** * @class OutputManagementInterface * * This class is used to change the configuration of the Wayland server's outputs. * The client requests an OutputConfiguration, changes its OutputDevices and then * calls OutputConfiguration::apply, which makes this class emit a signal, carrying * the new configuration. * The server is then expected to make the requested changes by applying the settings * of the OutputDevices to the Outputs. * * @see OutputConfiguration * @see OutputConfigurationInterface * @since 5.5 */ class KWAYLANDSERVER_EXPORT OutputManagementInterface : public Global { Q_OBJECT public: virtual ~OutputManagementInterface(); Q_SIGNALS: /** * Emitted after the client has requested an OutputConfiguration to be applied. * through OutputConfiguration::apply. The compositor can use this object to get * notified when the new configuration is set up, and it should be applied to the * Wayland server's OutputInterfaces. * * @param config The OutputConfigurationInterface corresponding to the client that * called apply(). * @see OutputConfiguration::apply * @see OutputConfigurationInterface * @see OutputDeviceInterface * @see OutputInterface */ void configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *configurationInterface); private: explicit OutputManagementInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } #endif diff --git a/src/server/plasmashell_interface.cpp b/src/server/plasmashell_interface.cpp index c606a8c..9b5046f 100644 --- a/src/server/plasmashell_interface.cpp +++ b/src/server/plasmashell_interface.cpp @@ -1,418 +1,404 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmashell_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include #include #include namespace KWayland { namespace Server { class PlasmaShellInterface::Private : public Global::Private { public: Private(PlasmaShellInterface *q, Display *d); QList surfaces; private: static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); PlasmaShellInterface *q; static const struct org_kde_plasma_shell_interface s_interface; static const quint32 s_version; }; const quint32 PlasmaShellInterface::Private::s_version = 6; PlasmaShellInterface::Private::Private(PlasmaShellInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_shell_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct org_kde_plasma_shell_interface PlasmaShellInterface::Private::s_interface = { createSurfaceCallback }; #endif class PlasmaShellSurfaceInterface::Private : public Resource::Private { public: Private(PlasmaShellSurfaceInterface *q, PlasmaShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource); SurfaceInterface *surface; QPoint m_globalPos; Role m_role = Role::Normal; bool m_positionSet = false; PanelBehavior m_panelBehavior = PanelBehavior::AlwaysVisible; bool m_skipTaskbar = false; bool m_skipSwitcher = false; bool panelTakesFocus = false; private: // interface callbacks static void setOutputCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static void setRoleCallback(wl_client *client, wl_resource *resource, uint32_t role); static void setPanelBehaviorCallback(wl_client *client, wl_resource *resource, uint32_t flag); static void setSkipTaskbarCallback(wl_client *client, wl_resource *resource, uint32_t skip); static void setSkipSwitcherCallback(wl_client *client, wl_resource *resource, uint32_t skip); static void panelAutoHideHideCallback(wl_client *client, wl_resource *resource); static void panelAutoHideShowCallback(wl_client *client, wl_resource *resource); static void panelTakesFocusCallback(wl_client *client, wl_resource *resource, uint32_t takesFocus); void setPosition(const QPoint &globalPos); void setRole(uint32_t role); void setPanelBehavior(org_kde_plasma_surface_panel_behavior behavior); PlasmaShellSurfaceInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_plasma_surface_interface s_interface; }; PlasmaShellInterface::PlasmaShellInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaShellInterface::~PlasmaShellInterface() = default; void PlasmaShellInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&org_kde_plasma_shell_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, nullptr); } void PlasmaShellInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void PlasmaShellInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](PlasmaShellSurfaceInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), WL_DISPLAY_ERROR_INVALID_OBJECT, "PlasmaShellSurface already created"); return; } PlasmaShellSurfaceInterface *shellSurface = new PlasmaShellSurfaceInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &PlasmaShellSurfaceInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } /********************************* * ShellSurfaceInterface *********************************/ PlasmaShellSurfaceInterface::Private::Private(PlasmaShellSurfaceInterface *q, PlasmaShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource) : Resource::Private(q, shell, parentResource, &org_kde_plasma_surface_interface, &s_interface) , surface(surface) { } #ifndef K_DOXYGEN const struct org_kde_plasma_surface_interface PlasmaShellSurfaceInterface::Private::s_interface = { resourceDestroyedCallback, setOutputCallback, setPositionCallback, setRoleCallback, setPanelBehaviorCallback, setSkipTaskbarCallback, panelAutoHideHideCallback, panelAutoHideShowCallback, panelTakesFocusCallback, setSkipSwitcherCallback }; #endif PlasmaShellSurfaceInterface::PlasmaShellSurfaceInterface(PlasmaShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource) : Resource(new Private(this, shell, parent, parentResource)) { auto unsetSurface = [this] { Q_D(); d->surface = nullptr; }; connect(parent, &Resource::unbound, this, unsetSurface); connect(parent, &QObject::destroyed, this, unsetSurface); } PlasmaShellSurfaceInterface::~PlasmaShellSurfaceInterface() = default; SurfaceInterface *PlasmaShellSurfaceInterface::surface() const { Q_D(); return d->surface; } PlasmaShellInterface *PlasmaShellSurfaceInterface::shell() const { Q_D(); return reinterpret_cast(d->global); } PlasmaShellSurfaceInterface::Private *PlasmaShellSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } void PlasmaShellSurfaceInterface::Private::setOutputCallback(wl_client *client, wl_resource *resource, wl_resource *output) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(output) // TODO: implement } void PlasmaShellSurfaceInterface::Private::setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setPosition(QPoint(x, y)); } void PlasmaShellSurfaceInterface::Private::setPosition(const QPoint &globalPos) { if (m_globalPos == globalPos && m_positionSet) { return; } m_positionSet = true; m_globalPos = globalPos; Q_Q(PlasmaShellSurfaceInterface); emit q->positionChanged(); } void PlasmaShellSurfaceInterface::Private::setRoleCallback(wl_client *client, wl_resource *resource, uint32_t role) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setRole(role); } void PlasmaShellSurfaceInterface::Private::setRole(uint32_t role) { Role r = Role::Normal; switch (role) { case ORG_KDE_PLASMA_SURFACE_ROLE_DESKTOP: r = Role::Desktop; break; case ORG_KDE_PLASMA_SURFACE_ROLE_PANEL: r = Role::Panel; break; case ORG_KDE_PLASMA_SURFACE_ROLE_ONSCREENDISPLAY: r = Role::OnScreenDisplay; break; case ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION: r = Role::Notification; break; case ORG_KDE_PLASMA_SURFACE_ROLE_TOOLTIP: r = Role::ToolTip; break; case ORG_KDE_PLASMA_SURFACE_ROLE_CRITICALNOTIFICATION: r = Role::CriticalNotification; break; case ORG_KDE_PLASMA_SURFACE_ROLE_NORMAL: default: r = Role::Normal; break; } if (r == m_role) { return; } m_role = r; Q_Q(PlasmaShellSurfaceInterface); emit q->roleChanged(); } void PlasmaShellSurfaceInterface::Private::setPanelBehaviorCallback(wl_client *client, wl_resource *resource, uint32_t flag) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setPanelBehavior(org_kde_plasma_surface_panel_behavior(flag)); } void PlasmaShellSurfaceInterface::Private::setSkipTaskbarCallback(wl_client *client, wl_resource *resource, uint32_t skip) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_skipTaskbar = (bool)skip; emit s->q_func()->skipTaskbarChanged(); } void PlasmaShellSurfaceInterface::Private::setSkipSwitcherCallback(wl_client *client, wl_resource *resource, uint32_t skip) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_skipSwitcher = (bool)skip; emit s->q_func()->skipSwitcherChanged(); } void PlasmaShellSurfaceInterface::Private::panelAutoHideHideCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_role != Role::Panel || s->m_panelBehavior != PanelBehavior::AutoHide) { wl_resource_post_error(s->resource, ORG_KDE_PLASMA_SURFACE_ERROR_PANEL_NOT_AUTO_HIDE, "Not an auto hide panel"); return; } emit s->q_func()->panelAutoHideHideRequested(); } void PlasmaShellSurfaceInterface::Private::panelAutoHideShowCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_role != Role::Panel || s->m_panelBehavior != PanelBehavior::AutoHide) { wl_resource_post_error(s->resource, ORG_KDE_PLASMA_SURFACE_ERROR_PANEL_NOT_AUTO_HIDE, "Not an auto hide panel"); return; } emit s->q_func()->panelAutoHideShowRequested(); } void PlasmaShellSurfaceInterface::Private::panelTakesFocusCallback(wl_client *client, wl_resource *resource, uint32_t takesFocus) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->panelTakesFocus == takesFocus) { return; } s->panelTakesFocus = takesFocus; emit s->q_func()->panelTakesFocusChanged(); } void PlasmaShellSurfaceInterface::Private::setPanelBehavior(org_kde_plasma_surface_panel_behavior behavior) { PanelBehavior newBehavior = PanelBehavior::AlwaysVisible; switch (behavior) { case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_AUTO_HIDE: newBehavior = PanelBehavior::AutoHide; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_CAN_COVER: newBehavior = PanelBehavior::WindowsCanCover; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_WINDOWS_GO_BELOW: newBehavior = PanelBehavior::WindowsGoBelow; break; case ORG_KDE_PLASMA_SURFACE_PANEL_BEHAVIOR_ALWAYS_VISIBLE: default: break; } if (m_panelBehavior == newBehavior) { return; } m_panelBehavior = newBehavior; Q_Q(PlasmaShellSurfaceInterface); emit q->panelBehaviorChanged(); } QPoint PlasmaShellSurfaceInterface::position() const { Q_D(); return d->m_globalPos; } PlasmaShellSurfaceInterface::Role PlasmaShellSurfaceInterface::role() const { Q_D(); return d->m_role; } bool PlasmaShellSurfaceInterface::isPositionSet() const { Q_D(); return d->m_positionSet; } PlasmaShellSurfaceInterface::PanelBehavior PlasmaShellSurfaceInterface::panelBehavior() const { Q_D(); return d->m_panelBehavior; } bool PlasmaShellSurfaceInterface::skipTaskbar() const { Q_D(); return d->m_skipTaskbar; } bool PlasmaShellSurfaceInterface::skipSwitcher() const { Q_D(); return d->m_skipSwitcher; } void PlasmaShellSurfaceInterface::hideAutoHidingPanel() { Q_D(); if (!d->resource) { return; } org_kde_plasma_surface_send_auto_hidden_panel_hidden(d->resource); } void PlasmaShellSurfaceInterface::showAutoHidingPanel() { Q_D(); if (!d->resource) { return; } org_kde_plasma_surface_send_auto_hidden_panel_shown(d->resource); } bool PlasmaShellSurfaceInterface::panelTakesFocus() const { Q_D(); return d->panelTakesFocus; } PlasmaShellSurfaceInterface *PlasmaShellSurfaceInterface::get(wl_resource *native) { return Private::get(native); } } } diff --git a/src/server/plasmashell_interface.h b/src/server/plasmashell_interface.h index ee82ecd..0fcc6e3 100644 --- a/src/server/plasmashell_interface.h +++ b/src/server/plasmashell_interface.h @@ -1,258 +1,244 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_PLASMA_SHELL_INTERFACE_H #define WAYLAND_SERVER_PLASMA_SHELL_INTERFACE_H #include #include #include "global.h" #include "resource.h" class QSize; struct wl_resource; namespace KWayland { namespace Server { class Display; class SurfaceInterface; class PlasmaShellSurfaceInterface; /** * @brief Global for the org_kde_plasma_shell interface. * * The PlasmaShellInterface allows to add additional information to a SurfaceInterface. * It goes beyond what a ShellSurfaceInterface provides and is adjusted toward the needs * of the Plasma desktop. * * A server providing this interface should think about how to restrict access to it as * it allows to perform absolute window positioning. * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT PlasmaShellInterface : public Global { Q_OBJECT public: virtual ~PlasmaShellInterface(); Q_SIGNALS: /** * Emitted whenever a PlasmaShellSurfaceInterface got created. **/ void surfaceCreated(KWayland::Server::PlasmaShellSurfaceInterface*); private: friend class Display; explicit PlasmaShellInterface(Display *display, QObject *parent); class Private; }; /** * @brief Resource for the org_kde_plasma_shell_surface interface. * * PlasmaShellSurfaceInterface gets created by PlasmaShellInterface. * * @since 5.4 **/ class KWAYLANDSERVER_EXPORT PlasmaShellSurfaceInterface : public Resource { Q_OBJECT public: virtual ~PlasmaShellSurfaceInterface(); /** * @returns the SurfaceInterface this PlasmaShellSurfaceInterface got created for **/ SurfaceInterface *surface() const; /** * @returns The PlasmaShellInterface which created this PlasmaShellSurfaceInterface. **/ PlasmaShellInterface *shell() const; /** * @returns the requested position in global coordinates. **/ QPoint position() const; /** * @returns Whether a global position has been requested. **/ bool isPositionSet() const; /** * Describes possible roles this PlasmaShellSurfaceInterface can have. * The role can be used by the server to e.g. change the stacking order accordingly. **/ enum class Role { Normal, ///< A normal surface Desktop, ///< The surface represents a desktop, normally stacked below all other surfaces Panel, ///< The surface represents a panel (dock), normally stacked above normal surfaces OnScreenDisplay, ///< The surface represents an on screen display, like a volume changed notification Notification, ///< The surface represents a notification @since 5.24 ToolTip, ///< The surface represents a tooltip @since 5.24 CriticalNotification, ///< The surface represents a critical notification, like battery is running out @since 5.58 }; /** * @returns The requested role, default value is @c Role::Normal. **/ Role role() const; /** * Describes how a PlasmaShellSurfaceInterface with role @c Role::Panel should behave. **/ enum class PanelBehavior { AlwaysVisible, ///< The panel should be always visible AutoHide, ///< The panel auto hides at a screen edge and returns on mouse press against edge WindowsCanCover, ///< Windows are allowed to go above the panel, it raises on mouse press against screen edge WindowsGoBelow ///< Window are allowed to go below the panel }; /** * @returns The PanelBehavior for a PlasmaShellSurfaceInterface with role @c Role::Panel * @see role **/ PanelBehavior panelBehavior() const; /** * @returns true if this window doesn't want to be listed * in the taskbar * @since 5.5 **/ bool skipTaskbar() const; /** * @returns true if this window doesn't want to be listed * in a window switcher * @since 5.47 **/ bool skipSwitcher() const; /** * Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got hidden. * Once it is shown again the method {@link showAutoHidingPanel} should be used. * * @see showAutoHidingPanel * @see panelAutoHideHideRequested * @see panelAutoHideShowRequested * @since 5.28 **/ void hideAutoHidingPanel(); /** * Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got shown again. * * @see hideAutoHidingPanel * @see panelAutoHideHideRequested * @see panelAutoHideShowRequested * @see 5.28 **/ void showAutoHidingPanel(); /** * Whether a PlasmaShellSurfaceInterface wants to have focus. * * By default some PlasmaShell roles do not get focus, but the PlasmaShellSurfaceInterface can * request that it wants to have focus. The compositor can use this information to * pass focus to the surface. * @since 5.28 **/ //TODO KF6 rename to something generic bool panelTakesFocus() const; /** * @returns The PlasmaShellSurfaceInterface for the @p native resource. * @since 5.5 **/ static PlasmaShellSurfaceInterface *get(wl_resource *native); Q_SIGNALS: /** * A change of global position has been requested. **/ void positionChanged(); /** * A change of the role has been requested. **/ void roleChanged(); /** * A change of the panel behavior has been requested. **/ void panelBehaviorChanged(); /** * A change in the skip taskbar property has been requested */ void skipTaskbarChanged(); /** * A change in the skip switcher property has been requested **/ void skipSwitcherChanged(); /** * A surface with Role Panel and PanelBehavior AutoHide requested to be hidden. * * The compositor should inform the PlasmaShellSurfaceInterface about the actual change. * Once the surface is hidden it should invoke {@link hideAutoHidingPanel}. If the compositor * cannot hide the surface (e.g. because it doesn't border a screen edge) it should inform * the surface through invoking {@link showAutoHidingPanel}. This method should also be invoked * whenever the surface gets shown again due to triggering the screen edge. * * @see hideAutoHidingPanel * @see showAutoHidingPanel * @see panelAutoHideShowRequested * @since 5.28 **/ void panelAutoHideHideRequested(); /** * A surface with Role Panel and PanelBehavior AutoHide requested to be shown. * * The compositor should inform the PlasmaShellSurfaceInterface about the actual change. * Once the surface is shown it should invoke {@link showAutoHidingPanel}. * * @see hideAutoHidingPanel * @see showAutoHidingPanel * @see panelAutoHideHideRequested * @since 5.28 **/ void panelAutoHideShowRequested(); /* * Emitted when panelTakesFocus changes * @see panelTakesFocus * @since 5.66 */ void panelTakesFocusChanged(); private: friend class PlasmaShellInterface; explicit PlasmaShellSurfaceInterface(PlasmaShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::PlasmaShellSurfaceInterface::Role) Q_DECLARE_METATYPE(KWayland::Server::PlasmaShellSurfaceInterface::PanelBehavior) #endif diff --git a/src/server/plasmavirtualdesktop_interface.cpp b/src/server/plasmavirtualdesktop_interface.cpp index 8c7c7b4..88af8de 100644 --- a/src/server/plasmavirtualdesktop_interface.cpp +++ b/src/server/plasmavirtualdesktop_interface.cpp @@ -1,416 +1,402 @@ -/**************************************************************************** -Copyright 2018 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmavirtualdesktop_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include #include #include #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN PlasmaVirtualDesktopInterface::Private { public: Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c); ~Private(); void createResource(wl_resource *parent, quint32 serial); PlasmaVirtualDesktopInterface *q; PlasmaVirtualDesktopManagementInterface *vdm; QVector resources; QString id; QString name; bool active = false; private: static void unbind(wl_resource *resource); static void requestActivateCallback(wl_client *client, wl_resource *resource); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } wl_listener listener; static const struct org_kde_plasma_virtual_desktop_interface s_interface; }; class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementInterface::Private : public Global::Private { public: Private(PlasmaVirtualDesktopManagementInterface *q, Display *d); QVector resources; QList desktops; quint32 rows = 0; quint32 columns = 0; inline QList::const_iterator constFindDesktop(const QString &id); inline QList::iterator findDesktop(const QString &id); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id); static void requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position); static void requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); PlasmaVirtualDesktopManagementInterface *q; static const struct org_kde_plasma_virtual_desktop_management_interface s_interface; static const quint32 s_version; }; const quint32 PlasmaVirtualDesktopManagementInterface::Private::s_version = 2; #ifndef K_DOXYGEN const struct org_kde_plasma_virtual_desktop_management_interface PlasmaVirtualDesktopManagementInterface::Private::s_interface = { getVirtualDesktopCallback, requestCreateVirtualDesktopCallback, requestRemoveVirtualDesktopCallback }; #endif inline QList::const_iterator PlasmaVirtualDesktopManagementInterface::Private::constFindDesktop(const QString &id) { return std::find_if( desktops.constBegin(), desktops.constEnd(), [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); } inline QList::iterator PlasmaVirtualDesktopManagementInterface::Private::findDesktop(const QString &id) { return std::find_if( desktops.begin(), desktops.end(), [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); } void PlasmaVirtualDesktopManagementInterface::Private::getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id) { Q_UNUSED(client) auto s = cast(resource); auto i = s->constFindDesktop(QString::fromUtf8(id)); if (i == s->desktops.constEnd()) { return; } (*i)->d->createResource(resource, serial); } void PlasmaVirtualDesktopManagementInterface::Private::requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position) { Q_UNUSED(client) auto s = cast(resource); emit s->q->desktopCreateRequested(QString::fromUtf8(name), qBound(0, position, (quint32)s->desktops.count())); } void PlasmaVirtualDesktopManagementInterface::Private::requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) auto s = cast(resource); emit s->q->desktopRemoveRequested(QString::fromUtf8(id)); } PlasmaVirtualDesktopManagementInterface::Private::Private(PlasmaVirtualDesktopManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_virtual_desktop_management_interface, s_version) , q(q) { } void PlasmaVirtualDesktopManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_management_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources << resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); quint32 i = 0; for (auto it = desktops.constBegin(); it != desktops.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_created(resource, (*it)->id().toUtf8().constData(), i++); } if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_VIRTUAL_DESKTOP_MANAGEMENT_ROWS_SINCE_VERSION) { org_kde_plasma_virtual_desktop_management_send_rows(resource, rows); } org_kde_plasma_virtual_desktop_management_send_done(resource); } void PlasmaVirtualDesktopManagementInterface::Private::unbind(wl_resource *resource) { auto dm = reinterpret_cast(wl_resource_get_user_data(resource)); dm->resources.removeAll(resource); } PlasmaVirtualDesktopManagementInterface::PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaVirtualDesktopManagementInterface::~PlasmaVirtualDesktopManagementInterface() { Q_D(); qDeleteAll(d->desktops); } PlasmaVirtualDesktopManagementInterface::Private *PlasmaVirtualDesktopManagementInterface::d_func() const { return reinterpret_cast(d.data()); } void PlasmaVirtualDesktopManagementInterface::setRows(quint32 rows) { Q_D(); if (rows == 0 || d->rows == rows) { return; } d->rows = rows; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { if (wl_resource_get_version(*it) < ORG_KDE_PLASMA_VIRTUAL_DESKTOP_MANAGEMENT_ROWS_SINCE_VERSION) { continue; } org_kde_plasma_virtual_desktop_management_send_rows(*it, rows); } } PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::desktop(const QString &id) { Q_D(); auto i = d->constFindDesktop(id); if (i != d->desktops.constEnd()) { return *i; } return nullptr; } PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::createDesktop(const QString &id, quint32 position) { Q_D(); auto i = d->constFindDesktop(id); if (i != d->desktops.constEnd()) { return *i; } const quint32 actualPosition = qMin(position, (quint32)d->desktops.count()); - + PlasmaVirtualDesktopInterface *desktop = new PlasmaVirtualDesktopInterface(this); desktop->d->id = id; for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_desktop_id(*it, id.toUtf8().constData()); } //activate the first desktop TODO: to be done here? if (d->desktops.isEmpty()) { desktop->d->active = true; } d->desktops.insert(actualPosition, desktop); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_created(*it, id.toUtf8().constData(), actualPosition); } return desktop; } void PlasmaVirtualDesktopManagementInterface::removeDesktop(const QString &id) { Q_D(); auto deskIt = d->findDesktop(id); if (deskIt == d->desktops.end()) { return; } for (auto it = (*deskIt)->d->resources.constBegin(); it != (*deskIt)->d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_removed(*it); } for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); } (*deskIt)->deleteLater(); d->desktops.erase(deskIt); } QList PlasmaVirtualDesktopManagementInterface::desktops() const { Q_D(); return d->desktops; } void PlasmaVirtualDesktopManagementInterface::sendDone() { Q_D(); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_done(*it); } } //// PlasmaVirtualDesktopInterface #ifndef K_DOXYGEN const struct org_kde_plasma_virtual_desktop_interface PlasmaVirtualDesktopInterface::Private::s_interface = { requestActivateCallback }; #endif void PlasmaVirtualDesktopInterface::Private::requestActivateCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) auto s = cast(resource); emit s->q->activateRequested(); } PlasmaVirtualDesktopInterface::Private::Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c) : q(q), vdm(c) { } PlasmaVirtualDesktopInterface::Private::~Private() { // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate const auto c = resources; for (const auto &r : c) { auto client = wl_resource_get_client(r); org_kde_plasma_virtual_desktop_send_removed(r); wl_resource_destroy(r); wl_client_flush(client); } } void PlasmaVirtualDesktopInterface::Private::unbind(wl_resource *resource) { Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->resources.removeAll(resource); } void PlasmaVirtualDesktopInterface::Private::createResource(wl_resource *parent, quint32 serial) { ClientConnection *c = vdm->display()->getConnection(wl_resource_get_client(parent)); wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_interface, wl_resource_get_version(parent), serial); if (!resource) { return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_plasma_virtual_desktop_send_desktop_id(resource, id.toUtf8().constData()); if (!name.isEmpty()) { org_kde_plasma_virtual_desktop_send_name(resource, name.toUtf8().constData()); } if (active) { org_kde_plasma_virtual_desktop_send_activated(resource); } c->flush(); } PlasmaVirtualDesktopInterface::PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent) : QObject(parent), d(new Private(this, parent)) { } PlasmaVirtualDesktopInterface::~PlasmaVirtualDesktopInterface() { d->vdm->removeDesktop(id()); } QString PlasmaVirtualDesktopInterface::id() const { return d->id; } void PlasmaVirtualDesktopInterface::setName(const QString &name) { if (d->name == name) { return; } d->name = name; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_name(*it, name.toUtf8().constData()); } } QString PlasmaVirtualDesktopInterface::name() const { return d->name; } void PlasmaVirtualDesktopInterface::setActive(bool active) { if (d->active == active) { return; } d->active = active; if (active) { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_activated(*it); } } else { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_deactivated(*it); } } } bool PlasmaVirtualDesktopInterface::isActive() const { return d->active; } void PlasmaVirtualDesktopInterface::sendDone() { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_done(*it); } } } } diff --git a/src/server/plasmavirtualdesktop_interface.h b/src/server/plasmavirtualdesktop_interface.h index b67078f..d44bdfa 100644 --- a/src/server/plasmavirtualdesktop_interface.h +++ b/src/server/plasmavirtualdesktop_interface.h @@ -1,172 +1,158 @@ -/**************************************************************************** -Copyright 2018 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H #define KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class PlasmaVirtualDesktopInterface; /** * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. * * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. * @since 5.52 */ class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopManagementInterface : public Global { Q_OBJECT public: virtual ~PlasmaVirtualDesktopManagementInterface(); /** * Sets how many rows the virtual desktops should be laid into * @since 5.55 */ void setRows(quint32 rows); /** * @returns A desktop identified uniquely by this id. * If not found, nullptr will be returned. * @see createDesktop */ PlasmaVirtualDesktopInterface *desktop(const QString &id); /** * @returns A desktop identified uniquely by this id, if not found * a new desktop will be created for this id at a given position. * @param id the id for the desktop * @param position the position the desktop will be in, if not provided, * it will be appended at the end. If the desktop was already * existing, position is ignored. */ PlasmaVirtualDesktopInterface *createDesktop(const QString &id, quint32 position = std::numeric_limits::max()); /** * Removed and destroys the desktop identified by id, if present */ void removeDesktop(const QString &id); /** * @returns All tghe desktops present. */ QList desktops() const; /** * Inform the clients that all the properties have been sent, and * their client-side representation is complete. */ void sendDone(); Q_SIGNALS: /** * A desktop has been activated */ void desktopActivated(const QString &id); /** * The client asked to remove a desktop, It's responsibility of the server * deciding whether to remove it or not. */ void desktopRemoveRequested(const QString &id); /** * The client asked to create a desktop, It's responsibility of the server * deciding whether to create it or not. * @param name The desired user readable name * @param position The desired position. Normalized, guaranteed to be in the range 0-count */ void desktopCreateRequested(const QString &name, quint32 position); private: explicit PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopInterface : public QObject { Q_OBJECT public: virtual ~PlasmaVirtualDesktopInterface(); /** * @returns the unique id for this desktop. * ids are set at creation time by PlasmaVirtualDesktopManagementInterface::createDesktop * and can't be changed at runtime. */ QString id() const; /** * Sets a new name for this desktop */ void setName(const QString &name); /** * @returns the name for this desktop */ QString name() const; /** * Set this desktop as active or not. * It's the compositor responsibility to manage the mutual exclusivity of active desktops. */ void setActive(bool active); /** * @returns true if this desktop is active. Only one at a time will be. */ bool isActive() const; /** * Inform the clients that all the properties have been sent, and * their client-side representation is complete. */ void sendDone(); Q_SIGNALS: /** * Emitted when the client asked to activate this desktop: * it's the decision of the server whether to perform the activation or not. */ void activateRequested(); private: explicit PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent); friend class PlasmaVirtualDesktopManagementInterface; class Private; const QScopedPointer d; }; } } #endif diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp index 57f49e2..4e611a0 100644 --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -1,967 +1,953 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "plasmawindowmanagement_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include "plasmavirtualdesktop_interface.h" #include #include #include #include #include #include #include #include #include namespace KWayland { namespace Server { class PlasmaWindowManagementInterface::Private : public Global::Private { public: Private(PlasmaWindowManagementInterface *q, Display *d); void sendShowingDesktopState(); ShowingDesktopState state = ShowingDesktopState::Disabled; QVector resources; QList windows; QPointer plasmaVirtualDesktopManagementInterface = nullptr; quint32 windowIdCounter = 0; private: static void unbind(wl_resource *resource); static void showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state); static void getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId); void bind(wl_client *client, uint32_t version, uint32_t id) override; void sendShowingDesktopState(wl_resource *r); PlasmaWindowManagementInterface *q; static const struct org_kde_plasma_window_management_interface s_interface; static const quint32 s_version; }; class PlasmaWindowInterface::Private { public: Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q); ~Private(); void createResource(wl_resource *parent, uint32_t id); void setTitle(const QString &title); void setAppId(const QString &appId); void setPid(quint32 pid); void setThemedIconName(const QString &iconName); void setIcon(const QIcon &icon); void setVirtualDesktop(quint32 desktop); void unmap(); void setState(org_kde_plasma_window_management_state flag, bool set); void setParentWindow(PlasmaWindowInterface *parent); void setGeometry(const QRect &geometry); void setApplicationMenuPaths(const QString &service, const QString &object); wl_resource *resourceForParent(PlasmaWindowInterface *parent, wl_resource *child) const; QVector resources; quint32 windowId = 0; QHash minimizedGeometries; PlasmaWindowManagementInterface *wm; bool unmapped = false; PlasmaWindowInterface *parentWindow = nullptr; QMetaObject::Connection parentWindowDestroyConnection; QStringList plasmaVirtualDesktops; QRect geometry; private: static void unbind(wl_resource *resource); static void setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state); static void setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number); static void closeCallback(wl_client *client, wl_resource *resource); static void requestMoveCallback(wl_client *client, wl_resource *resource); static void requestResizeCallback(wl_client *client, wl_resource *resource); static void setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height); static void unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel); static void destroyCallback(wl_client *client, wl_resource *resource); static void getIconCallback(wl_client *client, wl_resource *resource, int32_t fd); static void requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); static void requestEnterNewVirtualDesktopCallback(wl_client *client, wl_resource *resource); static void requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } PlasmaWindowInterface *q; QString m_title; QString m_appId; quint32 m_pid = 0; QString m_themedIconName; QIcon m_icon; quint32 m_virtualDesktop = 0; quint32 m_state = 0; wl_listener listener; static const struct org_kde_plasma_window_interface s_interface; }; const quint32 PlasmaWindowManagementInterface::Private::s_version = 10; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct org_kde_plasma_window_management_interface PlasmaWindowManagementInterface::Private::s_interface = { showDesktopCallback, getWindowCallback }; #endif void PlasmaWindowManagementInterface::Private::sendShowingDesktopState() { for (wl_resource *r : resources) { sendShowingDesktopState(r); } } void PlasmaWindowManagementInterface::Private::sendShowingDesktopState(wl_resource *r) { uint32_t s = 0; switch (state) { case ShowingDesktopState::Enabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED; break; case ShowingDesktopState::Disabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_window_management_send_show_desktop_changed(r, s); } void PlasmaWindowManagementInterface::Private::showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state) { Q_UNUSED(client) ShowingDesktopState s = ShowingDesktopState::Disabled; switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: s = ShowingDesktopState::Enabled; break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: default: s = ShowingDesktopState::Disabled; break; } emit reinterpret_cast(wl_resource_get_user_data(resource))->q->requestChangeShowingDesktop(s); } void PlasmaWindowManagementInterface::Private::getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId) { Q_UNUSED(client) auto p = reinterpret_cast(wl_resource_get_user_data(resource)); auto it = std::find_if(p->windows.constBegin(), p->windows.constEnd(), [internalWindowId] (PlasmaWindowInterface *window) { return window->d->windowId == internalWindowId; } ); if (it == p->windows.constEnd()) { // create a temp window just for the resource and directly send an unmapped PlasmaWindowInterface *window = new PlasmaWindowInterface(p->q, p->q); window->d->unmapped = true; window->d->createResource(resource, id); return; } (*it)->d->createResource(resource, id); } PlasmaWindowManagementInterface::PlasmaWindowManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaWindowManagementInterface::~PlasmaWindowManagementInterface() = default; void PlasmaWindowManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&org_kde_plasma_window_management_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, unbind); resources << shell; for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) { org_kde_plasma_window_management_send_window(shell, (*it)->d->windowId); } } void PlasmaWindowManagementInterface::Private::unbind(wl_resource *resource) { auto wm = reinterpret_cast(wl_resource_get_user_data(resource)); wm->resources.removeAll(resource); } void PlasmaWindowManagementInterface::setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState state) { Q_D(); if (d->state == state) { return; } d->state = state; d->sendShowingDesktopState(); } PlasmaWindowManagementInterface::Private *PlasmaWindowManagementInterface::d_func() const { return reinterpret_cast(d.data()); } PlasmaWindowInterface *PlasmaWindowManagementInterface::createWindow(QObject *parent) { Q_D(); PlasmaWindowInterface *window = new PlasmaWindowInterface(this, parent); // TODO: improve window ids so that it cannot wrap around window->d->windowId = ++d->windowIdCounter; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_management_send_window(*it, window->d->windowId); } d->windows << window; connect(window, &QObject::destroyed, this, [this, window] { Q_D(); d->windows.removeAll(window); } ); return window; } QList PlasmaWindowManagementInterface::windows() const { Q_D(); return d->windows; } void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { return; } Q_D(); d->windows.removeOne(window); Q_ASSERT(!d->windows.contains(window)); window->d->unmap(); } void PlasmaWindowManagementInterface::setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager) { Q_D(); if (d->plasmaVirtualDesktopManagementInterface == manager) { return; } d->plasmaVirtualDesktopManagementInterface = manager; } PlasmaVirtualDesktopManagementInterface *PlasmaWindowManagementInterface::plasmaVirtualDesktopManagementInterface() const { Q_D(); return d->plasmaVirtualDesktopManagementInterface; } #ifndef K_DOXYGEN const struct org_kde_plasma_window_interface PlasmaWindowInterface::Private::s_interface = { setStateCallback, setVirtualDesktopCallback, setMinimizedGeometryCallback, unsetMinimizedGeometryCallback, closeCallback, requestMoveCallback, requestResizeCallback, destroyCallback, getIconCallback, requestEnterVirtualDesktopCallback, requestEnterNewVirtualDesktopCallback, requestLeaveVirtualDesktopCallback }; #endif PlasmaWindowInterface::Private::Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q) : wm(wm) , q(q) { } PlasmaWindowInterface::Private::~Private() { // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate const auto c = resources; for (const auto &r : c) { auto client = wl_resource_get_client(r); org_kde_plasma_window_send_unmapped(r); wl_resource_destroy(r); wl_client_flush(client); } } void PlasmaWindowInterface::Private::destroyCallback(wl_client *, wl_resource *r) { Private *p = cast(r); p->resources.removeAll(r); wl_resource_destroy(r); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::unbind(wl_resource *resource) { Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->resources.removeAll(resource); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::createResource(wl_resource *parent, uint32_t id) { ClientConnection *c = wm->display()->getConnection(wl_resource_get_client(parent)); wl_resource *resource = c->createResource(&org_kde_plasma_window_interface, wl_resource_get_version(parent), id); if (!resource) { return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_plasma_window_send_virtual_desktop_changed(resource, m_virtualDesktop); for (const auto &desk : plasmaVirtualDesktops) { org_kde_plasma_window_send_virtual_desktop_entered(resource, desk.toUtf8().constData()); } if (!m_appId.isEmpty()) { org_kde_plasma_window_send_app_id_changed(resource, m_appId.toUtf8().constData()); } if (m_pid != 0) { org_kde_plasma_window_send_pid_changed(resource, m_pid); } if (!m_title.isEmpty()) { org_kde_plasma_window_send_title_changed(resource, m_title.toUtf8().constData()); } org_kde_plasma_window_send_state_changed(resource, m_state); if (!m_themedIconName.isEmpty()) { org_kde_plasma_window_send_themed_icon_name_changed(resource, m_themedIconName.toUtf8().constData()); } else { if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) { org_kde_plasma_window_send_icon_changed(resource); } } org_kde_plasma_window_send_parent_window(resource, resourceForParent(parentWindow, resource)); if (unmapped) { org_kde_plasma_window_send_unmapped(resource); } if (geometry.isValid() && wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) { org_kde_plasma_window_send_geometry(resource, geometry.x(), geometry.y(), geometry.width(), geometry.height()); } if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_INITIAL_STATE_SINCE_VERSION) { org_kde_plasma_window_send_initial_state(resource); } c->flush(); } void PlasmaWindowInterface::Private::setAppId(const QString &appId) { if (m_appId == appId) { return; } m_appId = appId; const QByteArray utf8 = m_appId.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_app_id_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setPid(quint32 pid) { if (m_pid == pid) { return; } m_pid = pid; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_pid_changed(*it, pid); } } void PlasmaWindowInterface::Private::setThemedIconName(const QString &iconName) { if (m_themedIconName == iconName) { return; } m_themedIconName = iconName; const QByteArray utf8 = m_themedIconName.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_themed_icon_name_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setIcon(const QIcon &icon) { m_icon = icon; setThemedIconName(m_icon.name()); if (m_icon.name().isEmpty()) { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { if (wl_resource_get_version(*it) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) { org_kde_plasma_window_send_icon_changed(*it); } } } } void PlasmaWindowInterface::Private::getIconCallback(wl_client *client, wl_resource *resource, int32_t fd) { Q_UNUSED(client) Private *p = cast(resource); QtConcurrent::run( [fd] (const QIcon &icon) { QFile file; file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle); QDataStream ds(&file); ds << icon; file.close(); }, p->m_icon ); } void PlasmaWindowInterface::Private::requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->enterPlasmaVirtualDesktopRequested(QString::fromUtf8(id)); } void PlasmaWindowInterface::Private::requestEnterNewVirtualDesktopCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->enterNewPlasmaVirtualDesktopRequested(); } void PlasmaWindowInterface::Private::requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->leavePlasmaVirtualDesktopRequested(QString::fromUtf8(id)); } void PlasmaWindowInterface::Private::setTitle(const QString &title) { if (m_title == title) { return; } m_title = title; const QByteArray utf8 = m_title.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_title_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setVirtualDesktop(quint32 desktop) { if (m_virtualDesktop == desktop) { return; } m_virtualDesktop = desktop; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_changed(*it, m_virtualDesktop); } } void PlasmaWindowInterface::Private::unmap() { unmapped = true; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_unmapped(*it); } if (resources.isEmpty()) { q->deleteLater(); } } void PlasmaWindowInterface::Private::setState(org_kde_plasma_window_management_state flag, bool set) { quint32 newState = m_state; if (set) { newState |= flag; } else { newState &= ~flag; } if (newState == m_state) { return; } m_state = newState; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_state_changed(*it, m_state); } } wl_resource *PlasmaWindowInterface::Private::resourceForParent(PlasmaWindowInterface *parent, wl_resource *child) const { if (!parent) { return nullptr; } auto it = std::find_if(parent->d->resources.begin(), parent->d->resources.end(), [child] (wl_resource *parentResource) { return wl_resource_get_client(child) == wl_resource_get_client(parentResource); } ); if (it != parent->d->resources.end()) { return *it; } return nullptr; } void PlasmaWindowInterface::Private::setParentWindow(PlasmaWindowInterface *window) { if (parentWindow == window) { return; } QObject::disconnect(parentWindowDestroyConnection); parentWindowDestroyConnection = QMetaObject::Connection(); parentWindow = window; if (parentWindow) { parentWindowDestroyConnection = QObject::connect(window, &QObject::destroyed, q, [this] { parentWindow = nullptr; parentWindowDestroyConnection = QMetaObject::Connection(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_parent_window(*it, nullptr); } } ); } for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_parent_window(*it, resourceForParent(window, *it)); } } void PlasmaWindowInterface::Private::setGeometry(const QRect &geo) { if (geometry == geo) { return; } geometry = geo; if (!geometry.isValid()) { return; } for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { auto resource = *it; if (wl_resource_get_version(resource) < ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) { continue; } org_kde_plasma_window_send_geometry(resource, geometry.x(), geometry.y(), geometry.width(), geometry.height()); } } void PlasmaWindowInterface::Private::setApplicationMenuPaths(const QString &service, const QString &object) { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { auto resource = *it; if (wl_resource_get_version(resource) < ORG_KDE_PLASMA_WINDOW_APPLICATION_MENU_SINCE_VERSION) { continue; } org_kde_plasma_window_send_application_menu(resource, qUtf8Printable(service), qUtf8Printable(object)); } } void PlasmaWindowInterface::Private::closeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->closeRequested(); } void PlasmaWindowInterface::Private::requestMoveCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->moveRequested(); } void PlasmaWindowInterface::Private::requestResizeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->resizeRequested(); } void PlasmaWindowInterface::Private::setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->virtualDesktopRequested(number); } void PlasmaWindowInterface::Private::setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state) { Q_UNUSED(client) Private *p = cast(resource); if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) { emit p->q->activeRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED) { emit p->q->minimizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED) { emit p->q->maximizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN) { emit p->q->fullscreenRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE) { emit p->q->keepAboveRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW) { emit p->q->keepBelowRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION) { emit p->q->demandsAttentionRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE) { emit p->q->closeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE) { emit p->q->minimizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE) { emit p->q->maximizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE) { emit p->q->fullscreenableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR) { emit p->q->skipTaskbarRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER) { emit p->q->skipSwitcherRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE) { emit p->q->shadeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED) { emit p->q->shadedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE) { emit p->q->movableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE) { emit p->q->resizableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE) { emit p->q->virtualDesktopChangeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } } void PlasmaWindowInterface::Private::setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (p->minimizedGeometries.value(panelSurface) == QRect(x, y, width, height)) { return; } p->minimizedGeometries[panelSurface] = QRect(x, y, width, height); emit p->q->minimizedGeometriesChanged(); connect(panelSurface, &QObject::destroyed, p->q, [p, panelSurface] () { if (p->minimizedGeometries.remove(panelSurface)) { emit p->q->minimizedGeometriesChanged(); } }); } void PlasmaWindowInterface::Private::unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (!p->minimizedGeometries.contains(panelSurface)) { return; } p->minimizedGeometries.remove(panelSurface); emit p->q->minimizedGeometriesChanged(); } PlasmaWindowInterface::PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent) : QObject(parent) , d(new Private(wm, this)) { } PlasmaWindowInterface::~PlasmaWindowInterface() = default; void PlasmaWindowInterface::setAppId(const QString &appId) { d->setAppId(appId); } void PlasmaWindowInterface::setPid(quint32 pid) { d->setPid(pid); } void PlasmaWindowInterface::setTitle(const QString &title) { d->setTitle(title); } void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } void PlasmaWindowInterface::unmap() { d->wm->unmapWindow(this); } QHash PlasmaWindowInterface::minimizedGeometries() const { return d->minimizedGeometries; } void PlasmaWindowInterface::setActive(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, set); } void PlasmaWindowInterface::setFullscreen(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, set); } void PlasmaWindowInterface::setKeepAbove(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, set); } void PlasmaWindowInterface::setKeepBelow(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, set); } void PlasmaWindowInterface::setMaximized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, set); } void PlasmaWindowInterface::setMinimized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, set); } void PlasmaWindowInterface::setOnAllDesktops(bool set) { //the deprecated vd management d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set); if (!d->wm->plasmaVirtualDesktopManagementInterface()) { return; } //the current vd management if (set) { if (d->plasmaVirtualDesktops.isEmpty()) { return; } //leaving everything means on all desktops for (auto desk : plasmaVirtualDesktops()) { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_left(*it, desk.toUtf8().constData()); } } d->plasmaVirtualDesktops.clear(); } else { if (!d->plasmaVirtualDesktops.isEmpty()) { return; } //enters the desktops which are active (usually only one but not a given) for (auto desk : d->wm->plasmaVirtualDesktopManagementInterface()->desktops()) { if (desk->isActive() && !d->plasmaVirtualDesktops.contains(desk->id())) { d->plasmaVirtualDesktops << desk->id(); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_entered(*it, desk->id().toUtf8().constData()); } } } } } void PlasmaWindowInterface::setDemandsAttention(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION, set); } void PlasmaWindowInterface::setCloseable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE, set); } void PlasmaWindowInterface::setFullscreenable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE, set); } void PlasmaWindowInterface::setMaximizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE, set); } void PlasmaWindowInterface::setMinimizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE, set); } void PlasmaWindowInterface::setSkipTaskbar(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set); } void PlasmaWindowInterface::setSkipSwitcher(bool skip) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER, skip); } void PlasmaWindowInterface::setThemedIconName(const QString &iconName) { d->setThemedIconName(iconName); } void PlasmaWindowInterface::setIcon(const QIcon &icon) { d->setIcon(icon); } void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id) { //don't add a desktop we're not sure it exists if (!d->wm->plasmaVirtualDesktopManagementInterface() || d->plasmaVirtualDesktops.contains(id)) { return; } PlasmaVirtualDesktopInterface *desktop = d->wm->plasmaVirtualDesktopManagementInterface()->desktop(id); if (!desktop) { return; } d->plasmaVirtualDesktops << id; //if the desktop dies, remove it from or list connect(desktop, &QObject::destroyed, this, [this, id](){removePlasmaVirtualDesktop(id);}); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_entered(*it, id.toUtf8().constData()); } } void PlasmaWindowInterface::removePlasmaVirtualDesktop(const QString &id) { if (!d->plasmaVirtualDesktops.contains(id)) { return; } d->plasmaVirtualDesktops.removeAll(id); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_left(*it, id.toUtf8().constData()); } //we went on all desktops if (d->plasmaVirtualDesktops.isEmpty()) { setOnAllDesktops(true); } } QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const { return d->plasmaVirtualDesktops; } void PlasmaWindowInterface::setShadeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set); } void PlasmaWindowInterface::setShaded(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, set); } void PlasmaWindowInterface::setMovable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE, set); } void PlasmaWindowInterface::setResizable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE, set); } void PlasmaWindowInterface::setVirtualDesktopChangeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE, set); } void PlasmaWindowInterface::setParentWindow(PlasmaWindowInterface *parentWindow) { d->setParentWindow(parentWindow); } void PlasmaWindowInterface::setGeometry(const QRect &geometry) { d->setGeometry(geometry); } void PlasmaWindowInterface::setApplicationMenuPaths(const QString &serviceName, const QString &objectPath) { d->setApplicationMenuPaths(serviceName, objectPath); } } } diff --git a/src/server/plasmawindowmanagement_interface.h b/src/server/plasmawindowmanagement_interface.h index 5c0743c..65f37fd 100644 --- a/src/server/plasmawindowmanagement_interface.h +++ b/src/server/plasmawindowmanagement_interface.h @@ -1,326 +1,312 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_PLASMA_WINDOW_MANAGEMENT_INTERFACE_H #define WAYLAND_SERVER_PLASMA_WINDOW_MANAGEMENT_INTERFACE_H #include #include #include "global.h" #include "resource.h" class QSize; namespace KWayland { namespace Server { class Display; class PlasmaWindowInterface; class SurfaceInterface; class PlasmaVirtualDesktopManagementInterface; /** * @todo Add documentation */ class KWAYLANDSERVER_EXPORT PlasmaWindowManagementInterface : public Global { Q_OBJECT public: virtual ~PlasmaWindowManagementInterface(); enum class ShowingDesktopState { Disabled, Enabled }; void setShowingDesktopState(ShowingDesktopState state); PlasmaWindowInterface *createWindow(QObject *parent); QList windows() const; /** * Unmaps the @p window previously created with {@link createWindow}. * The window will be unmapped and removed from the list of {@link windows}. * * Unmapping a @p window indicates to the client that it should destroy the * resource created for the window. Once all resources for the @p window are * destroyed, the @p window will get deleted automatically. There is no need * to manually delete the @p window. A manual delete will trigger the unmap * and resource destroy at the same time and can result in protocol errors on * client side if it still accesses the resource before receiving the unmap event. * * @see createWindow * @see windows * @since 5.23 **/ void unmapWindow(PlasmaWindowInterface *window); /** * Associate a PlasmaVirtualDesktopManagementInterface to this window management. * It's necessary to associate one in orderto use the plasma virtual desktop features * of PlasmaWindowInterface, as a window must know what are the deasktops available * @since 5.48 */ void setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager); /** * @returns the PlasmaVirtualDesktopManagementInterface associated to this PlasmaWindowManagementInterface * @since 5.48 */ PlasmaVirtualDesktopManagementInterface *plasmaVirtualDesktopManagementInterface() const; Q_SIGNALS: void requestChangeShowingDesktop(ShowingDesktopState requestedState); private: friend class Display; explicit PlasmaWindowManagementInterface(Display *display, QObject *parent); class Private; Private *d_func() const; }; /** * @todo Add documentation */ class KWAYLANDSERVER_EXPORT PlasmaWindowInterface : public QObject { Q_OBJECT public: virtual ~PlasmaWindowInterface(); void setTitle(const QString &title); void setAppId(const QString &appId); void setPid(quint32 pid); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 52) /** * @deprecated Since 5.52, use addPlasmaVirtualDesktop and removePlasmaVirtualDesktop */ KWAYLANDSERVER_DEPRECATED_VERSION(5, 52, "Use PlasmaWindowManagementInterface::addPlasmaVirtualDesktop(const QString&) and PlasmaWindowManagementInterface::removePlasmaVirtualDesktop(const QString&)") void setVirtualDesktop(quint32 desktop); #endif void setActive(bool set); void setMinimized(bool set); void setMaximized(bool set); void setFullscreen(bool set); void setKeepAbove(bool set); void setKeepBelow(bool set); void setOnAllDesktops(bool set); void setDemandsAttention(bool set); void setCloseable(bool set); void setMinimizeable(bool set); void setMaximizeable(bool set); void setFullscreenable(bool set); void setSkipTaskbar(bool skip); void setSkipSwitcher(bool skip); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 28) /** * @deprecated since 5.28 use setIcon **/ KWAYLANDSERVER_DEPRECATED_VERSION(5, 28, "Use PlasmaWindowManagementInterface::setIcon(const QIcon&)") void setThemedIconName(const QString &iconName); #endif /** * @since 5.22 */ void setShadeable(bool set); /** * @since 5.22 */ void setShaded(bool set); /** * @since 5.22 */ void setMovable(bool set); /** * @since 5.22 */ void setResizable(bool set); /** * FIXME: still relevant with new desktops? * @since 5.22 */ void setVirtualDesktopChangeable(bool set); /** * This method removes the Window and the Client is supposed to release the resource * bound for this Window. Once all resources are released the Window gets deleted. * * Prefer using {@link PlasmaWindowManagementInterface::unmapWindow}. * @see PlasmaWindowManagementInterface::unmapWindow **/ void unmap(); /** * @returns Geometries of the taskbar entries, indicized by the * surface of the panels * @since 5.5 */ QHash minimizedGeometries() const; /** * Sets this PlasmaWindowInterface as a transient window to @p parentWindow. * If @p parentWindow is @c nullptr, the PlasmaWindowInterface is a toplevel * window and does not have a parent window. * @since 5.24 **/ void setParentWindow(PlasmaWindowInterface *parentWindow); /** * Sets the window @p geometry of this PlasmaWindow. * * @param geometry The geometry in absolute coordinates * @since 5.25 **/ void setGeometry(const QRect &geometry); /** * Set the icon of the PlasmaWindowInterface. * * In case the icon has a themed name, only the name is sent to the client. * Otherwise the client is only informed that there is an icon and the client * can request the icon in an asynchronous way by passing a file descriptor * into which the icon will be serialized. * * @param icon The new icon * @since 5.28 **/ void setIcon(const QIcon &icon); /** * Adds a new desktop to this window: a window can be on * an arbitrary subset of virtual desktops. * If it's on none it will be considered on all desktops. * * @since 5.48 */ void addPlasmaVirtualDesktop(const QString &id); /** * Removes a visrtual desktop from a window * * @since 5.48 */ void removePlasmaVirtualDesktop(const QString &id); /** * The ids of all the desktops currently associated with this window. * When a desktop is deleted it will be automatically removed from this list * * @since 5.48 */ QStringList plasmaVirtualDesktops() const; /** * Set the application menu D-BUS service name and object path for the window. - * + * * @since 5.69 */ void setApplicationMenuPaths(const QString &serviceName, const QString &objectPath); Q_SIGNALS: void closeRequested(); /** * @since 5.22 */ void moveRequested(); /** * @since 5.22 */ void resizeRequested(); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 52) /** * @deprecated Since 5.52, use enterPlasmaVirtualDesktopRequested and leavePlasmaVirtualDesktopRequested instead */ KWAYLANDSERVER_DEPRECATED_VERSION(5, 52, "Use PlasmaWindowManagementInterface::enterPlasmaVirtualDesktopRequested(const QString&) and PlasmaWindowManagementInterface::leavePlasmaVirtualDesktopRequested(const QString&)") void virtualDesktopRequested(quint32 desktop); #endif void activeRequested(bool set); void minimizedRequested(bool set); void maximizedRequested(bool set); void fullscreenRequested(bool set); void keepAboveRequested(bool set); void keepBelowRequested(bool set); void demandsAttentionRequested(bool set); void closeableRequested(bool set); void minimizeableRequested(bool set); void maximizeableRequested(bool set); void fullscreenableRequested(bool set); void skipTaskbarRequested(bool set); void skipSwitcherRequested(bool set); QRect minimizedGeometriesChanged(); /** * @since 5.22 */ void shadeableRequested(bool set); /** * @since 5.22 */ void shadedRequested(bool set); /** * @since 5.22 */ void movableRequested(bool set); /** * @since 5.22 */ void resizableRequested(bool set); /** * FIXME: still relevant with new virtual desktops? * @since 5.22 */ void virtualDesktopChangeableRequested(bool set); /** * Emitted when the client wishes this window to enter in a new virtual desktop. * The server will decide whether to consent this request * @since 5.52 */ void enterPlasmaVirtualDesktopRequested(const QString &desktop); /** * Emitted when the client wishes this window to enter in * a new virtual desktop to be created for it. * The server will decide whether to consent this request * @since 5.52 */ void enterNewPlasmaVirtualDesktopRequested(); /** * Emitted when the client wishes to remove this window from a virtual desktop. * The server will decide whether to consent this request * @since 5.52 */ void leavePlasmaVirtualDesktopRequested(const QString &desktop); private: friend class PlasmaWindowManagementInterface; explicit PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent); class Private; const QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::PlasmaWindowManagementInterface::ShowingDesktopState) #endif diff --git a/src/server/pointer_interface.cpp b/src/server/pointer_interface.cpp index 03e5dfd..5647310 100644 --- a/src/server/pointer_interface.cpp +++ b/src/server/pointer_interface.cpp @@ -1,486 +1,472 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointer_interface.h" #include "pointer_interface_p.h" #include "pointerconstraints_interface.h" #include "pointergestures_interface_p.h" #include "resource_p.h" #include "relativepointer_interface_p.h" #include "seat_interface.h" #include "display.h" #include "subcompositor_interface.h" #include "surface_interface.h" #include "datadevice_interface.h" // Wayland #include namespace KWayland { namespace Server { class Cursor::Private { public: Private(Cursor *q, PointerInterface *pointer); PointerInterface *pointer; quint32 enteredSerial = 0; QPoint hotspot; QPointer surface; void update(const QPointer &surface, quint32 serial, const QPoint &hotspot); private: Cursor *q; }; PointerInterface::Private::Private(SeatInterface *parent, wl_resource *parentResource, PointerInterface *q) : Resource::Private(q, parent, parentResource, &wl_pointer_interface, &s_interface) , seat(parent) { } void PointerInterface::Private::setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot) { if (!cursor) { Q_Q(PointerInterface); cursor = new Cursor(q); cursor->d->update(QPointer(surface), serial, hotspot); QObject::connect(cursor, &Cursor::changed, q, &PointerInterface::cursorChanged); emit q->cursorChanged(); } else { cursor->d->update(QPointer(surface), serial, hotspot); } } void PointerInterface::Private::sendLeave(SurfaceInterface *surface, quint32 serial) { if (!surface) { return; } if (resource && surface->resource()) { wl_pointer_send_leave(resource, serial, surface->resource()); } } void PointerInterface::Private::registerRelativePointer(RelativePointerInterface *relativePointer) { relativePointers << relativePointer; QObject::connect(relativePointer, &QObject::destroyed, q, [this, relativePointer] { relativePointers.removeOne(relativePointer); } ); } void PointerInterface::Private::registerSwipeGesture(PointerSwipeGestureInterface *gesture) { swipeGestures << gesture; QObject::connect(gesture, &QObject::destroyed, q, [this, gesture] { swipeGestures.removeOne(gesture); } ); } void PointerInterface::Private::registerPinchGesture(PointerPinchGestureInterface *gesture) { pinchGestures << gesture; QObject::connect(gesture, &QObject::destroyed, q, [this, gesture] { pinchGestures.removeOne(gesture); } ); } namespace { static QPointF surfacePosition(SurfaceInterface *surface) { if (surface && surface->subSurface()) { return surface->subSurface()->position() + surfacePosition(surface->subSurface()->parentSurface().data()); } return QPointF(); } } void PointerInterface::Private::sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial) { if (!surface || !surface->resource()) { return; } const QPointF adjustedPos = parentSurfacePosition - surfacePosition(surface); wl_pointer_send_enter(resource, serial, surface->resource(), wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); } void PointerInterface::Private::startSwipeGesture(quint32 serial, quint32 fingerCount) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->start(serial, fingerCount); } } void PointerInterface::Private::updateSwipeGesture(const QSizeF &delta) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->update(delta); } } void PointerInterface::Private::endSwipeGesture(quint32 serial) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->end(serial); } } void PointerInterface::Private::cancelSwipeGesture(quint32 serial) { if (swipeGestures.isEmpty()) { return; } for (auto it = swipeGestures.constBegin(), end = swipeGestures.constEnd(); it != end; it++) { (*it)->cancel(serial); } } void PointerInterface::Private::startPinchGesture(quint32 serial, quint32 fingerCount) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->start(serial, fingerCount); } } void PointerInterface::Private::updatePinchGesture(const QSizeF &delta, qreal scale, qreal rotation) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->update(delta, scale, rotation); } } void PointerInterface::Private::endPinchGesture(quint32 serial) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->end(serial); } } void PointerInterface::Private::cancelPinchGesture(quint32 serial) { if (pinchGestures.isEmpty()) { return; } for (auto it = pinchGestures.constBegin(), end = pinchGestures.constEnd(); it != end; it++) { (*it)->cancel(serial); } } void PointerInterface::Private::sendFrame() { if (!resource || wl_resource_get_version(resource) < WL_POINTER_FRAME_SINCE_VERSION) { return; } wl_pointer_send_frame(resource); } #ifndef K_DOXYGEN const struct wl_pointer_interface PointerInterface::Private::s_interface = { setCursorCallback, resourceDestroyedCallback }; #endif PointerInterface::PointerInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { // TODO: handle touch connect(parent, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); if (!d->focusedSurface || !d->resource) { return; } if (d->seat->isDragPointer()) { const auto *originSurface = d->seat->dragSource()->origin(); const bool proxyRemoteFocused = originSurface->dataProxy() && originSurface == d->focusedSurface; if (!proxyRemoteFocused) { // handled by DataDevice return; } } if (!d->focusedSurface->lockedPointer().isNull() && d->focusedSurface->lockedPointer()->isLocked()) { return; } const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); auto targetSurface = d->focusedSurface->inputSurfaceAt(pos); if (!targetSurface) { targetSurface = d->focusedSurface; } if (targetSurface != d->focusedChildSurface.data()) { const quint32 serial = d->seat->display()->nextSerial(); d->sendLeave(d->focusedChildSurface.data(), serial); d->focusedChildSurface = QPointer(targetSurface); d->sendEnter(targetSurface, pos, serial); d->sendFrame(); d->client->flush(); } else { const QPointF adjustedPos = pos - surfacePosition(d->focusedChildSurface); wl_pointer_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); d->sendFrame(); } }); } PointerInterface::~PointerInterface() = default; void PointerInterface::setFocusedSurface(SurfaceInterface *surface, quint32 serial) { Q_D(); d->sendLeave(d->focusedChildSurface.data(), serial); disconnect(d->destroyConnection); if (!surface) { d->focusedSurface = nullptr; d->focusedChildSurface.clear(); return; } d->focusedSurface = surface; d->destroyConnection = connect(d->focusedSurface, &Resource::aboutToBeUnbound, this, [this] { Q_D(); d->sendLeave(d->focusedChildSurface.data(), d->global->display()->nextSerial()); d->sendFrame(); d->focusedSurface = nullptr; d->focusedChildSurface.clear(); } ); const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); d->focusedChildSurface = QPointer(d->focusedSurface->inputSurfaceAt(pos)); if (!d->focusedChildSurface) { d->focusedChildSurface = QPointer(d->focusedSurface); } d->sendEnter(d->focusedChildSurface.data(), pos, serial); d->client->flush(); } void PointerInterface::buttonPressed(quint32 button, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_button(d->resource, serial, d->seat->timestamp(), button, WL_POINTER_BUTTON_STATE_PRESSED); d->sendFrame(); } void PointerInterface::buttonReleased(quint32 button, quint32 serial) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_button(d->resource, serial, d->seat->timestamp(), button, WL_POINTER_BUTTON_STATE_RELEASED); d->sendFrame(); } void PointerInterface::axis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } const quint32 version = wl_resource_get_version(d->resource); const auto wlOrientation = (orientation == Qt::Vertical) ? WL_POINTER_AXIS_VERTICAL_SCROLL : WL_POINTER_AXIS_HORIZONTAL_SCROLL; if (source != PointerAxisSource::Unknown && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) { wl_pointer_axis_source wlSource; switch (source) { case PointerAxisSource::Wheel: wlSource = WL_POINTER_AXIS_SOURCE_WHEEL; break; case PointerAxisSource::Finger: wlSource = WL_POINTER_AXIS_SOURCE_FINGER; break; case PointerAxisSource::Continuous: wlSource = WL_POINTER_AXIS_SOURCE_CONTINUOUS; break; case PointerAxisSource::WheelTilt: wlSource = WL_POINTER_AXIS_SOURCE_WHEEL_TILT; break; default: Q_UNREACHABLE(); break; } wl_pointer_send_axis_source(d->resource, wlSource); } if (delta != 0.0) { if (discreteDelta && version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) { wl_pointer_send_axis_discrete(d->resource, wlOrientation, discreteDelta); } wl_pointer_send_axis(d->resource, d->seat->timestamp(), wlOrientation, wl_fixed_from_double(delta)); } else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) { wl_pointer_send_axis_stop(d->resource, d->seat->timestamp(), wlOrientation); } d->sendFrame(); } void PointerInterface::axis(Qt::Orientation orientation, quint32 delta) { Q_D(); Q_ASSERT(d->focusedSurface); if (!d->resource) { return; } wl_pointer_send_axis(d->resource, d->seat->timestamp(), (orientation == Qt::Vertical) ? WL_POINTER_AXIS_VERTICAL_SCROLL : WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_int(delta)); d->sendFrame(); } void PointerInterface::Private::setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) { auto p = cast(resource); Q_ASSERT(p->client->client() == client); p->setCursor(serial, SurfaceInterface::get(surface), QPoint(hotspot_x, hotspot_y)); } Cursor *PointerInterface::cursor() const { Q_D(); return d->cursor; } void PointerInterface::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { Q_D(); if (d->relativePointers.isEmpty()) { return; } for (auto it = d->relativePointers.constBegin(), end = d->relativePointers.constEnd(); it != end; it++) { (*it)->relativeMotion(delta, deltaNonAccelerated, microseconds); } d->sendFrame(); } PointerInterface::Private *PointerInterface::d_func() const { return reinterpret_cast(d.data()); } PointerInterface *PointerInterface::get(wl_resource *native) { return Private::get(native); } Cursor::Private::Private(Cursor *q, PointerInterface *pointer) : pointer(pointer) , q(q) { } void Cursor::Private::update(const QPointer< SurfaceInterface > &s, quint32 serial, const QPoint &p) { bool emitChanged = false; if (enteredSerial != serial) { enteredSerial = serial; emitChanged = true; emit q->enteredSerialChanged(); } if (hotspot != p) { hotspot = p; emitChanged = true; emit q->hotspotChanged(); } if (surface != s) { if (!surface.isNull()) { QObject::disconnect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } surface = s; if (!surface.isNull()) { QObject::connect(surface.data(), &SurfaceInterface::damaged, q, &Cursor::changed); } emitChanged = true; emit q->surfaceChanged(); } if (emitChanged) { emit q->changed(); } } Cursor::Cursor(PointerInterface *parent) : QObject(parent) , d(new Private(this, parent)) { } Cursor::~Cursor() = default; quint32 Cursor::enteredSerial() const { return d->enteredSerial; } QPoint Cursor::hotspot() const { return d->hotspot; } PointerInterface *Cursor::pointer() const { return d->pointer; } QPointer< SurfaceInterface > Cursor::surface() const { return d->surface; } } } diff --git a/src/server/pointer_interface.h b/src/server/pointer_interface.h index 841c097..510d3b9 100644 --- a/src/server/pointer_interface.h +++ b/src/server/pointer_interface.h @@ -1,134 +1,120 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_POINTER_INTERFACE_H #define WAYLAND_SERVER_POINTER_INTERFACE_H #include #include "resource.h" namespace KWayland { namespace Server { class Cursor; class PointerGesturesUnstableV1Interface; class RelativePointerManagerUnstableV1Interface; class SeatInterface; class SurfaceInterface; enum class PointerAxisSource; /** * @brief Resource for the wl_pointer interface. * * @see SeatInterface **/ class KWAYLANDSERVER_EXPORT PointerInterface : public Resource { Q_OBJECT public: virtual ~PointerInterface(); /** * @returns the focused SurfaceInterface on this pointer resource, if any. **/ SurfaceInterface *focusedSurface() const; /** * The Cursor set on this PointerInterface. Might be @c null. * @since 5.3 **/ Cursor *cursor() const; /** * @returns The PointerInterface for the @p native resource. * @since 5.28 **/ static PointerInterface *get(wl_resource *native); Q_SIGNALS: /** * Signal emitted whenever the Cursor changes. **/ void cursorChanged(); private: void setFocusedSurface(SurfaceInterface *surface, quint32 serial); void buttonPressed(quint32 button, quint32 serial); void buttonReleased(quint32 button, quint32 serial); void axis(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source); void axis(Qt::Orientation orientation, quint32 delta); void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); friend class SeatInterface; friend class RelativePointerManagerUnstableV1Interface; friend class PointerGesturesUnstableV1Interface; explicit PointerInterface(SeatInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; /** * @brief Class encapsulating a Cursor image. * * @since 5.3 **/ class KWAYLANDSERVER_EXPORT Cursor : public QObject { Q_OBJECT public: virtual ~Cursor(); /** * The hotspot of the cursor image in surface-relative coordinates. **/ QPoint hotspot() const; /** * The entered serial when the Cursor got set. **/ quint32 enteredSerial() const; /** * The PointerInterface this Cursor belongs to. **/ PointerInterface *pointer() const; /** * The SurfaceInterface for the image content of the Cursor. **/ QPointer surface() const; Q_SIGNALS: void hotspotChanged(); void enteredSerialChanged(); void surfaceChanged(); void changed(); private: friend class PointerInterface; Cursor(PointerInterface *parent); class Private; const QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::PointerInterface*) #endif diff --git a/src/server/pointer_interface_p.h b/src/server/pointer_interface_p.h index c964e36..5f64e8e 100644 --- a/src/server/pointer_interface_p.h +++ b/src/server/pointer_interface_p.h @@ -1,83 +1,69 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_POINTER_INTERFACE_P_H #define WAYLAND_SERVER_POINTER_INTERFACE_P_H #include "pointer_interface.h" #include "resource_p.h" #include #include namespace KWayland { namespace Server { class PointerPinchGestureInterface; class PointerSwipeGestureInterface; class RelativePointerInterface; class PointerInterface::Private : public Resource::Private { public: Private(SeatInterface *parent, wl_resource *parentResource, PointerInterface *q); SeatInterface *seat; SurfaceInterface *focusedSurface = nullptr; QPointer focusedChildSurface; QMetaObject::Connection destroyConnection; Cursor *cursor = nullptr; QVector relativePointers; QVector swipeGestures; QVector pinchGestures; void sendLeave(SurfaceInterface *surface, quint32 serial); void sendEnter(SurfaceInterface *surface, const QPointF &parentSurfacePosition, quint32 serial); void sendFrame(); void registerRelativePointer(RelativePointerInterface *relativePointer); void registerSwipeGesture(PointerSwipeGestureInterface *gesture); void registerPinchGesture(PointerPinchGestureInterface *gesture); void startSwipeGesture(quint32 serial, quint32 fingerCount); void updateSwipeGesture(const QSizeF &delta); void endSwipeGesture(quint32 serial); void cancelSwipeGesture(quint32 serial); void startPinchGesture(quint32 serial, quint32 fingerCount); void updatePinchGesture(const QSizeF &delta, qreal scale, qreal rotation); void endPinchGesture(quint32 serial); void cancelPinchGesture(quint32 serial); private: PointerInterface *q_func() { return reinterpret_cast(q); } void setCursor(quint32 serial, SurfaceInterface *surface, const QPoint &hotspot); // interface static void setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y); static const struct wl_pointer_interface s_interface; }; } } #endif diff --git a/src/server/pointerconstraints_interface.cpp b/src/server/pointerconstraints_interface.cpp index a4b89f5..b96499b 100644 --- a/src/server/pointerconstraints_interface.cpp +++ b/src/server/pointerconstraints_interface.cpp @@ -1,214 +1,200 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointerconstraints_interface_p.h" #include namespace KWayland { namespace Server { PointerConstraintsInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, PointerConstraintsInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } PointerConstraintsInterface::PointerConstraintsInterface(Private *d, QObject *parent) : Global(d, parent) { } PointerConstraintsInterface::~PointerConstraintsInterface() = default; PointerConstraintsInterfaceVersion PointerConstraintsInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } PointerConstraintsInterface::Private *PointerConstraintsInterface::d_func() const { return reinterpret_cast(d.data()); } LockedPointerInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, LockedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) , interfaceVersion(interfaceVersion) { } LockedPointerInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } void LockedPointerInterface::Private::commit() { if (regionIsSet) { region = pendingRegion; pendingRegion = QRegion(); regionIsSet = false; emit q_func()->regionChanged(); } if (hintIsSet) { hint = pendingHint; hintIsSet = false; emit q_func()->cursorPositionHintChanged(); } } LockedPointerInterface::LockedPointerInterface(Private *p, QObject *parent) : Resource(p, parent) { connect(this, &LockedPointerInterface::unbound, this, [this]() { setLocked(false); }); } LockedPointerInterface::~LockedPointerInterface() = default; PointerConstraintsInterfaceVersion LockedPointerInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } LockedPointerInterface::LifeTime LockedPointerInterface::lifeTime() const { Q_D(); return d->lifeTime; } QRegion LockedPointerInterface::region() const { Q_D(); return d->region; } QPointF LockedPointerInterface::cursorPositionHint() const { Q_D(); return d->hint; } bool LockedPointerInterface::isLocked() const { Q_D(); return d->locked; } void LockedPointerInterface::setLocked(bool locked) { Q_D(); if (locked == d->locked) { return; } if (!locked) { d->hint = QPointF(-1., -1.); } d->locked = locked; d->updateLocked(); emit lockedChanged(); } LockedPointerInterface::Private *LockedPointerInterface::d_func() const { return reinterpret_cast(d.data()); } ConfinedPointerInterface::Private::Private(PointerConstraintsInterfaceVersion interfaceVersion, ConfinedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) , interfaceVersion(interfaceVersion) { } ConfinedPointerInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } void ConfinedPointerInterface::Private::commit() { if (!regionIsSet) { return; } region = pendingRegion; pendingRegion = QRegion(); regionIsSet = false; emit q_func()->regionChanged(); } ConfinedPointerInterface::ConfinedPointerInterface(Private *p, QObject *parent) : Resource(p, parent) { connect(this, &ConfinedPointerInterface::unbound, this, [this]() { setConfined(false); }); } ConfinedPointerInterface::~ConfinedPointerInterface() = default; PointerConstraintsInterfaceVersion ConfinedPointerInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } ConfinedPointerInterface::LifeTime ConfinedPointerInterface::lifeTime() const { Q_D(); return d->lifeTime; } QRegion ConfinedPointerInterface::region() const { Q_D(); return d->region; } bool ConfinedPointerInterface::isConfined() const { Q_D(); return d->confined; } void ConfinedPointerInterface::setConfined(bool confined) { Q_D(); if (confined == d->confined) { return; } d->confined = confined; d->updateConfined(); emit confinedChanged(); } ConfinedPointerInterface::Private *ConfinedPointerInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/pointerconstraints_interface.h b/src/server/pointerconstraints_interface.h index 32f8752..e61ce30 100644 --- a/src/server/pointerconstraints_interface.h +++ b/src/server/pointerconstraints_interface.h @@ -1,296 +1,282 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_POINTERCONSTRAINTS_INTERFACE_H #define KWAYLAND_SERVER_POINTERCONSTRAINTS_INTERFACE_H #include "global.h" #include "resource.h" #include #include namespace KWayland { namespace Server { class Display; class SurfaceInterface; /** * Enum describing the interface versions the PointerConstraintsInterface can support. * * @since 5.29 **/ enum class PointerConstraintsInterfaceVersion { /** * zwp_pointer_constraints_v1 **/ UnstableV1 }; /** * Manager object to create pointer constraints. * * To create this manager use {@link Display::createPointerConstraints} * * @see ConfinedPointerInterface * @see LockedPointerInterface * @see Display::createPointerConstraints * @since 5.29 **/ class KWAYLANDSERVER_EXPORT PointerConstraintsInterface : public Global { Q_OBJECT public: virtual ~PointerConstraintsInterface(); /** * @returns The interface version used by this PointerConstraintsInterface **/ PointerConstraintsInterfaceVersion interfaceVersion() const; protected: class Private; explicit PointerConstraintsInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; /** * The LockedPointerInterface lets the client request to disable movements of * the virtual pointer (i.e. the cursor), effectively locking the pointer * to a position. * * It is up to the compositor whether the lock gets activated. * To activate it needs to use {@link LockedPointerInterface::setLocked}. * The compositor needs to ensure that the SurfaceInterface has pointer focus * and that the pointer is inside the {@link LockedPointerInterface::region} when * it activates the lock. * * While the lock is active the PointerInterface does no longer emit pointer motion * events, but still emits relative pointer motion events. * * @since 5.29 **/ class KWAYLANDSERVER_EXPORT LockedPointerInterface : public Resource { Q_OBJECT public: virtual ~LockedPointerInterface(); /** * @returns The interface version used by this LockedPointerInterface **/ PointerConstraintsInterfaceVersion interfaceVersion() const; enum class LifeTime { OneShot, Persistent }; LifeTime lifeTime() const; /** * The intersection of this region and the input region of the SurfaceInterface is used * to determine where the pointer must be in order for the lock to activate. * It is up to the compositor whether to warp the pointer or require some kind of * user interaction for the lock to activate. * * If the region is empty the SurfaceInterface input region is used. * * @see regionChanged * @see SurfaceInterface::input **/ QRegion region() const; /** * Indicates where the mouse cursor should be positioned after it has been unlocked again. * The compositor can warp the cursor at this moment to the position. For that it * will not emit any relative motion events. The hint is relative to the top-left * corner of the surface the lock was applied to. Only non-negative x and y values * are allowed. Otherwise the hint is invalid and should be ignored by the compositor. * * In case the client never set the hint, an invalid one will be returned. * * This function should be called when the compositor decides to break the lock or the * client unbinds the resource. To set the position in this case the compositor should * call this function when the aboutToBeUnbound signal has been emitted. * * @see cursorPositionHintChanged * @since 5.49 **/ QPointF cursorPositionHint() const; /** * Whether the Compositor set this pointer lock to be active. * @see setLocked * @see lockedChanged **/ bool isLocked() const; /** * Activates or deactivates the lock. * * A pointer lock can only be activated if the SurfaceInterface * this LockedPointerInterface was created for has pointer focus * and the pointer is inside the {@link region}. * * Unlocking resets the cursor position hint. * * @param locked Whether the lock should be active * @see isLocked * @see lockedChanged **/ void setLocked(bool locked); Q_SIGNALS: /** * Emitted whenever the region changes. * This happens when the parent SurfaceInterface gets committed * @see region **/ void regionChanged(); /** * Emitted whenever the cursor position hint changes. * This happens when the parent SurfaceInterface gets committed * @see cursorPositionHint * @since 5.49 **/ void cursorPositionHintChanged(); /** * Emitted whenever the {@link isLocked} state changes. * @see isLocked * @see setLocked **/ void lockedChanged(); protected: class Private; explicit LockedPointerInterface(Private *p, QObject *parent = nullptr); private: Private *d_func() const; friend class SurfaceInterface; }; /** * * The ConfinedPointerInterface gets installed on a SurfaceInterface. * The confinement indicates that the SurfaceInterface wants to confine the * pointer to a region of the SurfaceInterface. * * It is up to the compositor whether the confinement gets activated. * To activate it needs to use {@link ConfinedPointerInterface::setConfined}. * The compositor needs to ensure that the SurfaceInterface has pointer focus * and that the pointer is inside the {@link ConfinedPointerInterface::region} when * it activates the confinement. * * From client side the confinement gets deactivated by destroying the ConfinedPointerInterface. * From compositor side the confinement can be deactivated by setting * {@link ConfinedPointerInterface::setConfined} to @c false. * * @since 5.29 **/ class KWAYLANDSERVER_EXPORT ConfinedPointerInterface : public Resource { Q_OBJECT public: virtual ~ConfinedPointerInterface(); /** * @returns The interface version used by this ConfinedPointerInterface **/ PointerConstraintsInterfaceVersion interfaceVersion() const; enum class LifeTime { OneShot, Persistent }; LifeTime lifeTime() const; /** * The intersection of this region and the input region of the SurfaceInterface is used * to determine where the pointer must be in order for the confinement to activate. * It is up to the compositor whether to warp the pointer or require some kind of * user interaction for the confinement to activate. * * If the region is empty the SurfaceInterface input region is used. * * @see regionChanged * @see SurfaceInterface::input **/ QRegion region() const; /** * Whether the Compositor set this pointer confinement to be active. * @see setConfined * @see confinedChanged **/ bool isConfined() const; /** * Activates or deactivates the confinement. * * A pointer confinement can only be activated if the SurfaceInterface * this ConfinedPointerInterface was created for has pointer focus * and the pointer is inside the {@link region}. * * @param confined Whether the confinement should be active * @see isConfined * @see confinedChanged **/ void setConfined(bool confined); Q_SIGNALS: /** * Emitted whenever the region changes. * This happens when the parent SurfaceInterface gets committed * @see region **/ void regionChanged(); /** * Emitted whenever the {@link isConfined} state changes. * @see isConfined * @see setConfined **/ void confinedChanged(); protected: class Private; explicit ConfinedPointerInterface(Private *p, QObject *parent = nullptr); private: Private *d_func() const; friend class SurfaceInterface; }; } } #endif diff --git a/src/server/pointerconstraints_interface_p.h b/src/server/pointerconstraints_interface_p.h index eb25ee4..703384b 100644 --- a/src/server/pointerconstraints_interface_p.h +++ b/src/server/pointerconstraints_interface_p.h @@ -1,140 +1,126 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_POINTERCONSTRAINTS_INTERFACE_P_H #define KWAYLAND_SERVER_POINTERCONSTRAINTS_INTERFACE_P_H #include "pointerconstraints_interface.h" #include "global_p.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class PointerConstraintsInterface::Private : public Global::Private { public: PointerConstraintsInterfaceVersion interfaceVersion; protected: Private(PointerConstraintsInterfaceVersion interfaceVersion, PointerConstraintsInterface *q, Display *d, const wl_interface *interface, quint32 version); PointerConstraintsInterface *q; }; class PointerConstraintsUnstableV1Interface : public PointerConstraintsInterface { Q_OBJECT public: explicit PointerConstraintsUnstableV1Interface(Display *display, QObject *parent = nullptr); virtual ~PointerConstraintsUnstableV1Interface(); private: class Private; }; class LockedPointerInterface::Private : public Resource::Private { public: ~Private(); virtual void updateLocked() = 0; void commit(); PointerConstraintsInterfaceVersion interfaceVersion; LifeTime lifeTime; QRegion region; bool locked = false; QPointF hint = QPointF(-1., -1.); protected: Private(PointerConstraintsInterfaceVersion interfaceVersion, LockedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); QRegion pendingRegion; bool regionIsSet = false; QPointF pendingHint; bool hintIsSet = false; private: LockedPointerInterface *q_func() { return reinterpret_cast(q); } }; class LockedPointerUnstableV1Interface : public LockedPointerInterface { Q_OBJECT public: explicit LockedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource); virtual ~LockedPointerUnstableV1Interface(); private: class Private; Private *d_func() const; friend class PointerConstraintsUnstableV1Interface; }; class ConfinedPointerInterface::Private : public Resource::Private { public: ~Private(); virtual void updateConfined() = 0; void commit(); PointerConstraintsInterfaceVersion interfaceVersion; LifeTime lifeTime; QRegion region; bool confined = false; protected: Private(PointerConstraintsInterfaceVersion interfaceVersion, ConfinedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); QRegion pendingRegion; bool regionIsSet = false; private: ConfinedPointerInterface *q_func() { return reinterpret_cast(q); } }; class ConfinedPointerUnstableV1Interface : public ConfinedPointerInterface { Q_OBJECT public: explicit ConfinedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource); virtual ~ConfinedPointerUnstableV1Interface(); private: class Private; Private *d_func() const; friend class PointerConstraintsUnstableV1Interface; }; } } #endif diff --git a/src/server/pointerconstraints_interface_v1.cpp b/src/server/pointerconstraints_interface_v1.cpp index 1eabfb1..559ad11 100644 --- a/src/server/pointerconstraints_interface_v1.cpp +++ b/src/server/pointerconstraints_interface_v1.cpp @@ -1,285 +1,271 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointerconstraints_interface_p.h" #include "display.h" #include "pointer_interface.h" #include "region_interface.h" #include "surface_interface_p.h" #include namespace KWayland { namespace Server { class PointerConstraintsUnstableV1Interface::Private : public PointerConstraintsInterface::Private { public: Private(PointerConstraintsUnstableV1Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; template void createConstraint(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void lockPointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * pointer, wl_resource * region, uint32_t lifetime); static void confinePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * pointer, wl_resource * region, uint32_t lifetime); PointerConstraintsUnstableV1Interface *q; static const struct zwp_pointer_constraints_v1_interface s_interface; static const quint32 s_version; }; class LockedPointerUnstableV1Interface::Private : public LockedPointerInterface::Private { public: Private(LockedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource); ~Private(); void updateLocked() override; private: static void setCursorPositionHintCallback(wl_client *client, wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y); static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region); LockedPointerUnstableV1Interface *q_func() { return reinterpret_cast(q); } static const struct zwp_locked_pointer_v1_interface s_interface; }; const quint32 PointerConstraintsUnstableV1Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zwp_pointer_constraints_v1_interface PointerConstraintsUnstableV1Interface::Private::s_interface = { destroyCallback, lockPointerCallback, confinePointerCallback }; #endif void PointerConstraintsUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } template void PointerConstraintsUnstableV1Interface::Private::createConstraint(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime) { auto s = SurfaceInterface::get(surface); auto p = PointerInterface::get(pointer); if (!s || !p) { // send error? return; } if (!s->lockedPointer().isNull() || !s->confinedPointer().isNull()) { wl_resource_post_error(s->resource(), ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, "Surface already constrained"); return; } auto constraint = new T(q, resource); switch (lifetime) { case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT: constraint->d_func()->lifeTime = T::LifeTime::Persistent; break; case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: // fall through default: constraint->d_func()->lifeTime = T::LifeTime::OneShot; break; } auto r = RegionInterface::get(region); constraint->d_func()->region = r ? r->region() : QRegion(); constraint->d_func()->create(display->getConnection(client), version, id); s->d_func()->installPointerConstraint(constraint); } void PointerConstraintsUnstableV1Interface::Private::lockPointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime) { cast(resource)->createConstraint(client, resource, id, surface, pointer, region, lifetime); } void PointerConstraintsUnstableV1Interface::Private::confinePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *pointer, wl_resource *region, uint32_t lifetime) { cast(resource)->createConstraint(client, resource, id, surface, pointer, region, lifetime); } PointerConstraintsUnstableV1Interface::Private::Private(PointerConstraintsUnstableV1Interface *q, Display *d) : PointerConstraintsInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, d, &zwp_pointer_constraints_v1_interface, s_version) , q(q) { } void PointerConstraintsUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zwp_pointer_constraints_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void PointerConstraintsUnstableV1Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } PointerConstraintsUnstableV1Interface::PointerConstraintsUnstableV1Interface(Display *display, QObject *parent) : PointerConstraintsInterface(new Private(this, display), parent) { } PointerConstraintsUnstableV1Interface::~PointerConstraintsUnstableV1Interface() = default; #ifndef K_DOXYGEN const struct zwp_locked_pointer_v1_interface LockedPointerUnstableV1Interface::Private::s_interface = { resourceDestroyedCallback, setCursorPositionHintCallback, setRegionCallback }; #endif void LockedPointerUnstableV1Interface::Private::setCursorPositionHintCallback(wl_client *client, wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y) { Q_UNUSED(client) auto p = cast(resource); p->pendingHint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); p->hintIsSet = true; } void LockedPointerUnstableV1Interface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region) { Q_UNUSED(client) auto p = cast(resource); auto r = RegionInterface::get(region); p->pendingRegion = r ? r->region() : QRegion(); p->regionIsSet = true; } void LockedPointerUnstableV1Interface::Private::updateLocked() { if (!resource) { return; } if (locked) { zwp_locked_pointer_v1_send_locked(resource); } else { zwp_locked_pointer_v1_send_unlocked(resource); } } LockedPointerUnstableV1Interface::Private::Private(LockedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource) : LockedPointerInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, c, parentResource, &zwp_locked_pointer_v1_interface, &s_interface) { } LockedPointerUnstableV1Interface::LockedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource) : LockedPointerInterface(new Private(this, parent, parentResource)) { } LockedPointerUnstableV1Interface::Private::~Private() = default; LockedPointerUnstableV1Interface::~LockedPointerUnstableV1Interface() = default; LockedPointerUnstableV1Interface::Private *LockedPointerUnstableV1Interface::d_func() const { return reinterpret_cast(d.data()); } class ConfinedPointerUnstableV1Interface::Private : public ConfinedPointerInterface::Private { public: Private(ConfinedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource); ~Private(); void updateConfined() override; private: static void setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region); ConfinedPointerUnstableV1Interface *q_func() { return reinterpret_cast(q); } static const struct zwp_confined_pointer_v1_interface s_interface; }; #ifndef K_DOXYGEN const struct zwp_confined_pointer_v1_interface ConfinedPointerUnstableV1Interface::Private::s_interface = { resourceDestroyedCallback, setRegionCallback }; #endif void ConfinedPointerUnstableV1Interface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { Q_UNUSED(client) auto p = cast(resource); auto r = RegionInterface::get(region); p->pendingRegion = r ? r->region() : QRegion(); p->regionIsSet = true; } ConfinedPointerUnstableV1Interface::Private::Private(ConfinedPointerUnstableV1Interface *q, PointerConstraintsUnstableV1Interface *c, wl_resource *parentResource) : ConfinedPointerInterface::Private(PointerConstraintsInterfaceVersion::UnstableV1, q, c, parentResource, &zwp_confined_pointer_v1_interface, &s_interface) { } ConfinedPointerUnstableV1Interface::ConfinedPointerUnstableV1Interface(PointerConstraintsUnstableV1Interface *parent, wl_resource *parentResource) : ConfinedPointerInterface(new Private(this, parent, parentResource)) { } ConfinedPointerUnstableV1Interface::Private::~Private() = default; ConfinedPointerUnstableV1Interface::~ConfinedPointerUnstableV1Interface() = default; void ConfinedPointerUnstableV1Interface::Private::updateConfined() { if (!resource) { return; } if (confined) { zwp_confined_pointer_v1_send_confined(resource); } else { zwp_confined_pointer_v1_send_unconfined(resource); } } ConfinedPointerUnstableV1Interface::Private *ConfinedPointerUnstableV1Interface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/pointergestures_interface.cpp b/src/server/pointergestures_interface.cpp index 3b05da5..fbe6a7e 100644 --- a/src/server/pointergestures_interface.cpp +++ b/src/server/pointergestures_interface.cpp @@ -1,105 +1,91 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointergestures_interface_p.h" namespace KWayland { namespace Server { PointerGesturesInterface::Private::Private(PointerGesturesInterfaceVersion interfaceVersion, PointerGesturesInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } PointerGesturesInterface::PointerGesturesInterface(Private *d, QObject *parent) : Global(d, parent) { } PointerGesturesInterface::~PointerGesturesInterface() = default; PointerGesturesInterfaceVersion PointerGesturesInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } PointerGesturesInterface::Private *PointerGesturesInterface::d_func() const { return reinterpret_cast(d.data()); } PointerSwipeGestureInterface::Private::Private(PointerSwipeGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer) : Resource::Private(q, c, parentResource, interface, implementation) , pointer(pointer) { } PointerSwipeGestureInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } PointerSwipeGestureInterface::PointerSwipeGestureInterface(Private *p, QObject *parent) : Resource(p, parent) { } PointerSwipeGestureInterface::~PointerSwipeGestureInterface() = default; PointerSwipeGestureInterface::Private *PointerSwipeGestureInterface::d_func() const { return reinterpret_cast(d.data()); } PointerPinchGestureInterface::Private::Private(PointerPinchGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer) : Resource::Private(q, c, parentResource, interface, implementation) , pointer(pointer) { } PointerPinchGestureInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } PointerPinchGestureInterface::PointerPinchGestureInterface(Private *p, QObject *parent) : Resource(p, parent) { } PointerPinchGestureInterface::~PointerPinchGestureInterface() = default; PointerPinchGestureInterface::Private *PointerPinchGestureInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/pointergestures_interface.h b/src/server/pointergestures_interface.h index e5f9d09..6a8dcf5 100644 --- a/src/server/pointergestures_interface.h +++ b/src/server/pointergestures_interface.h @@ -1,75 +1,61 @@ -/**************************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_POINTERGESTURES_INTERFACE_H #define KWAYLAND_SERVER_POINTERGESTURES_INTERFACE_H #include "global.h" #include namespace KWayland { namespace Server { /** * Enum describing the interface versions the PointerGesturesInterface can support. * * @since 5.29 **/ enum class PointerGesturesInterfaceVersion { /** * zwp_pointer_gestures_v1, zwp_pointer_gesture_swipe_v1 and zwp_pointer_gesture_pinch_v1 **/ UnstableV1 }; /** * Manager object for the PointerGestures. * * Creates and manages pointer swipe and pointer pinch gestures which are * reported to the SeatInterface. * * @see Display::createPointerGestures * @since 5.29 **/ class KWAYLANDSERVER_EXPORT PointerGesturesInterface : public Global { Q_OBJECT public: virtual ~PointerGesturesInterface(); /** * @returns The interface version used by this PointerGesturesInterface **/ PointerGesturesInterfaceVersion interfaceVersion() const; protected: class Private; explicit PointerGesturesInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; } } #endif diff --git a/src/server/pointergestures_interface_p.h b/src/server/pointergestures_interface_p.h index 264d111..2d99bb0 100644 --- a/src/server/pointergestures_interface_p.h +++ b/src/server/pointergestures_interface_p.h @@ -1,165 +1,151 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_POINTERGESTURES_INTERFACE_P_H #define KWAYLAND_SERVER_POINTERGESTURES_INTERFACE_P_H #include "pointergestures_interface.h" #include "resource_p.h" #include "global_p.h" namespace KWayland { namespace Server { class PointerInterface; class PointerGesturesInterface::Private : public Global::Private { public: PointerGesturesInterfaceVersion interfaceVersion; protected: Private(PointerGesturesInterfaceVersion interfaceVersion, PointerGesturesInterface *q, Display *d, const wl_interface *interface, quint32 version); PointerGesturesInterface *q; }; class PointerGesturesUnstableV1Interface : public PointerGesturesInterface { Q_OBJECT public: explicit PointerGesturesUnstableV1Interface(Display *display, QObject *parent = nullptr); virtual ~PointerGesturesUnstableV1Interface(); private: class Private; }; class PointerSwipeGestureInterface : public Resource { Q_OBJECT public: virtual ~PointerSwipeGestureInterface(); virtual void start(quint32 serial, quint32 fingerCount) = 0; virtual void update(const QSizeF &delta) = 0; virtual void end(quint32 serial) = 0; virtual void cancel(quint32 serial) = 0; protected: class Private; explicit PointerSwipeGestureInterface(Private *p, QObject *parent = nullptr); private: Private *d_func() const; }; class PointerSwipeGestureInterface::Private : public Resource::Private { public: ~Private(); PointerInterface *pointer; protected: Private(PointerSwipeGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer); private: PointerSwipeGestureInterface *q_func() { return reinterpret_cast(q); } }; class PointerPinchGestureInterface : public Resource { Q_OBJECT public: virtual ~PointerPinchGestureInterface(); virtual void start(quint32 serial, quint32 fingerCount) = 0; virtual void update(const QSizeF &delta, qreal scale, qreal rotation) = 0; virtual void end(quint32 serial) = 0; virtual void cancel(quint32 serial) = 0; protected: class Private; explicit PointerPinchGestureInterface(Private *p, QObject *parent = nullptr); private: Private *d_func() const; }; class PointerPinchGestureInterface::Private : public Resource::Private { public: ~Private(); PointerInterface *pointer; protected: Private(PointerPinchGestureInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation, PointerInterface *pointer); private: PointerPinchGestureInterface *q_func() { return reinterpret_cast(q); } }; class PointerSwipeGestureUnstableV1Interface : public PointerSwipeGestureInterface { Q_OBJECT public: explicit PointerSwipeGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer); virtual ~PointerSwipeGestureUnstableV1Interface(); void start(quint32 serial, quint32 fingerCount) override; void update(const QSizeF &delta) override; void end(quint32 serial) override; void cancel(quint32 serial) override; private: friend class PointerGesturesUnstableV1Interface; class Private; Private *d_func() const; }; class PointerPinchGestureUnstableV1Interface : public PointerPinchGestureInterface { Q_OBJECT public: explicit PointerPinchGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer); virtual ~PointerPinchGestureUnstableV1Interface(); void start(quint32 serial, quint32 fingerCount) override; void update(const QSizeF &delta, qreal scale, qreal rotation) override; void end(quint32 serial) override; void cancel(quint32 serial) override; private: friend class PointerGesturesUnstableV1Interface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/pointergestures_interface_v1.cpp b/src/server/pointergestures_interface_v1.cpp index 347a751..913080b 100644 --- a/src/server/pointergestures_interface_v1.cpp +++ b/src/server/pointergestures_interface_v1.cpp @@ -1,297 +1,283 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "pointergestures_interface_p.h" #include "display.h" #include "pointer_interface_p.h" #include "resource_p.h" #include "seat_interface.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class PointerGesturesUnstableV1Interface::Private : public PointerGesturesInterface::Private { public: Private(PointerGesturesUnstableV1Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void getSwipeGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); static void getPinchGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); PointerGesturesUnstableV1Interface *q; static const struct zwp_pointer_gestures_v1_interface s_interface; static const quint32 s_version; }; const quint32 PointerGesturesUnstableV1Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zwp_pointer_gestures_v1_interface PointerGesturesUnstableV1Interface::Private::s_interface = { getSwipeGestureCallback, getPinchGestureCallback }; #endif void PointerGesturesUnstableV1Interface::Private::getSwipeGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *pointer) { PointerInterface *p = PointerInterface::get(pointer); if (!p) { // TODO: raise error? return; } auto m = cast(resource); auto *g = new PointerSwipeGestureUnstableV1Interface(m->q, resource, p); g->d->create(m->display->getConnection(client), version, id); p->d_func()->registerSwipeGesture(g); } void PointerGesturesUnstableV1Interface::Private::getPinchGestureCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer) { PointerInterface *p = PointerInterface::get(pointer); if (!p) { // TODO: raise error? return; } auto m = cast(resource); auto *g = new PointerPinchGestureUnstableV1Interface(m->q, resource, p); g->d->create(m->display->getConnection(client), version, id); p->d_func()->registerPinchGesture(g); } PointerGesturesUnstableV1Interface::Private::Private(PointerGesturesUnstableV1Interface *q, Display *d) : PointerGesturesInterface::Private(PointerGesturesInterfaceVersion::UnstableV1, q, d, &zwp_pointer_gestures_v1_interface, s_version) , q(q) { } void PointerGesturesUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zwp_pointer_gestures_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void PointerGesturesUnstableV1Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } PointerGesturesUnstableV1Interface::PointerGesturesUnstableV1Interface(Display *display, QObject *parent) : PointerGesturesInterface(new Private(this, display), parent) { } PointerGesturesUnstableV1Interface::~PointerGesturesUnstableV1Interface() = default; class PointerSwipeGestureUnstableV1Interface::Private : public PointerSwipeGestureInterface::Private { public: Private(PointerSwipeGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer); ~Private(); void end(quint32 serial, bool end); private: PointerSwipeGestureUnstableV1Interface *q_func() { return reinterpret_cast(q); } static const struct zwp_pointer_gesture_swipe_v1_interface s_interface; }; #ifndef K_DOXYGEN const struct zwp_pointer_gesture_swipe_v1_interface PointerSwipeGestureUnstableV1Interface::Private::s_interface = { resourceDestroyedCallback }; #endif PointerSwipeGestureUnstableV1Interface::Private::Private(PointerSwipeGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer) : PointerSwipeGestureInterface::Private(q, c, parentResource, &zwp_pointer_gesture_swipe_v1_interface, &s_interface, pointer) { } PointerSwipeGestureUnstableV1Interface::Private::~Private() = default; PointerSwipeGestureUnstableV1Interface::PointerSwipeGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer) : PointerSwipeGestureInterface(new Private(this, parent, parentResource, pointer)) { } PointerSwipeGestureUnstableV1Interface::~PointerSwipeGestureUnstableV1Interface() = default; void PointerSwipeGestureUnstableV1Interface::start(quint32 serial, quint32 fingerCount) { Q_D(); SeatInterface *seat = qobject_cast(d->pointer->global()); if (!seat) { return; } if (!seat->focusedPointerSurface()) { return; } zwp_pointer_gesture_swipe_v1_send_begin(resource(), serial, seat->timestamp(), seat->focusedPointerSurface()->resource(), fingerCount); } void PointerSwipeGestureUnstableV1Interface::update(const QSizeF &delta) { Q_D(); SeatInterface *seat = qobject_cast(d->pointer->global()); if (!seat) { return; } zwp_pointer_gesture_swipe_v1_send_update(resource(), seat->timestamp(), wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height())); } void PointerSwipeGestureUnstableV1Interface::Private::end(quint32 serial, bool cancel) { SeatInterface *seat = qobject_cast(pointer->global()); if (!seat) { return; } zwp_pointer_gesture_swipe_v1_send_end(resource, serial, seat->timestamp(), uint32_t(cancel)); } void PointerSwipeGestureUnstableV1Interface::end(quint32 serial) { Q_D(); d->end(serial, false); } void PointerSwipeGestureUnstableV1Interface::cancel(quint32 serial) { Q_D(); d->end(serial, true); } PointerSwipeGestureUnstableV1Interface::Private *PointerSwipeGestureUnstableV1Interface::d_func() const { return reinterpret_cast(d.data()); } class PointerPinchGestureUnstableV1Interface::Private : public PointerPinchGestureInterface::Private { public: Private(PointerPinchGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer); ~Private(); void end(quint32 serial, bool end); private: PointerPinchGestureUnstableV1Interface *q_func() { return reinterpret_cast(q); } static const struct zwp_pointer_gesture_pinch_v1_interface s_interface; }; #ifndef K_DOXYGEN const struct zwp_pointer_gesture_pinch_v1_interface PointerPinchGestureUnstableV1Interface::Private::s_interface = { resourceDestroyedCallback }; #endif PointerPinchGestureUnstableV1Interface::Private::Private(PointerPinchGestureUnstableV1Interface *q, PointerGesturesUnstableV1Interface *c, wl_resource *parentResource, PointerInterface *pointer) : PointerPinchGestureInterface::Private(q, c, parentResource, &zwp_pointer_gesture_pinch_v1_interface, &s_interface, pointer) { } PointerPinchGestureUnstableV1Interface::Private::~Private() = default; PointerPinchGestureUnstableV1Interface::PointerPinchGestureUnstableV1Interface(PointerGesturesUnstableV1Interface *parent, wl_resource *parentResource, PointerInterface *pointer) : PointerPinchGestureInterface(new Private(this, parent, parentResource, pointer)) { } PointerPinchGestureUnstableV1Interface::~PointerPinchGestureUnstableV1Interface() = default; void PointerPinchGestureUnstableV1Interface::start(quint32 serial, quint32 fingerCount) { Q_D(); SeatInterface *seat = qobject_cast(d->pointer->global()); if (!seat) { return; } if (!seat->focusedPointerSurface()) { return; } zwp_pointer_gesture_pinch_v1_send_begin(resource(), serial, seat->timestamp(), seat->focusedPointerSurface()->resource(), fingerCount); } void PointerPinchGestureUnstableV1Interface::update(const QSizeF &delta, qreal scale, qreal rotation) { Q_D(); SeatInterface *seat = qobject_cast(d->pointer->global()); if (!seat) { return; } zwp_pointer_gesture_pinch_v1_send_update(resource(), seat->timestamp(), wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height()), wl_fixed_from_double(scale), wl_fixed_from_double(rotation)); } void PointerPinchGestureUnstableV1Interface::Private::end(quint32 serial, bool cancel) { SeatInterface *seat = qobject_cast(pointer->global()); if (!seat) { return; } zwp_pointer_gesture_pinch_v1_send_end(resource, serial, seat->timestamp(), uint32_t(cancel)); } void PointerPinchGestureUnstableV1Interface::end(quint32 serial) { Q_D(); d->end(serial, false); } void PointerPinchGestureUnstableV1Interface::cancel(quint32 serial) { Q_D(); d->end(serial, true); } PointerPinchGestureUnstableV1Interface::Private *PointerPinchGestureUnstableV1Interface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/qtsurfaceextension_interface.cpp b/src/server/qtsurfaceextension_interface.cpp index a1d530f..4c777ae 100644 --- a/src/server/qtsurfaceextension_interface.cpp +++ b/src/server/qtsurfaceextension_interface.cpp @@ -1,234 +1,220 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "qtsurfaceextension_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include #include #include #include namespace KWayland { namespace Server { class QtSurfaceExtensionInterface::Private : public Global::Private { public: Private(QtSurfaceExtensionInterface *q, Display *d); QList surfaces; private: static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); QtSurfaceExtensionInterface *q; static const struct qt_surface_extension_interface s_interface; static const quint32 s_version; }; const quint32 QtSurfaceExtensionInterface::Private::s_version = 1; QtSurfaceExtensionInterface::Private::Private(QtSurfaceExtensionInterface *q, Display *d) : Global::Private(d, &qt_surface_extension_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct qt_surface_extension_interface QtSurfaceExtensionInterface::Private::s_interface = { createSurfaceCallback }; #endif class QtExtendedSurfaceInterface::Private : public Resource::Private { public: Private(QtExtendedSurfaceInterface *q, QtSurfaceExtensionInterface *shell, SurfaceInterface *surface, wl_resource *parentResource); SurfaceInterface *surface; private: // interface callbacks static void updateGenericPropertyCallback(wl_client *client, wl_resource *resource, const char *name, wl_array *value); static void setContentOrientationMaskCallback(wl_client *client, wl_resource *resource, int32_t orientation); static void setWindowFlagsCallback(wl_client *client, wl_resource *resource, int32_t flags); static void raiseCallback(wl_client *client, wl_resource *resource); static void lowerCallback(wl_client *client, wl_resource *resource); QtExtendedSurfaceInterface *q_func() { return reinterpret_cast(q); } static const struct qt_extended_surface_interface s_interface; }; QtSurfaceExtensionInterface::QtSurfaceExtensionInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } QtSurfaceExtensionInterface::~QtSurfaceExtensionInterface() = default; void QtSurfaceExtensionInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&qt_surface_extension_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, nullptr); } void QtSurfaceExtensionInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void QtSurfaceExtensionInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](QtExtendedSurfaceInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), WL_DISPLAY_ERROR_INVALID_OBJECT, "Qt Surface Extension already created"); return; } QtExtendedSurfaceInterface *shellSurface = new QtExtendedSurfaceInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &QtExtendedSurfaceInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } /********************************* * ShellSurfaceInterface *********************************/ QtExtendedSurfaceInterface::Private::Private(QtExtendedSurfaceInterface *q, QtSurfaceExtensionInterface *shell, SurfaceInterface *surface, wl_resource *parentResource) : Resource::Private(q, shell, parentResource, &qt_extended_surface_interface, &s_interface) , surface(surface) { } #ifndef K_DOXYGEN const struct qt_extended_surface_interface QtExtendedSurfaceInterface::Private::s_interface = { updateGenericPropertyCallback, setContentOrientationMaskCallback, setWindowFlagsCallback, raiseCallback, lowerCallback }; #endif void QtExtendedSurfaceInterface::Private::lowerCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) emit cast(resource)->q_func()->lowerRequested(); } void QtExtendedSurfaceInterface::Private::raiseCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) emit cast(resource)->q_func()->raiseRequested(); } void QtExtendedSurfaceInterface::Private::setContentOrientationMaskCallback(wl_client *client, wl_resource *resource, int32_t orientation) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(orientation) } void QtExtendedSurfaceInterface::Private::setWindowFlagsCallback(wl_client *client, wl_resource *resource, int32_t flags) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(flags) } void QtExtendedSurfaceInterface::Private::updateGenericPropertyCallback(wl_client *client, wl_resource *resource, const char *name, wl_array *value) { Q_UNUSED(client) QByteArray data = QByteArray::fromRawData(static_cast(value->data), value->size); QVariant variantValue; QDataStream ds(data); ds >> variantValue; cast(resource)->q_func()->setProperty(name, variantValue); } QtExtendedSurfaceInterface::QtExtendedSurfaceInterface(QtSurfaceExtensionInterface *shell, SurfaceInterface *parent, wl_resource *parentResource) : Resource(new Private(this, shell, parent, parentResource)) { auto unsetSurface = [this] { Q_D(); d->surface = nullptr; }; connect(parent, &Resource::unbound, this, unsetSurface); connect(parent, &QObject::destroyed, this, unsetSurface); } QtExtendedSurfaceInterface::~QtExtendedSurfaceInterface() = default; SurfaceInterface *QtExtendedSurfaceInterface::surface() const { Q_D(); return d->surface; } QtSurfaceExtensionInterface *QtExtendedSurfaceInterface::shell() const { Q_D(); return reinterpret_cast(d->global); } QtExtendedSurfaceInterface::Private *QtExtendedSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } void QtExtendedSurfaceInterface::close() { Q_D(); if (!d->resource) { return; } qt_extended_surface_send_close(d->resource); d->client->flush(); } } } diff --git a/src/server/qtsurfaceextension_interface.h b/src/server/qtsurfaceextension_interface.h index 09d5f0c..f4329b8 100644 --- a/src/server/qtsurfaceextension_interface.h +++ b/src/server/qtsurfaceextension_interface.h @@ -1,96 +1,82 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_QTSURFACEEXTENSION_INTERFACE_H #define WAYLAND_SERVER_QTSURFACEEXTENSION_INTERFACE_H #include #include #include "global.h" #include "resource.h" class QSize; struct wl_resource; namespace KWayland { namespace Server { class Display; class SurfaceInterface; class QtExtendedSurfaceInterface; /** * TODO */ class KWAYLANDSERVER_EXPORT QtSurfaceExtensionInterface : public Global { Q_OBJECT public: virtual ~QtSurfaceExtensionInterface(); Q_SIGNALS: void surfaceCreated(KWayland::Server::QtExtendedSurfaceInterface*); private: friend class Display; explicit QtSurfaceExtensionInterface(Display *display, QObject *parent); class Private; }; /** * TODO */ class KWAYLANDSERVER_EXPORT QtExtendedSurfaceInterface : public Resource { Q_OBJECT public: virtual ~QtExtendedSurfaceInterface(); SurfaceInterface *surface() const; QtSurfaceExtensionInterface *shell() const; void close(); Q_SIGNALS: /** * Requests that the window be raised to appear above other windows. * @since 5.5 **/ void raiseRequested(); /** * Requests that the window be lowered to appear below other windows. * @since 5.5 **/ void lowerRequested(); private: friend class QtSurfaceExtensionInterface; explicit QtExtendedSurfaceInterface(QtSurfaceExtensionInterface *shell, SurfaceInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/region_interface.cpp b/src/server/region_interface.cpp index e894563..b012807 100644 --- a/src/server/region_interface.cpp +++ b/src/server/region_interface.cpp @@ -1,119 +1,105 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "region_interface.h" #include "resource_p.h" #include "compositor_interface.h" // Wayland #include namespace KWayland { namespace Server { class RegionInterface::Private : public Resource::Private { public: Private(CompositorInterface *compositor, RegionInterface *q, wl_resource *parentResource); ~Private(); QRegion qtRegion; private: RegionInterface *q_func() { return reinterpret_cast(q); } void add(const QRect &rect); void subtract(const QRect &rect); static void addCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height); static void subtractCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height); static const struct wl_region_interface s_interface; }; #ifndef K_DOXYGEN const struct wl_region_interface RegionInterface::Private::s_interface = { resourceDestroyedCallback, addCallback, subtractCallback }; #endif RegionInterface::Private::Private(CompositorInterface *compositor, RegionInterface *q, wl_resource *parentResource) : Resource::Private(q, compositor, parentResource, &wl_region_interface, &s_interface) { } RegionInterface::Private::~Private() = default; void RegionInterface::Private::add(const QRect &rect) { qtRegion = qtRegion.united(rect); Q_Q(RegionInterface); emit q->regionChanged(qtRegion); } void RegionInterface::Private::subtract(const QRect &rect) { if (qtRegion.isEmpty()) { return; } qtRegion = qtRegion.subtracted(rect); Q_Q(RegionInterface); emit q->regionChanged(qtRegion); } void RegionInterface::Private::addCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) cast(r)->add(QRect(x, y, width, height)); } void RegionInterface::Private::subtractCallback(wl_client *client, wl_resource *r, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) cast(r)->subtract(QRect(x, y, width, height)); } RegionInterface::RegionInterface(CompositorInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, this, parentResource)) { } RegionInterface::~RegionInterface() = default; QRegion RegionInterface::region() const { Q_D(); return d->qtRegion; } RegionInterface *RegionInterface::get(wl_resource *native) { return Private::get(native); } RegionInterface::Private *RegionInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/region_interface.h b/src/server/region_interface.h index 24dbc39..962a766 100644 --- a/src/server/region_interface.h +++ b/src/server/region_interface.h @@ -1,78 +1,64 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_REGION_INTERFACE_H #define WAYLAND_SERVER_REGION_INTERFACE_H #include #include #include #include "resource.h" namespace KWayland { namespace Server { class CompositorInterface; /** * @brief Resource for the wl_region. * * A RegionInterface gets created by the CompositorInterface and represents * a QRegion. * * @see CompositorInterface **/ class KWAYLANDSERVER_EXPORT RegionInterface : public Resource { Q_OBJECT public: virtual ~RegionInterface(); /** * @returns the data of the region as a QRegion. **/ QRegion region() const; /** * @returns The RegionInterface for the @p native resource. **/ static RegionInterface *get(wl_resource *native); Q_SIGNALS: /** * Emitted whenever the region changes. **/ void regionChanged(const QRegion&); private: friend class CompositorInterface; explicit RegionInterface(CompositorInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::RegionInterface*) #endif diff --git a/src/server/relativepointer_interface.cpp b/src/server/relativepointer_interface.cpp index 69af3ad..2a790af 100644 --- a/src/server/relativepointer_interface.cpp +++ b/src/server/relativepointer_interface.cpp @@ -1,84 +1,70 @@ -/**************************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "relativepointer_interface_p.h" namespace KWayland { namespace Server { RelativePointerManagerInterface::Private::Private(RelativePointerInterfaceVersion interfaceVersion, RelativePointerManagerInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } RelativePointerManagerInterface::RelativePointerManagerInterface(Private *d, QObject *parent) : Global(d, parent) { } RelativePointerManagerInterface::~RelativePointerManagerInterface() = default; RelativePointerInterfaceVersion RelativePointerManagerInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } RelativePointerManagerInterface::Private *RelativePointerManagerInterface::d_func() const { return reinterpret_cast(d.data()); } RelativePointerInterface::Private::Private(RelativePointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) { } RelativePointerInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } RelativePointerInterface::RelativePointerInterface(Private *p, QObject *parent) : Resource(p, parent) { } RelativePointerInterface::~RelativePointerInterface() = default; void RelativePointerInterface::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { Q_D(); d->relativeMotion(delta, deltaNonAccelerated, microseconds); } RelativePointerInterface::Private *RelativePointerInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/relativepointer_interface.h b/src/server/relativepointer_interface.h index c2311f6..8731d8d 100644 --- a/src/server/relativepointer_interface.h +++ b/src/server/relativepointer_interface.h @@ -1,73 +1,59 @@ -/**************************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_RELATIVE_POINTER_INTERFACE_H #define KWAYLAND_SERVER_RELATIVE_POINTER_INTERFACE_H #include "global.h" #include namespace KWayland { namespace Server { class Display; enum class RelativePointerInterfaceVersion { /** * zwp_relative_pointer_manager_v1 and zwp_relative_pointer_v1 **/ UnstableV1 }; /** * Manager object to create relative pointer interfaces. * * Once created the interaction happens through the SeatInterface class * which automatically delegates relative motion events to the created relative pointer * interfaces. * * @see SeatInterface::relativePointerMotion * @since 5.28 **/ class KWAYLANDSERVER_EXPORT RelativePointerManagerInterface : public Global { Q_OBJECT public: virtual ~RelativePointerManagerInterface(); /** * @returns The interface version used by this RelativePointerManagerInterface **/ RelativePointerInterfaceVersion interfaceVersion() const; protected: class Private; explicit RelativePointerManagerInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; } } #endif diff --git a/src/server/relativepointer_interface_p.h b/src/server/relativepointer_interface_p.h index a7220a0..784f587 100644 --- a/src/server/relativepointer_interface_p.h +++ b/src/server/relativepointer_interface_p.h @@ -1,100 +1,86 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_RELATIVEPOINTER_INTERFACE_P_H #define KWAYLAND_SERVER_RELATIVEPOINTER_INTERFACE_P_H #include "relativepointer_interface.h" #include "resource_p.h" #include "global_p.h" namespace KWayland { namespace Server { class RelativePointerManagerInterface::Private : public Global::Private { public: RelativePointerInterfaceVersion interfaceVersion; protected: Private(RelativePointerInterfaceVersion interfaceVersion, RelativePointerManagerInterface *q, Display *d, const wl_interface *interface, quint32 version); RelativePointerManagerInterface *q; }; class RelativePointerManagerUnstableV1Interface : public RelativePointerManagerInterface { Q_OBJECT public: explicit RelativePointerManagerUnstableV1Interface(Display *display, QObject *parent = nullptr); virtual ~RelativePointerManagerUnstableV1Interface(); private: class Private; }; class RelativePointerInterface : public Resource { Q_OBJECT public: virtual ~RelativePointerInterface(); void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); protected: class Private; explicit RelativePointerInterface(Private *p, QObject *parent = nullptr); private: Private *d_func() const; }; class RelativePointerInterface::Private : public Resource::Private { public: ~Private(); virtual void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) = 0; protected: Private(RelativePointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); private: RelativePointerInterface *q_func() { return reinterpret_cast(q); } }; class RelativePointerUnstableV1Interface : public RelativePointerInterface { Q_OBJECT public: virtual ~RelativePointerUnstableV1Interface(); private: explicit RelativePointerUnstableV1Interface(RelativePointerManagerUnstableV1Interface *parent, wl_resource *parentResource); friend class RelativePointerManagerUnstableV1Interface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/relativepointer_interface_v1.cpp b/src/server/relativepointer_interface_v1.cpp index 6dcaf49..e128494 100644 --- a/src/server/relativepointer_interface_v1.cpp +++ b/src/server/relativepointer_interface_v1.cpp @@ -1,160 +1,146 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "relativepointer_interface_p.h" #include "display.h" #include "pointer_interface_p.h" #include #include namespace KWayland { namespace Server { class RelativePointerManagerUnstableV1Interface::Private : public RelativePointerManagerInterface::Private { public: Private(RelativePointerManagerUnstableV1Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void getRelativePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * pointer); RelativePointerManagerUnstableV1Interface *q; static const struct zwp_relative_pointer_manager_v1_interface s_interface; static const quint32 s_version; }; const quint32 RelativePointerManagerUnstableV1Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zwp_relative_pointer_manager_v1_interface RelativePointerManagerUnstableV1Interface::Private::s_interface = { destroyCallback, getRelativePointerCallback }; #endif void RelativePointerManagerUnstableV1Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void RelativePointerManagerUnstableV1Interface::Private::getRelativePointerCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *pointer) { PointerInterface *p = PointerInterface::get(pointer); if (!p) { // TODO: raise error? return; } auto m = cast(resource); auto *r = new RelativePointerUnstableV1Interface(m->q, resource); r->d->create(m->display->getConnection(client), version, id); p->d_func()->registerRelativePointer(r); } RelativePointerManagerUnstableV1Interface::Private::Private(RelativePointerManagerUnstableV1Interface *q, Display *d) : RelativePointerManagerInterface::Private(RelativePointerInterfaceVersion::UnstableV1, q, d, &zwp_relative_pointer_manager_v1_interface, s_version) , q(q) { } void RelativePointerManagerUnstableV1Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zwp_relative_pointer_manager_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void RelativePointerManagerUnstableV1Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } RelativePointerManagerUnstableV1Interface::RelativePointerManagerUnstableV1Interface(Display *display, QObject *parent) : RelativePointerManagerInterface(new Private(this, display), parent) { } RelativePointerManagerUnstableV1Interface::~RelativePointerManagerUnstableV1Interface() = default; class RelativePointerUnstableV1Interface::Private : public RelativePointerInterface::Private { public: Private(RelativePointerUnstableV1Interface *q, RelativePointerManagerUnstableV1Interface *c, wl_resource *parentResource); ~Private(); void relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) override; private: RelativePointerUnstableV1Interface *q_func() { return reinterpret_cast(q); } static const struct zwp_relative_pointer_v1_interface s_interface; }; #ifndef K_DOXYGEN const struct zwp_relative_pointer_v1_interface RelativePointerUnstableV1Interface::Private::s_interface = { resourceDestroyedCallback }; #endif RelativePointerUnstableV1Interface::Private::Private(RelativePointerUnstableV1Interface *q, RelativePointerManagerUnstableV1Interface *c, wl_resource *parentResource) : RelativePointerInterface::Private(q, c, parentResource, &zwp_relative_pointer_v1_interface, &s_interface) { } RelativePointerUnstableV1Interface::Private::~Private() = default; void RelativePointerUnstableV1Interface::Private::relativeMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { if (!resource) { return; } zwp_relative_pointer_v1_send_relative_motion(resource, (microseconds >> 32), microseconds, wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height()), wl_fixed_from_double(deltaNonAccelerated.width()), wl_fixed_from_double(deltaNonAccelerated.height())); } RelativePointerUnstableV1Interface::RelativePointerUnstableV1Interface(RelativePointerManagerUnstableV1Interface *parent, wl_resource *parentResource) : RelativePointerInterface(new Private(this, parent, parentResource)) { } RelativePointerUnstableV1Interface::~RelativePointerUnstableV1Interface() = default; } } diff --git a/src/server/remote_access_interface.cpp b/src/server/remote_access_interface.cpp index 6504c04..e4da254 100644 --- a/src/server/remote_access_interface.cpp +++ b/src/server/remote_access_interface.cpp @@ -1,385 +1,371 @@ -/**************************************************************************** -Copyright 2016 Oleg Chernovskiy - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "remote_access_interface.h" #include "remote_access_interface_p.h" #include "output_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "logging.h" #include #include #include #include namespace KWayland { namespace Server { class BufferHandle::Private // @see gbm_import_fd_data { public: // Note that on client side received fd number will be different // and meaningful only for client process! // Thus we can use server-side fd as an implicit unique id qint32 fd = 0; ///< also internal buffer id for client quint32 width = 0; quint32 height = 0; quint32 stride = 0; quint32 format = 0; }; BufferHandle::BufferHandle() : d(new Private) { } BufferHandle::~BufferHandle() { } void BufferHandle::setFd(qint32 fd) { d->fd = fd; } qint32 BufferHandle::fd() const { return d->fd; } void BufferHandle::setSize(quint32 width, quint32 height) { d->width = width; d->height = height; } quint32 BufferHandle::width() const { return d->width; } quint32 BufferHandle::height() const { return d->height; } void BufferHandle::setStride(quint32 stride) { d->stride = stride; } quint32 BufferHandle::stride() const { return d->stride; } void BufferHandle::setFormat(quint32 format) { d->format = format; } quint32 BufferHandle::format() const { return d->format; } /** * @brief helper struct for manual reference counting. * automatic counting via QSharedPointer is no-go here as we hold strong reference in sentBuffers. */ struct BufferHolder { const BufferHandle *buf; quint64 counter; }; - + class RemoteAccessManagerInterface::Private : public Global::Private { public: Private(RemoteAccessManagerInterface *q, Display *d); ~Private() override; /** * @brief Send buffer ready notification to all connected clients * @param output wl_output interface to determine which screen sent this buf * @param buf buffer containing GBM-related params */ void sendBufferReady(const OutputInterface *output, const BufferHandle *buf); /** * @brief Release all bound buffers associated with this resource * @param resource one of bound clients */ void release(wl_resource *resource); /** * Clients of this interface. * This may be screenshot app, video capture app, * remote control app etc. */ QList clientResources; private: // methods static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void getBufferCallback(wl_client *client, wl_resource *resource, uint32_t buffer, int32_t internalBufId); static void releaseCallback(wl_client *client, wl_resource *resource); void bind(wl_client *client, uint32_t version, uint32_t id) override; /** * @brief Unreferences counter and frees buffer when it reaches zero * @param buf holder to decrease reference counter on * @return true if buffer was released, false otherwise */ bool unref(BufferHolder &buf); // fields static const struct org_kde_kwin_remote_access_manager_interface s_interface; static const quint32 s_version; RemoteAccessManagerInterface *q; /** * Buffers that were sent but still not acked by server * Keys are fd numbers as they are unique **/ QHash sentBuffers; }; const quint32 RemoteAccessManagerInterface::Private::s_version = 1; RemoteAccessManagerInterface::Private::Private(RemoteAccessManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_remote_access_manager_interface, s_version) , q(q) { } void RemoteAccessManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { // create new client resource auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_remote_access_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // add newly created client resource to the list clientResources << resource; } void RemoteAccessManagerInterface::Private::sendBufferReady(const OutputInterface *output, const BufferHandle *buf) { BufferHolder holder {buf, 0}; // notify clients qCDebug(KWAYLAND_SERVER) << "Server buffer sent: fd" << buf->fd(); for (auto res : clientResources) { auto client = wl_resource_get_client(res); auto boundScreens = output->clientResources(display->getConnection(client)); // clients don't necessarily bind outputs if (boundScreens.isEmpty()) { continue; } // no reason for client to bind wl_output multiple times, send only to first one org_kde_kwin_remote_access_manager_send_buffer_ready(res, buf->fd(), boundScreens[0]); holder.counter++; } if (holder.counter == 0) { // buffer was not requested by any client emit q->bufferReleased(buf); return; } // store buffer locally, clients will ask it later sentBuffers[buf->fd()] = holder; } #ifndef K_DOXYGEN const struct org_kde_kwin_remote_access_manager_interface RemoteAccessManagerInterface::Private::s_interface = { getBufferCallback, releaseCallback }; #endif void RemoteAccessManagerInterface::Private::getBufferCallback(wl_client *client, wl_resource *resource, uint32_t buffer, int32_t internalBufId) { Private *p = cast(resource); // client asks for buffer we earlier announced, we must have it if (Q_UNLIKELY(!p->sentBuffers.contains(internalBufId))) { // no such buffer (?) wl_resource_post_no_memory(resource); return; } BufferHolder &bh = p->sentBuffers[internalBufId]; auto rbuf = new RemoteBufferInterface(p->q, resource, bh.buf); rbuf->create(p->display->getConnection(client), wl_resource_get_version(resource), buffer); if (!rbuf->resource()) { wl_resource_post_no_memory(resource); delete rbuf; return; } QObject::connect(rbuf, &Resource::aboutToBeUnbound, p->q, [p, rbuf, resource, &bh] { if (!p->clientResources.contains(resource)) { // remote buffer destroy confirmed after client is already gone // all relevant buffers are already unreferenced return; } qCDebug(KWAYLAND_SERVER) << "Remote buffer returned, client" << wl_resource_get_id(resource) << ", id" << rbuf->id() << ", fd" << bh.buf->fd(); if (p->unref(bh)) { p->sentBuffers.remove(bh.buf->fd()); } }); // send buffer params rbuf->passFd(); } void RemoteAccessManagerInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); unbind(resource); } bool RemoteAccessManagerInterface::Private::unref(BufferHolder &bh) { bh.counter--; if (!bh.counter) { // no more clients using this buffer qCDebug(KWAYLAND_SERVER) << "Buffer released, fd" << bh.buf->fd(); emit q->bufferReleased(bh.buf); return true; } return false; } void RemoteAccessManagerInterface::Private::unbind(wl_resource *resource) { // we're unbinding, all sent buffers for this client are now effectively invalid Private *p = cast(resource); p->release(resource); } void RemoteAccessManagerInterface::Private::release(wl_resource *resource) { // all holders should decrement their counter as one client is gone QMutableHashIterator itr(sentBuffers); while (itr.hasNext()) { BufferHolder &bh = itr.next().value(); if (unref(bh)) { itr.remove(); } } clientResources.removeAll(resource); } RemoteAccessManagerInterface::Private::~Private() { // server deletes created interfaces, release all held buffers auto c = clientResources; // shadow copy for (auto res : c) { release(res); } } RemoteAccessManagerInterface::RemoteAccessManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } void RemoteAccessManagerInterface::sendBufferReady(const OutputInterface *output, const BufferHandle *buf) { Private *priv = reinterpret_cast(d.data()); priv->sendBufferReady(output, buf); } bool RemoteAccessManagerInterface::isBound() const { Private *priv = reinterpret_cast(d.data()); return !priv->clientResources.isEmpty(); } class RemoteBufferInterface::Private : public Resource::Private { public: Private(RemoteAccessManagerInterface *ram, RemoteBufferInterface *q, wl_resource *pResource, const BufferHandle *buf); ~Private(); void passFd(); private: static const struct org_kde_kwin_remote_buffer_interface s_interface; const BufferHandle *wrapped; }; #ifndef K_DOXYGEN const struct org_kde_kwin_remote_buffer_interface RemoteBufferInterface::Private::s_interface = { resourceDestroyedCallback }; #endif RemoteBufferInterface::Private::Private(RemoteAccessManagerInterface *ram, RemoteBufferInterface *q, wl_resource *pResource, const BufferHandle *buf) : Resource::Private(q, ram, pResource, &org_kde_kwin_remote_buffer_interface, &s_interface), wrapped(buf) { } RemoteBufferInterface::Private::~Private() { } void RemoteBufferInterface::Private::passFd() { org_kde_kwin_remote_buffer_send_gbm_handle(resource, wrapped->fd(), wrapped->width(), wrapped->height(), wrapped->stride(), wrapped->format()); } RemoteBufferInterface::RemoteBufferInterface(RemoteAccessManagerInterface *ram, wl_resource *pResource, const BufferHandle *buf) : Resource(new Private(ram, this, pResource, buf), ram) { } RemoteBufferInterface::Private *RemoteBufferInterface::d_func() const { return reinterpret_cast(d.data()); } void RemoteBufferInterface::passFd() { d_func()->passFd(); } } } diff --git a/src/server/remote_access_interface.h b/src/server/remote_access_interface.h index c0f8ee6..94bf507 100644 --- a/src/server/remote_access_interface.h +++ b/src/server/remote_access_interface.h @@ -1,99 +1,85 @@ -/**************************************************************************** -Copyright 2016 Oleg Chernovskiy +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_REMOTE_ACCESS_INTERFACE_H #define KWAYLAND_SERVER_REMOTE_ACCESS_INTERFACE_H #include "global.h" namespace KWayland { namespace Server { class Display; class OutputInterface; /** * The structure server should fill to use this interface. * Lifecycle: * 1. BufferHandle is filled and passed to RemoteAccessManager * (stored in manager's sent list) - * 2. Clients confirm that they wants this buffer, the RemoteBuffer + * 2. Clients confirm that they wants this buffer, the RemoteBuffer * interfaces are then created and wrapped around BufferHandle. * 3. Once all clients are done with buffer (or disconnected), * RemoteBuffer notifies manager and release signal is emitted. - * + * * It's the responsibility of your process to delete this BufferHandle * and release its' fd afterwards. **/ class KWAYLANDSERVER_EXPORT BufferHandle { public: explicit BufferHandle(); virtual ~BufferHandle(); void setFd(qint32 fd); void setSize(quint32 width, quint32 height); void setStride(quint32 stride); void setFormat(quint32 format); - + qint32 fd() const; quint32 height() const; quint32 width() const; quint32 stride() const; quint32 format() const; private: friend class RemoteAccessManagerInterface; friend class RemoteBufferInterface; class Private; QScopedPointer d; }; class KWAYLANDSERVER_EXPORT RemoteAccessManagerInterface : public Global { Q_OBJECT public: virtual ~RemoteAccessManagerInterface() = default; /** * Store buffer in sent list and notify client that we have a buffer for it **/ void sendBufferReady(const OutputInterface *output, const BufferHandle *buf); /** * Check whether interface has been bound **/ bool isBound() const; Q_SIGNALS: /** * Previously sent buffer has been released by client */ void bufferReleased(const BufferHandle *buf); private: explicit RemoteAccessManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; } } #endif diff --git a/src/server/remote_access_interface_p.h b/src/server/remote_access_interface_p.h index fa28ea9..334b8ef 100644 --- a/src/server/remote_access_interface_p.h +++ b/src/server/remote_access_interface_p.h @@ -1,58 +1,44 @@ -/**************************************************************************** -Copyright 2016 Oleg Chernovskiy - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Oleg Chernovskiy + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_REMOTE_ACCESS_INTERFACE_P_H #define KWAYLAND_SERVER_REMOTE_ACCESS_INTERFACE_P_H #include "resource.h" namespace KWayland { namespace Server { - + /** * @class RemoteBufferInterface * @brief Internally used class. Holds data of passed buffer and client resource. Also controls buffer lifecycle. * @see RemoteAccessManagerInterface */ class RemoteBufferInterface : public Resource { Q_OBJECT public: virtual ~RemoteBufferInterface() = default; /** * Sends GBM fd to the client. * Note that server still has to close mirror fd from its side. **/ void passFd(); private: explicit RemoteBufferInterface(RemoteAccessManagerInterface *ram, wl_resource *pResource, const BufferHandle *buf); friend class RemoteAccessManagerInterface; class Private; Private *d_func() const; }; } } #endif // KWAYLAND_SERVER_REMOTE_ACCESS_P_H diff --git a/src/server/resource.cpp b/src/server/resource.cpp index f728ff3..a32f510 100644 --- a/src/server/resource.cpp +++ b/src/server/resource.cpp @@ -1,121 +1,107 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "resource.h" #include "resource_p.h" #include "clientconnection.h" #include namespace KWayland { namespace Server { QList Resource::Private::s_allResources; Resource::Private::Private(Resource *q, Global *g, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : parentResource(parentResource) , global(g) , q(q) , m_interface(interface) , m_interfaceImplementation(implementation) { s_allResources << this; } Resource::Private::~Private() { s_allResources.removeAll(this); if (resource) { wl_resource_destroy(resource); } } void Resource::Private::create(ClientConnection *c, quint32 version, quint32 id) { Q_ASSERT(!resource); Q_ASSERT(!client); client = c; resource = client->createResource(m_interface, version, id); if (!resource) { return; } wl_resource_set_implementation(resource, m_interfaceImplementation, this, unbind); } void Resource::Private::unbind(wl_resource *r) { Private *p = cast(r); emit p->q->aboutToBeUnbound(); p->resource = nullptr; emit p->q->unbound(); p->q->deleteLater(); } void Resource::Private::resourceDestroyedCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } Resource::Resource(Resource::Private *d, QObject *parent) : QObject(parent) , d(d) { } Resource::~Resource() = default; void Resource::create(ClientConnection *client, quint32 version, quint32 id) { d->create(client, version, id); } ClientConnection *Resource::client() { return d->client; } Global *Resource::global() { return d->global; } wl_resource *Resource::resource() { return d->resource; } wl_resource *Resource::parentResource() const { return d->parentResource; } quint32 Resource::id() const { if (!d->resource) { return 0; } return wl_resource_get_id(d->resource); } } } diff --git a/src/server/resource.h b/src/server/resource.h index c513815..fe7db21 100644 --- a/src/server/resource.h +++ b/src/server/resource.h @@ -1,104 +1,90 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_RESOURCE_H #define WAYLAND_SERVER_RESOURCE_H #include #include struct wl_client; struct wl_resource; namespace KWayland { namespace Server { class ClientConnection; class Global; /** * @brief Represents a bound Resource. * * A Resource normally gets created by a @link Global @endlink. * * The Resource is a base class for all specific resources and provides * access to various common aspects. **/ class KWAYLANDSERVER_EXPORT Resource : public QObject { Q_OBJECT public: virtual ~Resource(); void create(ClientConnection *client, quint32 version, quint32 id); /** * @returns the native wl_resource this Resource was created for. **/ wl_resource *resource(); /** * @returns The ClientConnection for which the Resource was created. **/ ClientConnection *client(); /** * @returns The Global which created the Resource. **/ Global *global(); /** * @returns the native parent wl_resource, e.g. the wl_resource bound on the Global **/ wl_resource *parentResource() const; /** * @returns The id of this Resource if it is created, otherwise @c 0. * * This is a convenient wrapper for wl_resource_get_id. * @since 5.3 **/ quint32 id() const; Q_SIGNALS: /** * This signal is emitted when the client unbound this Resource. * The Resource will be deleted in the next event cycle after this event. * @since 5.24 **/ void unbound(); /** * This signal is emitted when the client is in the process of unbinding the Resource. * In opposite to @link{unbound} the @link{resource} is still valid and allows to perform * cleanup tasks. Example: send a keyboard leave for the Surface which is in the process of * getting destroyed. * * @see unbound * @since 5.37 **/ void aboutToBeUnbound(); protected: class Private; explicit Resource(Private *d, QObject *parent = nullptr); QScopedPointer d; }; } } #endif diff --git a/src/server/resource_p.h b/src/server/resource_p.h index 34ca5c5..f7d9fe0 100644 --- a/src/server/resource_p.h +++ b/src/server/resource_p.h @@ -1,97 +1,83 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_RESOURCE_P_H #define WAYLAND_SERVER_RESOURCE_P_H #include "resource.h" #include #include namespace KWayland { namespace Server { class Resource::Private { public: virtual ~Private(); void create(ClientConnection *client, quint32 version, quint32 id); wl_resource *parentResource = nullptr; wl_resource *resource = nullptr; ClientConnection *client = nullptr; Global *global; template static ResourceDerived *get(wl_resource *native) { static_assert(std::is_base_of::value, "ResourceDerived must be derived from Resource"); if (!native) { return nullptr; } auto it = std::find_if(s_allResources.constBegin(), s_allResources.constEnd(), [native](Private *p) { return p->resource == native; } ); if (it == s_allResources.constEnd()) { return nullptr; } return reinterpret_cast((*it)->q); } template static ResourceDerived *get(quint32 id, const ClientConnection *c) { static_assert(std::is_base_of::value, "ResourceDerived must be derived from Resource"); auto it = std::find_if(s_allResources.constBegin(), s_allResources.constEnd(), [id, c](Private *p) { return c == p->client && p->resource && wl_resource_get_id(p->resource) == id; } ); if (it == s_allResources.constEnd()) { return nullptr; } return reinterpret_cast((*it)->q); } protected: explicit Private(Resource *q, Global *g, wl_resource *parentResource, const wl_interface *interface, const void *implementation); template static Derived *cast(wl_resource *r) { static_assert(std::is_base_of::value, "Derived must be derived from Resource::Private"); return r ? reinterpret_cast(wl_resource_get_user_data(r)) : nullptr; } static void unbind(wl_resource *resource); static void resourceDestroyedCallback(wl_client *client, wl_resource *resource); Resource *q; static QList s_allResources; private: const wl_interface *const m_interface; const void *const m_interfaceImplementation; }; } } #endif diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp index 558a15a..b1dc4cf 100644 --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -1,1615 +1,1601 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "seat_interface.h" #include "seat_interface_p.h" #include "display.h" #include "datadevice_interface.h" #include "datasource_interface.h" #include "keyboard_interface.h" #include "keyboard_interface_p.h" #include "pointer_interface.h" #include "pointer_interface_p.h" #include "surface_interface.h" #include "textinput_interface_p.h" // Wayland #ifndef WL_SEAT_NAME_SINCE_VERSION #define WL_SEAT_NAME_SINCE_VERSION 2 #endif // linux #include #if HAVE_LINUX_INPUT_H #include #endif #include namespace KWayland { namespace Server { const quint32 SeatInterface::Private::s_version = 5; const qint32 SeatInterface::Private::s_pointerVersion = 5; const qint32 SeatInterface::Private::s_touchVersion = 5; const qint32 SeatInterface::Private::s_keyboardVersion = 5; SeatInterface::Private::Private(SeatInterface *q, Display *display) : Global::Private(display, &wl_seat_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct wl_seat_interface SeatInterface::Private::s_interface = { getPointerCallback, getKeyboardCallback, getTouchCallback, releaseCallback }; #endif SeatInterface::SeatInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &SeatInterface::nameChanged, this, [d] { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendName(*it); } } ); auto sendCapabilitiesAll = [d] { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendCapabilities(*it); } }; connect(this, &SeatInterface::hasPointerChanged, this, sendCapabilitiesAll); connect(this, &SeatInterface::hasKeyboardChanged, this, sendCapabilitiesAll); connect(this, &SeatInterface::hasTouchChanged, this, sendCapabilitiesAll); } SeatInterface::~SeatInterface() { Q_D(); while (!d->resources.isEmpty()) { wl_resource_destroy(d->resources.takeLast()); } } void SeatInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { wl_resource *r = wl_resource_create(client, &wl_seat_interface, qMin(s_version, version), id); if (!r) { wl_client_post_no_memory(client); return; } resources << r; wl_resource_set_implementation(r, &s_interface, this, unbind); sendCapabilities(r); sendName(r); } void SeatInterface::Private::unbind(wl_resource *r) { cast(r)->resources.removeAll(r); } void SeatInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void SeatInterface::Private::updatePointerButtonSerial(quint32 button, quint32 serial) { auto it = globalPointer.buttonSerials.find(button); if (it == globalPointer.buttonSerials.end()) { globalPointer.buttonSerials.insert(button, serial); return; } it.value() = serial; } void SeatInterface::Private::updatePointerButtonState(quint32 button, Pointer::State state) { auto it = globalPointer.buttonStates.find(button); if (it == globalPointer.buttonStates.end()) { globalPointer.buttonStates.insert(button, state); return; } it.value() = state; } bool SeatInterface::Private::updateKey(quint32 key, Keyboard::State state) { auto it = keys.states.find(key); if (it == keys.states.end()) { keys.states.insert(key, state); return true; } if (it.value() == state) { return false; } it.value() = state; return true; } void SeatInterface::Private::sendName(wl_resource *r) { if (wl_resource_get_version(r) < WL_SEAT_NAME_SINCE_VERSION) { return; } wl_seat_send_name(r, name.toUtf8().constData()); } void SeatInterface::Private::sendCapabilities(wl_resource *r) { uint32_t capabilities = 0; if (pointer) { capabilities |= WL_SEAT_CAPABILITY_POINTER; } if (keyboard) { capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; } if (touch) { capabilities |= WL_SEAT_CAPABILITY_TOUCH; } wl_seat_send_capabilities(r, capabilities); } SeatInterface::Private *SeatInterface::Private::cast(wl_resource *r) { return r ? reinterpret_cast(wl_resource_get_user_data(r)) : nullptr; } namespace { template static T *interfaceForSurface(SurfaceInterface *surface, const QVector &interfaces) { if (!surface) { return nullptr; } for (auto it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) { if ((*it)->client() == surface->client()) { return (*it); } } return nullptr; } template static QVector interfacesForSurface(SurfaceInterface *surface, const QVector &interfaces) { QVector ret; if (!surface) { return ret; } for (auto it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) { if ((*it)->client() == surface->client() && (*it)->resource()) { ret << *it; } } return ret; } template static bool forEachInterface(SurfaceInterface *surface, const QVector &interfaces, std::function method) { if (!surface) { return false; } bool calledAtLeastOne = false; for (auto it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) { if ((*it)->client() == surface->client() && (*it)->resource()) { method(*it); calledAtLeastOne = true; } } return calledAtLeastOne; } } QVector SeatInterface::Private::pointersForSurface(SurfaceInterface *surface) const { return interfacesForSurface(surface, pointers); } QVector SeatInterface::Private::keyboardsForSurface(SurfaceInterface *surface) const { return interfacesForSurface(surface, keyboards); } QVector SeatInterface::Private::touchsForSurface(SurfaceInterface *surface) const { return interfacesForSurface(surface, touchs); } DataDeviceInterface *SeatInterface::Private::dataDeviceForSurface(SurfaceInterface *surface) const { return interfaceForSurface(surface, dataDevices); } TextInputInterface *SeatInterface::Private::textInputForSurface(SurfaceInterface *surface) const { return interfaceForSurface(surface, textInputs); } void SeatInterface::Private::registerDataDevice(DataDeviceInterface *dataDevice) { Q_ASSERT(dataDevice->seat() == q); dataDevices << dataDevice; auto dataDeviceCleanup = [this, dataDevice] { dataDevices.removeOne(dataDevice); if (keys.focus.selection == dataDevice) { keys.focus.selection = nullptr; } if (currentSelection == dataDevice) { // current selection is cleared currentSelection = nullptr; emit q->selectionChanged(nullptr); if (keys.focus.selection) { keys.focus.selection->sendClearSelection(); } } }; QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup); QObject::connect(dataDevice, &Resource::unbound, q, dataDeviceCleanup); QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this, dataDevice] { updateSelection(dataDevice, true); } ); QObject::connect(dataDevice, &DataDeviceInterface::selectionCleared, q, [this, dataDevice] { updateSelection(dataDevice, false); } ); QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, q, [this, dataDevice] { const auto dragSerial = dataDevice->dragImplicitGrabSerial(); auto *dragSurface = dataDevice->origin(); if (q->hasImplicitPointerGrab(dragSerial)) { drag.mode = Drag::Mode::Pointer; drag.sourcePointer = interfaceForSurface(dragSurface, pointers); drag.transformation = globalPointer.focus.transformation; } else if (q->hasImplicitTouchGrab(dragSerial)) { drag.mode = Drag::Mode::Touch; drag.sourceTouch = interfaceForSurface(dragSurface, touchs); // TODO: touch transformation } else { // no implicit grab, abort drag return; } auto *originSurface = dataDevice->origin(); const bool proxied = originSurface->dataProxy(); if (!proxied) { // origin surface drag.target = dataDevice; drag.surface = originSurface; // TODO: transformation needs to be either pointer or touch drag.transformation = globalPointer.focus.transformation; } drag.source = dataDevice; drag.sourcePointer = interfaceForSurface(originSurface, pointers); drag.destroyConnection = QObject::connect(dataDevice, &QObject::destroyed, q, [this] { endDrag(display->nextSerial()); } ); if (dataDevice->dragSource()) { drag.dragSourceDestroyConnection = QObject::connect(dataDevice->dragSource(), &Resource::aboutToBeUnbound, q, [this] { const auto serial = display->nextSerial(); if (drag.target) { drag.target->updateDragTarget(nullptr, serial); drag.target = nullptr; } endDrag(serial); } ); } else { drag.dragSourceDestroyConnection = QMetaObject::Connection(); } dataDevice->updateDragTarget(proxied ? nullptr : originSurface, dataDevice->dragImplicitGrabSerial()); emit q->dragStarted(); emit q->dragSurfaceChanged(); } ); // is the new DataDevice for the current keyoard focus? if (keys.focus.surface && !keys.focus.selection) { // same client? if (keys.focus.surface->client() == dataDevice->client()) { keys.focus.selection = dataDevice; if (currentSelection && currentSelection->selection()) { dataDevice->sendSelection(currentSelection); } } } } void SeatInterface::Private::registerTextInput(TextInputInterface *ti) { // text input version 0 might call this multiple times if (textInputs.contains(ti)) { return; } textInputs << ti; if (textInput.focus.surface && textInput.focus.surface->client() == ti->client()) { // this is a text input for the currently focused text input surface if (!textInput.focus.textInput) { textInput.focus.textInput = ti; ti->d_func()->sendEnter(textInput.focus.surface, textInput.focus.serial); emit q->focusedTextInputChanged(); } } QObject::connect(ti, &QObject::destroyed, q, [this, ti] { textInputs.removeAt(textInputs.indexOf(ti)); if (textInput.focus.textInput == ti) { textInput.focus.textInput = nullptr; emit q->focusedTextInputChanged(); } } ); } void SeatInterface::Private::endDrag(quint32 serial) { auto target = drag.target; QObject::disconnect(drag.destroyConnection); QObject::disconnect(drag.dragSourceDestroyConnection); if (drag.source && drag.source->dragSource()) { drag.source->dragSource()->dropPerformed(); } if (target) { target->drop(); target->updateDragTarget(nullptr, serial); } drag = Drag(); emit q->dragSurfaceChanged(); emit q->dragEnded(); } void SeatInterface::Private::cancelPreviousSelection(DataDeviceInterface *dataDevice) { if (!currentSelection) { return; } if (auto s = currentSelection->selection()) { if (currentSelection != dataDevice) { // only if current selection is not on the same device // that would cancel the newly set source s->cancel(); } } } void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice, bool set) { bool selChanged = currentSelection != dataDevice; if (keys.focus.surface && (keys.focus.surface->client() == dataDevice->client())) { // cancel the previous selection cancelPreviousSelection(dataDevice); // new selection on a data device belonging to current keyboard focus currentSelection = dataDevice; } if (dataDevice == currentSelection) { // need to send out the selection if (keys.focus.selection) { if (set) { keys.focus.selection->sendSelection(dataDevice); } else { keys.focus.selection->sendClearSelection(); currentSelection = nullptr; selChanged = true; } } } if (selChanged) { emit q->selectionChanged(currentSelection); } } void SeatInterface::setHasKeyboard(bool has) { Q_D(); if (d->keyboard == has) { return; } d->keyboard = has; emit hasKeyboardChanged(d->keyboard); } void SeatInterface::setHasPointer(bool has) { Q_D(); if (d->pointer == has) { return; } d->pointer = has; emit hasPointerChanged(d->pointer); } void SeatInterface::setHasTouch(bool has) { Q_D(); if (d->touch == has) { return; } d->touch = has; emit hasTouchChanged(d->touch); } void SeatInterface::setName(const QString &name) { Q_D(); if (d->name == name) { return; } d->name = name; emit nameChanged(d->name); } void SeatInterface::Private::getPointerCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getPointer(client, resource, id); } void SeatInterface::Private::getPointer(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has pointer? PointerInterface *pointer = new PointerInterface(q, resource); auto clientConnection = display->getConnection(client); pointer->create(clientConnection, qMin(wl_resource_get_version(resource), s_pointerVersion), id); if (!pointer->resource()) { wl_resource_post_no_memory(resource); delete pointer; return; } pointers << pointer; if (globalPointer.focus.surface && globalPointer.focus.surface->client() == clientConnection) { // this is a pointer for the currently focused pointer surface globalPointer.focus.pointers << pointer; pointer->setFocusedSurface(globalPointer.focus.surface, globalPointer.focus.serial); pointer->d_func()->sendFrame(); if (globalPointer.focus.pointers.count() == 1) { // got a new pointer emit q->focusedPointerChanged(pointer); } } QObject::connect(pointer, &QObject::destroyed, q, [pointer,this] { pointers.removeAt(pointers.indexOf(pointer)); if (globalPointer.focus.pointers.removeOne(pointer)) { if (globalPointer.focus.pointers.isEmpty()) { emit q->focusedPointerChanged(nullptr); } } } ); emit q->pointerCreated(pointer); } void SeatInterface::Private::getKeyboardCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getKeyboard(client, resource, id); } void SeatInterface::Private::getKeyboard(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has keyboard? KeyboardInterface *keyboard = new KeyboardInterface(q, resource); auto clientConnection = display->getConnection(client); keyboard->create(clientConnection, qMin(wl_resource_get_version(resource), s_keyboardVersion) , id); if (!keyboard->resource()) { wl_resource_post_no_memory(resource); delete keyboard; return; } keyboard->repeatInfo(keys.keyRepeat.charactersPerSecond, keys.keyRepeat.delay); if (keys.keymap.xkbcommonCompatible) { keyboard->setKeymap(keys.keymap.fd, keys.keymap.size); } keyboards << keyboard; if (keys.focus.surface && keys.focus.surface->client() == clientConnection) { // this is a keyboard for the currently focused keyboard surface keys.focus.keyboards << keyboard; keyboard->setFocusedSurface(keys.focus.surface, keys.focus.serial); } QObject::connect(keyboard, &QObject::destroyed, q, [keyboard,this] { keyboards.removeAt(keyboards.indexOf(keyboard)); keys.focus.keyboards.removeOne(keyboard); } ); emit q->keyboardCreated(keyboard); } void SeatInterface::Private::getTouchCallback(wl_client *client, wl_resource *resource, uint32_t id) { cast(resource)->getTouch(client, resource, id); } void SeatInterface::Private::getTouch(wl_client *client, wl_resource *resource, uint32_t id) { // TODO: only create if seat has touch? TouchInterface *touch = new TouchInterface(q, resource); auto clientConnection = display->getConnection(client); touch->create(clientConnection, qMin(wl_resource_get_version(resource), s_touchVersion), id); if (!touch->resource()) { wl_resource_post_no_memory(resource); delete touch; return; } touchs << touch; if (globalTouch.focus.surface && globalTouch.focus.surface->client() == clientConnection) { // this is a touch for the currently focused touch surface globalTouch.focus.touchs << touch; if (!globalTouch.ids.isEmpty()) { // TODO: send out all the points } } QObject::connect(touch, &QObject::destroyed, q, [touch,this] { touchs.removeAt(touchs.indexOf(touch)); globalTouch.focus.touchs.removeOne(touch); } ); emit q->touchCreated(touch); } QString SeatInterface::name() const { Q_D(); return d->name; } bool SeatInterface::hasPointer() const { Q_D(); return d->pointer; } bool SeatInterface::hasKeyboard() const { Q_D(); return d->keyboard; } bool SeatInterface::hasTouch() const { Q_D(); return d->touch; } SeatInterface *SeatInterface::get(wl_resource *native) { return Private::get(native); } SeatInterface::Private *SeatInterface::d_func() const { return reinterpret_cast(d.data()); } QPointF SeatInterface::pointerPos() const { Q_D(); return d->globalPointer.pos; } void SeatInterface::setPointerPos(const QPointF &pos) { Q_D(); if (d->globalPointer.pos == pos) { return; } d->globalPointer.pos = pos; emit pointerPosChanged(pos); } quint32 SeatInterface::timestamp() const { Q_D(); return d->timestamp; } void SeatInterface::setTimestamp(quint32 time) { Q_D(); if (d->timestamp == time) { return; } d->timestamp = time; emit timestampChanged(time); } void SeatInterface::setDragTarget(SurfaceInterface *surface, const QPointF &globalPosition, const QMatrix4x4 &inputTransformation) { Q_D(); if (surface == d->drag.surface) { // no change return; } const quint32 serial = d->display->nextSerial(); if (d->drag.target) { d->drag.target->updateDragTarget(nullptr, serial); } d->drag.target = d->dataDeviceForSurface(surface); if (d->drag.mode == Private::Drag::Mode::Pointer) { setPointerPos(globalPosition); } else if (d->drag.mode == Private::Drag::Mode::Touch && d->globalTouch.focus.firstTouchPos != globalPosition) { touchMove(d->globalTouch.ids.first(), globalPosition); } if (d->drag.target) { d->drag.surface = surface; d->drag.transformation = inputTransformation; d->drag.target->updateDragTarget(surface, serial); } else { d->drag.surface = nullptr; } emit dragSurfaceChanged(); return; } void SeatInterface::setDragTarget(SurfaceInterface *surface, const QMatrix4x4 &inputTransformation) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { setDragTarget(surface, pointerPos(), inputTransformation); } else { Q_ASSERT(d->drag.mode == Private::Drag::Mode::Touch); setDragTarget(surface, d->globalTouch.focus.firstTouchPos, inputTransformation); } } SurfaceInterface *SeatInterface::focusedPointerSurface() const { Q_D(); return d->globalPointer.focus.surface; } void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QPointF &surfacePosition) { QMatrix4x4 m; m.translate(-surfacePosition.x(), -surfacePosition.y()); setFocusedPointerSurface(surface, m); Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.offset = surfacePosition; } } void SeatInterface::setFocusedPointerSurface(SurfaceInterface *surface, const QMatrix4x4 &transformation) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } const quint32 serial = d->display->nextSerial(); QSet framePointers; for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(nullptr, serial); framePointers << *it; } if (d->globalPointer.focus.surface) { disconnect(d->globalPointer.focus.destroyConnection); } d->globalPointer.focus = Private::Pointer::Focus(); d->globalPointer.focus.surface = surface; auto p = d->pointersForSurface(surface); d->globalPointer.focus.pointers = p; if (d->globalPointer.focus.surface) { d->globalPointer.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); d->globalPointer.focus = Private::Pointer::Focus(); emit focusedPointerChanged(nullptr); } ); d->globalPointer.focus.offset = QPointF(); d->globalPointer.focus.transformation = transformation; d->globalPointer.focus.serial = serial; } if (p.isEmpty()) { emit focusedPointerChanged(nullptr); for (auto p : qAsConst(framePointers)) { p->d_func()->sendFrame(); } return; } // TODO: signal with all pointers emit focusedPointerChanged(p.first()); for (auto it = p.constBegin(), end = p.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(surface, serial); framePointers << *it; } for (auto p : qAsConst(framePointers)) { p->d_func()->sendFrame(); } } PointerInterface *SeatInterface::focusedPointer() const { Q_D(); if (d->globalPointer.focus.pointers.isEmpty()) { return nullptr; } return d->globalPointer.focus.pointers.first(); } void SeatInterface::setFocusedPointerSurfacePosition(const QPointF &surfacePosition) { Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.offset = surfacePosition; d->globalPointer.focus.transformation = QMatrix4x4(); d->globalPointer.focus.transformation.translate(-surfacePosition.x(), -surfacePosition.y()); } } QPointF SeatInterface::focusedPointerSurfacePosition() const { Q_D(); return d->globalPointer.focus.offset; } void SeatInterface::setFocusedPointerSurfaceTransformation(const QMatrix4x4 &transformation) { Q_D(); if (d->globalPointer.focus.surface) { d->globalPointer.focus.transformation = transformation; } } QMatrix4x4 SeatInterface::focusedPointerSurfaceTransformation() const { Q_D(); return d->globalPointer.focus.transformation; } namespace { static quint32 qtToWaylandButton(Qt::MouseButton button) { #if HAVE_LINUX_INPUT_H static const QHash s_buttons({ {Qt::LeftButton, BTN_LEFT}, {Qt::RightButton, BTN_RIGHT}, {Qt::MiddleButton, BTN_MIDDLE}, {Qt::ExtraButton1, BTN_BACK}, // note: QtWayland maps BTN_SIDE {Qt::ExtraButton2, BTN_FORWARD}, // note: QtWayland maps BTN_EXTRA {Qt::ExtraButton3, BTN_TASK}, // note: QtWayland maps BTN_FORWARD {Qt::ExtraButton4, BTN_EXTRA}, // note: QtWayland maps BTN_BACK {Qt::ExtraButton5, BTN_SIDE}, // note: QtWayland maps BTN_TASK {Qt::ExtraButton6, BTN_TASK + 1}, {Qt::ExtraButton7, BTN_TASK + 2}, {Qt::ExtraButton8, BTN_TASK + 3}, {Qt::ExtraButton9, BTN_TASK + 4}, {Qt::ExtraButton10, BTN_TASK + 5}, {Qt::ExtraButton11, BTN_TASK + 6}, {Qt::ExtraButton12, BTN_TASK + 7}, {Qt::ExtraButton13, BTN_TASK + 8} // further mapping not possible, 0x120 is BTN_JOYSTICK }); return s_buttons.value(button, 0); #else return 0; #endif } } bool SeatInterface::isPointerButtonPressed(Qt::MouseButton button) const { return isPointerButtonPressed(qtToWaylandButton(button)); } bool SeatInterface::isPointerButtonPressed(quint32 button) const { Q_D(); auto it = d->globalPointer.buttonStates.constFind(button); if (it == d->globalPointer.buttonStates.constEnd()) { return false; } return it.value() == Private::Pointer::State::Pressed ? true : false; } void SeatInterface::pointerAxisV5(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->axis(orientation, delta, discreteDelta, source); } } } void SeatInterface::pointerAxis(Qt::Orientation orientation, quint32 delta) { Q_D(); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->axis(orientation, delta); } } } void SeatInterface::pointerButtonPressed(Qt::MouseButton button) { const quint32 nativeButton = qtToWaylandButton(button); if (nativeButton == 0) { return; } pointerButtonPressed(nativeButton); } void SeatInterface::pointerButtonPressed(quint32 button) { Q_D(); const quint32 serial = d->display->nextSerial(); d->updatePointerButtonSerial(button, serial); d->updatePointerButtonState(button, Private::Pointer::State::Pressed); if (d->drag.mode == Private::Drag::Mode::Pointer) { // ignore return; } if (auto *focusSurface = d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->buttonPressed(button, serial); } if (focusSurface == d->keys.focus.surface) { // update the focused child surface auto p = focusedPointer(); if (p) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->d_func()->focusChildSurface(p->d_func()->focusedChildSurface, serial); } } } } } void SeatInterface::pointerButtonReleased(Qt::MouseButton button) { const quint32 nativeButton = qtToWaylandButton(button); if (nativeButton == 0) { return; } pointerButtonReleased(nativeButton); } void SeatInterface::pointerButtonReleased(quint32 button) { Q_D(); const quint32 serial = d->display->nextSerial(); const quint32 currentButtonSerial = pointerButtonSerial(button); d->updatePointerButtonSerial(button, serial); d->updatePointerButtonState(button, Private::Pointer::State::Released); if (d->drag.mode == Private::Drag::Mode::Pointer) { if (d->drag.source->dragImplicitGrabSerial() != currentButtonSerial) { // not our drag button - ignore return; } d->endDrag(serial); return; } if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->buttonReleased(button, serial); } } } quint32 SeatInterface::pointerButtonSerial(Qt::MouseButton button) const { return pointerButtonSerial(qtToWaylandButton(button)); } quint32 SeatInterface::pointerButtonSerial(quint32 button) const { Q_D(); auto it = d->globalPointer.buttonSerials.constFind(button); if (it == d->globalPointer.buttonSerials.constEnd()) { return 0; } return it.value(); } void SeatInterface::relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds) { Q_D(); if (d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->relativeMotion(delta, deltaNonAccelerated, microseconds); } } } void SeatInterface::startPointerSwipeGesture(quint32 fingerCount) { Q_D(); if (!d->globalPointer.gestureSurface.isNull()) { return; } d->globalPointer.gestureSurface = QPointer(d->globalPointer.focus.surface); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial, fingerCount] (PointerInterface *p) { p->d_func()->startSwipeGesture(serial, fingerCount); } ); } void SeatInterface::updatePointerSwipeGesture(const QSizeF &delta) { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [delta] (PointerInterface *p) { p->d_func()->updateSwipeGesture(delta); } ); } void SeatInterface::endPointerSwipeGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->endSwipeGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::cancelPointerSwipeGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->cancelSwipeGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::startPointerPinchGesture(quint32 fingerCount) { Q_D(); if (!d->globalPointer.gestureSurface.isNull()) { return; } d->globalPointer.gestureSurface = QPointer(d->globalPointer.focus.surface); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial, fingerCount] (PointerInterface *p) { p->d_func()->startPinchGesture(serial, fingerCount); } ); } void SeatInterface::updatePointerPinchGesture(const QSizeF &delta, qreal scale, qreal rotation) { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [delta, scale, rotation] (PointerInterface *p) { p->d_func()->updatePinchGesture(delta, scale, rotation); } ); } void SeatInterface::endPointerPinchGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->endPinchGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::cancelPointerPinchGesture() { Q_D(); if (d->globalPointer.gestureSurface.isNull()) { return; } const quint32 serial = d->display->nextSerial(); forEachInterface(d->globalPointer.gestureSurface.data(), d->pointers, [serial] (PointerInterface *p) { p->d_func()->cancelPinchGesture(serial); } ); d->globalPointer.gestureSurface.clear(); } void SeatInterface::keyPressed(quint32 key) { Q_D(); d->keys.lastStateSerial = d->display->nextSerial(); if (!d->updateKey(key, Private::Keyboard::State::Pressed)) { return; } if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->keyPressed(key, d->keys.lastStateSerial); } } } void SeatInterface::keyReleased(quint32 key) { Q_D(); d->keys.lastStateSerial = d->display->nextSerial(); if (!d->updateKey(key, Private::Keyboard::State::Released)) { return; } if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->keyReleased(key, d->keys.lastStateSerial); } } } SurfaceInterface *SeatInterface::focusedKeyboardSurface() const { Q_D(); return d->keys.focus.surface; } void SeatInterface::setFocusedKeyboardSurface(SurfaceInterface *surface) { Q_D(); const quint32 serial = d->display->nextSerial(); for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(nullptr, serial); } if (d->keys.focus.surface) { disconnect(d->keys.focus.destroyConnection); } d->keys.focus = Private::Keyboard::Focus(); d->keys.focus.surface = surface; d->keys.focus.keyboards = d->keyboardsForSurface(surface); if (d->keys.focus.surface) { d->keys.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); d->keys.focus = Private::Keyboard::Focus(); } ); d->keys.focus.serial = serial; // selection? d->keys.focus.selection = d->dataDeviceForSurface(surface); if (d->keys.focus.selection) { if (d->currentSelection && d->currentSelection->selection()) { d->keys.focus.selection->sendSelection(d->currentSelection); } else { d->keys.focus.selection->sendClearSelection(); } } } for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(surface, serial); } // focused text input surface follows keyboard if (hasKeyboard()) { setFocusedTextInputSurface(surface); } } void SeatInterface::setKeymap(int fd, quint32 size) { Q_D(); d->keys.keymap.xkbcommonCompatible = true; d->keys.keymap.fd = fd; d->keys.keymap.size = size; for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { (*it)->setKeymap(fd, size); } } void SeatInterface::updateKeyboardModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group) { Q_D(); bool changed = false; #define UPDATE( value ) \ if (d->keys.modifiers.value != value) { \ d->keys.modifiers.value = value; \ changed = true; \ } UPDATE(depressed) UPDATE(latched) UPDATE(locked) UPDATE(group) if (!changed) { return; } const quint32 serial = d->display->nextSerial(); d->keys.modifiers.serial = serial; if (d->keys.focus.surface) { for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->updateModifiers(depressed, latched, locked, group, serial); } } } void SeatInterface::setKeyRepeatInfo(qint32 charactersPerSecond, qint32 delay) { Q_D(); d->keys.keyRepeat.charactersPerSecond = qMax(charactersPerSecond, 0); d->keys.keyRepeat.delay = qMax(delay, 0); for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { (*it)->repeatInfo(d->keys.keyRepeat.charactersPerSecond, d->keys.keyRepeat.delay); } } qint32 SeatInterface::keyRepeatDelay() const { Q_D(); return d->keys.keyRepeat.delay; } qint32 SeatInterface::keyRepeatRate() const { Q_D(); return d->keys.keyRepeat.charactersPerSecond; } bool SeatInterface::isKeymapXkbCompatible() const { Q_D(); return d->keys.keymap.xkbcommonCompatible; } int SeatInterface::keymapFileDescriptor() const { Q_D(); return d->keys.keymap.fd; } quint32 SeatInterface::keymapSize() const { Q_D(); return d->keys.keymap.size; } quint32 SeatInterface::depressedModifiers() const { Q_D(); return d->keys.modifiers.depressed; } quint32 SeatInterface::groupModifiers() const { Q_D(); return d->keys.modifiers.group; } quint32 SeatInterface::latchedModifiers() const { Q_D(); return d->keys.modifiers.latched; } quint32 SeatInterface::lockedModifiers() const { Q_D(); return d->keys.modifiers.locked; } quint32 SeatInterface::lastModifiersSerial() const { Q_D(); return d->keys.modifiers.serial; } QVector< quint32 > SeatInterface::pressedKeys() const { Q_D(); QVector keys; for (auto it = d->keys.states.constBegin(); it != d->keys.states.constEnd(); ++it) { if (it.value() == Private::Keyboard::State::Pressed) { keys << it.key(); } } return keys; } KeyboardInterface *SeatInterface::focusedKeyboard() const { Q_D(); if (d->keys.focus.keyboards.isEmpty()) { return nullptr; } return d->keys.focus.keyboards.first(); } void SeatInterface::cancelTouchSequence() { Q_D(); for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->cancel(); } if (d->drag.mode == Private::Drag::Mode::Touch) { // cancel the drag, don't drop. if (d->drag.target) { // remove the current target d->drag.target->updateDragTarget(nullptr, 0); d->drag.target = nullptr; } // and end the drag for the source, serial does not matter d->endDrag(0); } d->globalTouch.ids.clear(); } TouchInterface *SeatInterface::focusedTouch() const { Q_D(); if (d->globalTouch.focus.touchs.isEmpty()) { return nullptr; } return d->globalTouch.focus.touchs.first(); } SurfaceInterface *SeatInterface::focusedTouchSurface() const { Q_D(); return d->globalTouch.focus.surface; } QPointF SeatInterface::focusedTouchSurfacePosition() const { Q_D(); return d->globalTouch.focus.offset; } bool SeatInterface::isTouchSequence() const { Q_D(); return !d->globalTouch.ids.isEmpty(); } void SeatInterface::setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition) { if (isTouchSequence()) { // changing surface not allowed during a touch sequence return; } Q_ASSERT(!isDragTouch()); Q_D(); if (d->globalTouch.focus.surface) { disconnect(d->globalTouch.focus.destroyConnection); } d->globalTouch.focus = Private::Touch::Focus(); d->globalTouch.focus.surface = surface; d->globalTouch.focus.offset = surfacePosition; d->globalTouch.focus.touchs = d->touchsForSurface(surface); if (d->globalTouch.focus.surface) { d->globalTouch.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this] { Q_D(); if (isTouchSequence()) { // Surface destroyed during touch sequence - send a cancel for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->cancel(); } } d->globalTouch.focus = Private::Touch::Focus(); } ); } } void SeatInterface::setFocusedTouchSurfacePosition(const QPointF &surfacePosition) { Q_D(); d->globalTouch.focus.offset = surfacePosition; } qint32 SeatInterface::touchDown(const QPointF &globalPosition) { Q_D(); const qint32 id = d->globalTouch.ids.isEmpty() ? 0 : d->globalTouch.ids.lastKey() + 1; const qint32 serial = display()->nextSerial(); const auto pos = globalPosition - d->globalTouch.focus.offset; for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->down(id, serial, pos); } if (id == 0) { d->globalTouch.focus.firstTouchPos = globalPosition; } #if HAVE_LINUX_INPUT_H if (id == 0 && d->globalTouch.focus.touchs.isEmpty()) { // If the client did not bind the touch interface fall back // to at least emulating touch through pointer events. forEachInterface(focusedTouchSurface(), d->pointers, [this, pos, serial] (PointerInterface *p) { wl_pointer_send_enter(p->resource(), serial, focusedTouchSurface()->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); wl_pointer_send_motion(p->resource(), timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); wl_pointer_send_button(p->resource(), serial, timestamp(), BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); p->d_func()->sendFrame(); } ); } #endif d->globalTouch.ids[id] = serial; return id; } void SeatInterface::touchMove(qint32 id, const QPointF &globalPosition) { Q_D(); Q_ASSERT(d->globalTouch.ids.contains(id)); const auto pos = globalPosition - d->globalTouch.focus.offset; for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->move(id, pos); } if (id == 0) { d->globalTouch.focus.firstTouchPos = globalPosition; } if (id == 0 && d->globalTouch.focus.touchs.isEmpty()) { // Client did not bind touch, fall back to emulating with pointer events. forEachInterface(focusedTouchSurface(), d->pointers, [this, pos] (PointerInterface *p) { wl_pointer_send_motion(p->resource(), timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } ); } emit touchMoved(id, d->globalTouch.ids[id], globalPosition); } void SeatInterface::touchUp(qint32 id) { Q_D(); Q_ASSERT(d->globalTouch.ids.contains(id)); const qint32 serial = display()->nextSerial(); if (d->drag.mode == Private::Drag::Mode::Touch && d->drag.source->dragImplicitGrabSerial() == d->globalTouch.ids.value(id)) { // the implicitly grabbing touch point has been upped d->endDrag(serial); } for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->up(id, serial); } #if HAVE_LINUX_INPUT_H if (id == 0 && d->globalTouch.focus.touchs.isEmpty()) { // Client did not bind touch, fall back to emulating with pointer events. const quint32 serial = display()->nextSerial(); forEachInterface(focusedTouchSurface(), d->pointers, [this, serial] (PointerInterface *p) { wl_pointer_send_button(p->resource(), serial, timestamp(), BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); } ); } #endif d->globalTouch.ids.remove(id); } void SeatInterface::touchFrame() { Q_D(); for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->frame(); } } bool SeatInterface::hasImplicitTouchGrab(quint32 serial) const { Q_D(); if (!d->globalTouch.focus.surface) { // origin surface has been destroyed return false; } return d->globalTouch.ids.key(serial, -1) != -1; } bool SeatInterface::isDrag() const { Q_D(); return d->drag.mode != Private::Drag::Mode::None; } bool SeatInterface::isDragPointer() const { Q_D(); return d->drag.mode == Private::Drag::Mode::Pointer; } bool SeatInterface::isDragTouch() const { Q_D(); return d->drag.mode == Private::Drag::Mode::Touch; } bool SeatInterface::hasImplicitPointerGrab(quint32 serial) const { Q_D(); const auto &serials = d->globalPointer.buttonSerials; for (auto it = serials.constBegin(), end = serials.constEnd(); it != end; it++) { if (it.value() == serial) { return isPointerButtonPressed(it.key()); } } return false; } QMatrix4x4 SeatInterface::dragSurfaceTransformation() const { Q_D(); return d->drag.transformation; } SurfaceInterface *SeatInterface::dragSurface() const { Q_D(); return d->drag.surface; } PointerInterface *SeatInterface::dragPointer() const { Q_D(); if (d->drag.mode != Private::Drag::Mode::Pointer) { return nullptr; } return d->drag.sourcePointer; } DataDeviceInterface *SeatInterface::dragSource() const { Q_D(); return d->drag.source; } void SeatInterface::setFocusedTextInputSurface(SurfaceInterface *surface) { Q_D(); const quint32 serial = d->display->nextSerial(); const auto old = d->textInput.focus.textInput; if (d->textInput.focus.textInput) { // TODO: setFocusedSurface like in other interfaces d->textInput.focus.textInput->d_func()->sendLeave(serial, d->textInput.focus.surface); } if (d->textInput.focus.surface) { disconnect(d->textInput.focus.destroyConnection); } d->textInput.focus = Private::TextInput::Focus(); d->textInput.focus.surface = surface; TextInputInterface *t = d->textInputForSurface(surface); if (t && !t->resource()) { t = nullptr; } d->textInput.focus.textInput = t; if (d->textInput.focus.surface) { d->textInput.focus.destroyConnection = connect(surface, &Resource::aboutToBeUnbound, this, [this] { setFocusedTextInputSurface(nullptr); } ); d->textInput.focus.serial = serial; } if (t) { // TODO: setFocusedSurface like in other interfaces t->d_func()->sendEnter(surface, serial); } if (old != t) { emit focusedTextInputChanged(); } } SurfaceInterface *SeatInterface::focusedTextInputSurface() const { Q_D(); return d->textInput.focus.surface; } TextInputInterface *SeatInterface::focusedTextInput() const { Q_D(); return d->textInput.focus.textInput; } DataDeviceInterface *SeatInterface::selection() const { Q_D(); return d->currentSelection; } void SeatInterface::setSelection(DataDeviceInterface *dataDevice) { Q_D(); if (d->currentSelection == dataDevice) { return; } // cancel the previous selection d->cancelPreviousSelection(dataDevice); d->currentSelection = dataDevice; if (d->keys.focus.selection) { if (dataDevice && dataDevice->selection()) { d->keys.focus.selection->sendSelection(dataDevice); } else { d->keys.focus.selection->sendClearSelection(); } } emit selectionChanged(dataDevice); } } } diff --git a/src/server/seat_interface.h b/src/server/seat_interface.h index 972b5f9..c305afe 100644 --- a/src/server/seat_interface.h +++ b/src/server/seat_interface.h @@ -1,778 +1,764 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SEAT_INTERFACE_H #define WAYLAND_SERVER_SEAT_INTERFACE_H #include #include #include #include #include "global.h" #include "keyboard_interface.h" #include "pointer_interface.h" #include "touch_interface.h" struct wl_client; struct wl_resource; namespace KWayland { namespace Server { class DataDeviceInterface; class Display; class SurfaceInterface; class TextInputInterface; /** * Describes the source types for axis events. This indicates to the * client how an axis event was physically generated; a client may * adjust the user interface accordingly. For example, scroll events * from a "finger" source may be in a smooth coordinate space with * kinetic scrolling whereas a "wheel" source may be in discrete steps * of a number of lines. * * The "continuous" axis source is a device generating events in a * continuous coordinate space, but using something other than a * finger. One example for this source is button-based scrolling where * the vertical motion of a device is converted to scroll events while * a button is held down. * * The "wheel tilt" axis source indicates that the actual device is a * wheel but the scroll event is not caused by a rotation but a * (usually sideways) tilt of the wheel. * * @since 5.59 **/ enum class PointerAxisSource { Unknown, Wheel, Finger, Continuous, WheelTilt }; /** * @brief Represents a Seat on the Wayland Display. * * A Seat is a set of input devices (e.g. Keyboard, Pointer and Touch) the client can connect * to. The server needs to announce which input devices are supported and passes dedicated input * focus to a SurfaceInterface. Only the focused surface receives input events. * * The SeatInterface internally handles enter and release events when setting a focused surface. * Also it handles input translation from global to the local coordination, removing the need from * the user of the API to track the focused surfaces and can just interact with this class. * * To create a SeatInterface use @link Display::createSeat @endlink. Then one can set up what is * supported. Last but not least create needs to be called. * * @code * SeatInterface *seat = display->createSeat(); * // set up * seat->setName(QStringLiteral("seat0")); * seat->setHasPointer(true); * seat->setHasKeyboard(true); * seat->setHasTouch(false); * // now fully create * seat->create(); * @endcode * * To forward input events one needs to set the focused surface, update time stamp and then * forward the actual events: * * @code * // example for pointer * seat->setFocusedPointerSurface(surface, QPointF(100, 200)); // surface at it's global position * seat->setTimestamp(100); * seat->setPointerPos(QPointF(350, 210)); // global pos, local pos in surface: 250,10 * seat->setTimestamp(110); * seat->pointerButtonPressed(Qt::LeftButton); * seat->setTimestamp(120); * seat->pointerButtonReleased(Qt::LeftButton); * @endcode * * @see KeyboardInterface * @see PointerInterface * @see TouchInterface * @see SurfaceInterface **/ class KWAYLANDSERVER_EXPORT SeatInterface : public Global { Q_OBJECT /** * The name of the Seat **/ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) /** * Whether the SeatInterface supports a pointer device. **/ Q_PROPERTY(bool pointer READ hasPointer WRITE setHasPointer NOTIFY hasPointerChanged) /** * Whether the SeatInterface supports a keyboard device. **/ Q_PROPERTY(bool keyboard READ hasKeyboard WRITE setHasKeyboard NOTIFY hasKeyboardChanged) /** * Whether the SeatInterface supports a touch device. * @deprecated Since 5.5, use touch **/ Q_PROPERTY(bool tourch READ hasTouch WRITE setHasTouch NOTIFY hasTouchChanged) /** * Whether the SeatInterface supports a touch device. **/ Q_PROPERTY(bool touch READ hasTouch WRITE setHasTouch NOTIFY hasTouchChanged) /** * The global pointer position. **/ Q_PROPERTY(QPointF pointerPos READ pointerPos WRITE setPointerPos NOTIFY pointerPosChanged) /** * The current timestamp passed to the input events. **/ Q_PROPERTY(quint32 timestamp READ timestamp WRITE setTimestamp NOTIFY timestampChanged) public: virtual ~SeatInterface(); QString name() const; bool hasPointer() const; bool hasKeyboard() const; bool hasTouch() const; void setName(const QString &name); void setHasPointer(bool has); void setHasKeyboard(bool has); void setHasTouch(bool has); void setTimestamp(quint32 time); quint32 timestamp() const; /** * @name Drag'n'Drop related methods **/ ///@{ /** * @returns whether there is currently a drag'n'drop going on. * @since 5.6 * @see isDragPointer * @see isDragTouch * @see dragStarted * @see dragEnded **/ bool isDrag() const; /** * @returns whether the drag'n'drop is operated through the pointer device * @since 5.6 * @see isDrag * @see isDragTouch **/ bool isDragPointer() const; /** * @returns whether the drag'n'drop is operated through the touch device * @since 5.6 * @see isDrag * @see isDragPointer **/ bool isDragTouch() const; /** * @returns The transformation applied to go from global to local coordinates for drag motion events. * @see dragSurfaceTransformation * @since 5.6 **/ QMatrix4x4 dragSurfaceTransformation() const; /** * @returns The currently focused Surface for drag motion events. * @since 5.6 * @see dragSurfaceTransformation * @see dragSurfaceChanged **/ SurfaceInterface *dragSurface() const; /** * @returns The PointerInterface which triggered the drag operation * @since 5.6 * @see isDragPointer **/ PointerInterface *dragPointer() const; /** * @returns The DataDeviceInterface which started the drag and drop operation. * @see isDrag * @since 5.6 **/ DataDeviceInterface *dragSource() const; /** * Sets the current drag target to @p surface. * * Sends a drag leave event to the current target and an enter event to @p surface. * The enter position is derived from @p globalPosition and transformed by @p inputTransformation. * @since 5.6 **/ void setDragTarget(SurfaceInterface *surface, const QPointF &globalPosition, const QMatrix4x4 &inputTransformation); /** * Sets the current drag target to @p surface. * * Sends a drag leave event to the current target and an enter event to @p surface. * The enter position is derived from current global position and transformed by @p inputTransformation. * @since 5.6 **/ void setDragTarget(SurfaceInterface *surface, const QMatrix4x4 &inputTransformation = QMatrix4x4()); ///@} /** * @name Pointer related methods **/ ///@{ /** * Updates the global pointer @p pos. * * Sends a pointer motion event to the focused pointer surface. **/ void setPointerPos(const QPointF &pos); /** * @returns the global pointer position **/ QPointF pointerPos() const; /** * Sets the focused pointer @p surface. * All pointer events will be sent to the @p surface till a new focused pointer surface gets * installed. When the focus pointer surface changes a leave event is sent to the previous * focused surface. * * To unset the focused pointer surface pass @c nullptr as @p surface. * * Pointer motion events are adjusted to the local position based on the @p surfacePosition. * If the surface changes it's position in the global coordinate system * use setFocusedPointerSurfacePosition to update. * The surface position is used to create the base transformation matrix to go from global * to surface local coordinates. The default generated matrix is a translation with * negative @p surfacePosition. * * @param surface The surface which should become the new focused pointer surface. * @param surfacePosition The position of the surface in the global coordinate system * * @see setPointerPos * @see focucedPointerSurface * @see focusedPointer * @see setFocusedPointerSurfacePosition * @see focusedPointerSurfacePosition * @see setFocusedPointerSurfaceTransformation * @see focusedPointerSurfaceTransformation **/ void setFocusedPointerSurface(SurfaceInterface *surface, const QPointF &surfacePosition = QPoint()); /** * Sets the focused pointer @p surface. * All pointer events will be sent to the @p surface till a new focused pointer surface gets * installed. When the focus pointer surface changes a leave event is sent to the previous * focused surface. * * To unset the focused pointer surface pass @c nullptr as @p surface. * * Pointer motion events are adjusted to the local position based on the @p transformation. * If the surface changes it's position in the global coordinate system * use setFocusedPointerSurfaceTransformation to update. * * @param surface The surface which should become the new focused pointer surface. * @param transformation The transformation to transform global into local coordinates * * @see setPointerPos * @see focucedPointerSurface * @see focusedPointer * @see setFocusedPointerSurfacePosition * @see focusedPointerSurfacePosition * @see setFocusedPointerSurfaceTransformation * @see focusedPointerSurfaceTransformation * @since 5.6 **/ void setFocusedPointerSurface(SurfaceInterface *surface, const QMatrix4x4 &transformation); /** * @returns The currently focused pointer surface, that is the surface receiving pointer events. * @see setFocusedPointerSurface **/ SurfaceInterface *focusedPointerSurface() const; /** * @returns The PointerInterface belonging to the focused pointer surface, if any. * @see setFocusedPointerSurface **/ PointerInterface *focusedPointer() const; /** * Updates the global position of the currently focused pointer surface. * * Updating the focused surface position also generates a new transformation matrix. * The default generated matrix is a translation with negative @p surfacePosition. * If a different transformation is required a dedicated call to * @link setFocusedPointerSurfaceTransformation is required. * * @param surfacePosition The new global position of the focused pointer surface * @see focusedPointerSurface * @see setFocusedPointerSurface * @see focusedPointerSurfaceTransformation * @see setFocusedPointerSurfaceTransformation **/ void setFocusedPointerSurfacePosition(const QPointF &surfacePosition); /** * @returns The position of the focused pointer surface in global coordinates. * @see setFocusedPointerSurfacePosition * @see setFocusedPointerSurface * @see focusedPointerSurfaceTransformation **/ QPointF focusedPointerSurfacePosition() const; /** * Sets the @p transformation for going from global to local coordinates. * * The default transformation gets generated from the surface position and reset whenever * the surface position changes. * * @see focusedPointerSurfaceTransformation * @see focusedPointerSurfacePosition * @see setFocusedPointerSurfacePosition * @since 5.6 **/ void setFocusedPointerSurfaceTransformation(const QMatrix4x4 &transformation); /** * @returns The transformation applied to pointer position to go from global to local coordinates. * @see setFocusedPointerSurfaceTransformation * @since 5.6 **/ QMatrix4x4 focusedPointerSurfaceTransformation() const; /** * Marks the @p button as pressed. * * If there is a focused pointer surface a button pressed event is sent to it. * * @param button The Linux button code **/ void pointerButtonPressed(quint32 button); /** * @overload **/ void pointerButtonPressed(Qt::MouseButton button); /** * Marks the @p button as released. * * If there is a focused pointer surface a button release event is sent to it. * * @param button The Linux button code **/ void pointerButtonReleased(quint32 button); /** * @overload **/ void pointerButtonReleased(Qt::MouseButton button); /** * @returns whether the @p button is pressed **/ bool isPointerButtonPressed(quint32 button) const; /** * @returns whether the @p button is pressed **/ bool isPointerButtonPressed(Qt::MouseButton button) const; /** * @returns the last serial for @p button. **/ quint32 pointerButtonSerial(quint32 button) const; /** * @returns the last serial for @p button. **/ quint32 pointerButtonSerial(Qt::MouseButton button) const; /** * Sends axis events to the currently focused pointer surface. * * @param orientation The scroll axis. * @param delta The length of a vector along the specified axis @p orientation. * @param discreteDelta The number of discrete steps, e.g. mouse wheel clicks. * @param source Describes how the axis event was physically generated. * @since 5.59 * @todo Drop V5 suffix with KF6. **/ void pointerAxisV5(Qt::Orientation orientation, qreal delta, qint32 discreteDelta, PointerAxisSource source); /** * @see pointerAxisV5 **/ void pointerAxis(Qt::Orientation orientation, quint32 delta); /** * @returns true if there is a pressed button with the given @p serial * @since 5.6 **/ bool hasImplicitPointerGrab(quint32 serial) const; /** * A relative motion is in the same dimension as regular motion events, * except they do not represent an absolute position. For example, * moving a pointer from (x, y) to (x', y') would have the equivalent * relative motion (x' - x, y' - y). If a pointer motion caused the * absolute pointer position to be clipped by for example the edge of the * monitor, the relative motion is unaffected by the clipping and will * represent the unclipped motion. * * This method also contains non-accelerated motion deltas (@p deltaNonAccelerated). * The non-accelerated delta is, when applicable, the regular pointer motion * delta as it was before having applied motion acceleration and other * transformations such as normalization. * * Note that the non-accelerated delta does not represent 'raw' events as * they were read from some device. Pointer motion acceleration is device- * and configuration-specific and non-accelerated deltas and accelerated * deltas may have the same value on some devices. * * Relative motions are not coupled to wl_pointer.motion events (see {@link setPointerPos}, * and can be sent in combination with such events, but also independently. There may * also be scenarios where wl_pointer.motion is sent, but there is no * relative motion. The order of an absolute and relative motion event * originating from the same physical motion is not guaranteed. * * Sending relative pointer events only makes sense if the RelativePointerManagerInterface * is created on the Display. * * @param delta Motion vector * @param deltaNonAccelerated non-accelerated motion vector * @param microseconds timestamp with microseconds granularity * @see setPointerPos * @since 5.28 **/ void relativePointerMotion(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 microseconds); /** * Starts a multi-finger swipe gesture for the currently focused pointer surface. * * Such gestures are normally reported through dedicated input devices such as touchpads. * * The gesture is usually initiated by multiple fingers moving in the * same direction but once initiated the direction may change. * The precise conditions of when such a gesture is detected are * implementation-dependent. * * Only one gesture (either swipe or pinch) can be active at a given time. * * @param fingerCount The number of fingers involved in this multi-finger touchpad gesture * * @see PointerGesturesInterface * @see focusedPointerSurface * @see updatePointerSwipeGesture * @see endPointerSwipeGesture * @see cancelPointerSwipeGesture * @see startPointerPinchGesture * @since 5.29 **/ void startPointerSwipeGesture(quint32 fingerCount); /** * The position of the logical center of the currently active multi-finger swipe gesture changes. * * @param delta coordinates are relative coordinates of the logical center of the gesture compared to the previous event. * @see startPointerSwipeGesture * @see endPointerSwipeGesture * @see cancelPointerSwipeGesture * @since 5.29 **/ void updatePointerSwipeGesture(const QSizeF &delta); /** * The multi-finger swipe gesture ended. This may happen when one or more fingers are lifted. * @see startPointerSwipeGesture * @see updatePointerSwipeGesture * @see cancelPointerSwipeGesture * @see 5.29 **/ void endPointerSwipeGesture(); /** * The multi-finger swipe gestures ended and got cancelled by the Wayland compositor. * @see startPointerSwipeGesture * @see updatePointerSwipeGesture * @see endPointerSwipeGesture * @since 5.29 **/ void cancelPointerSwipeGesture(); /** * Starts a multi-finch pinch gesture for the currently focused pointer surface. * * Such gestures are normally reported through dedicated input devices such as touchpads. * * The gesture is usually initiated by multiple fingers moving towards * each other or away from each other, or by two or more fingers rotating * around a logical center of gravity. The precise conditions of when * such a gesture is detected are implementation-dependent. * * Only one gesture (either swipe or pinch) can be active at a given time. * * @param fingerCount The number of fingers involved in this multi-touch touchpad gesture * * @see PointerGesturesInterface * @see focusedPointerSurface * @see updatePointerPinchGesture * @see endPointerPinchGesture * @see cancelPointerPinchGesture * @see startPointerSwipeGesture * @since 5.29 **/ void startPointerPinchGesture(quint32 fingerCount); /** * The position of the logical center, the rotation or the relative scale of this * multi-finger pinch gesture changes. * * @param delta coordinates are relative coordinates of the logical center of the gesture compared to the previous event. * @param scale an absolute scale compared to the gesture start * @param rotation relative angle in degrees clockwise compared to the previous start of update * @see startPointerPinchGesture * @see endPointerPinchGesture * @see cancelPointerPinchGesture * @since 5.29 **/ void updatePointerPinchGesture(const QSizeF &delta, qreal scale, qreal rotation); /** * * @see startPointerPinchGesture * @see updatePointerPinchGesture * @see cancelPointerPinchGesture * @since 5.29 **/ void endPointerPinchGesture(); /** * * @see startPointerPinchGesture * @see updatePointerPinchGesture * @see endPointerPinchGesture * @since 5.29 **/ void cancelPointerPinchGesture(); ///@} /** * @name keyboard related methods **/ ///@{ void setKeymap(int fd, quint32 size); void keyPressed(quint32 key); void keyReleased(quint32 key); void updateKeyboardModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group); /** * Sets the key repeat information to be forwarded to all bound keyboards. * * To disable key repeat set a @p charactersPerSecond of @c 0. * * Requires wl_seat version 4. * * @param charactersPerSecond The characters per second rate, value of @c 0 disables key repeating * @param delay The delay on key press before starting repeating keys * * @since 5.5 ***/ void setKeyRepeatInfo(qint32 charactersPerSecond, qint32 delay); quint32 depressedModifiers() const; quint32 latchedModifiers() const; quint32 lockedModifiers() const; quint32 groupModifiers() const; quint32 lastModifiersSerial() const; int keymapFileDescriptor() const; quint32 keymapSize() const; bool isKeymapXkbCompatible() const; QVector pressedKeys() const; /** * @returns The key repeat in character per second * @since 5.5 * @see setKeyRepeatInfo * @see keyRepeatDelay **/ qint32 keyRepeatRate() const; /** * @returns The delay on key press before starting repeating keys * @since 5.5 * @see keyRepeatRate * @see setKeyRepeatInfo **/ qint32 keyRepeatDelay() const; /** * Passes keyboard focus to @p surface. * * If the SeatInterface has the keyboard capability, also the focused * text input surface will be set to @p surface. * * @see focusedKeyboardSurface * @see hasKeyboard * @see setFocusedTextInputSurface **/ void setFocusedKeyboardSurface(SurfaceInterface *surface); SurfaceInterface *focusedKeyboardSurface() const; KeyboardInterface *focusedKeyboard() const; ///@} /** * @name touch related methods **/ ///@{ void setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition = QPointF()); SurfaceInterface *focusedTouchSurface() const; TouchInterface *focusedTouch() const; void setFocusedTouchSurfacePosition(const QPointF &surfacePosition); QPointF focusedTouchSurfacePosition() const; qint32 touchDown(const QPointF &globalPosition); void touchUp(qint32 id); void touchMove(qint32 id, const QPointF &globalPosition); void touchFrame(); void cancelTouchSequence(); bool isTouchSequence() const; /** * @returns true if there is a touch sequence going on associated with a touch * down of the given @p serial. * @since 5.XX **/ bool hasImplicitTouchGrab(quint32 serial) const; ///@} /** * @name Text input related methods. **/ ///@{ /** * Passes text input focus to @p surface. * * If the SeatInterface has the keyboard capability this method will * be invoked automatically when setting the focused keyboard surface. * * In case there is a TextInputInterface for the @p surface, the enter * event will be triggered on the TextInputInterface for @p surface. * The focusedTextInput will be set to that TextInputInterface. If there * is no TextInputInterface for that @p surface, it might get updated later on. * In both cases the signal focusedTextInputChanged will be emitted. * * @see focusedTextInputSurface * @see focusedTextInput * @see focusedTextInputChanged * @see setFocusedKeyboardSurface * @since 5.23 **/ void setFocusedTextInputSurface(SurfaceInterface *surface); /** * @returns The SurfaceInterface which is currently focused for text input. * @see setFocusedTextInputSurface * @since 5.23 **/ SurfaceInterface *focusedTextInputSurface() const; /** * The currently focused text input, may be @c null even if there is a * focused text input surface set. * * The focused text input might not be enabled for the {@link focusedTextInputSurface}. * It is recommended to check the enabled state before interacting with the * TextInputInterface. * * @see focusedTextInputChanged * @see focusedTextInputSurface * @since 5.23 **/ TextInputInterface *focusedTextInput() const; ///@} /** * @returns The DataDeviceInterface holding the current clipboard selection. * @since 5.24 * @see selectionChanged * @see setSelection **/ DataDeviceInterface *selection() const; /** * This method allows to manually set the @p dataDevice for the current clipboard selection. * The clipboard selection is handled automatically in SeatInterface. * If a DataDeviceInterface belonging to the current focused KeyboardInterface * sets a selection, the current clipboard selection will be updated automatically. * With this method it's possible to override the automatic clipboard update for * e.g. the case of a clipboard manager. * * @param dataDevice Sets the current clipboard selection. * @see selection * @see selectionChanged * @since 5.24 **/ void setSelection(DataDeviceInterface *dataDevice); static SeatInterface *get(wl_resource *native); Q_SIGNALS: void nameChanged(const QString&); void hasPointerChanged(bool); void hasKeyboardChanged(bool); void hasTouchChanged(bool); void pointerPosChanged(const QPointF &pos); void touchMoved(qint32 id, quint32 serial, const QPointF &globalPosition); void timestampChanged(quint32); void pointerCreated(KWayland::Server::PointerInterface*); void keyboardCreated(KWayland::Server::KeyboardInterface*); void touchCreated(KWayland::Server::TouchInterface*); /** * Emitted whenever the focused pointer changes * @since 5.6 **/ void focusedPointerChanged(KWayland::Server::PointerInterface*); /** * Emitted whenever the selection changes * @since 5.56 * @see selection * @see setSelection **/ void selectionChanged(DataDeviceInterface*); /** * Emitted when a drag'n'drop operation is started * @since 5.6 * @see dragEnded **/ void dragStarted(); /** * Emitted when a drag'n'drop operation ended, either by dropping or canceling. * @since 5.6 * @see dragStarted **/ void dragEnded(); /** * Emitted whenever the drag surface for motion events changed. * @since 5.6 * @see dragSurface **/ void dragSurfaceChanged(); /** * Emitted whenever the focused text input changed. * @see focusedTextInput * @since 5.23 **/ void focusedTextInputChanged(); private: friend class Display; friend class DataDeviceManagerInterface; friend class TextInputManagerUnstableV0Interface; friend class TextInputManagerUnstableV2Interface; explicit SeatInterface(Display *display, QObject *parent); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::SeatInterface*) #endif diff --git a/src/server/seat_interface_p.h b/src/server/seat_interface_p.h index 4cbc671..6f17e3c 100644 --- a/src/server/seat_interface_p.h +++ b/src/server/seat_interface_p.h @@ -1,206 +1,192 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SEAT_INTERFACE_P_H #define WAYLAND_SERVER_SEAT_INTERFACE_P_H // KWayland #include "seat_interface.h" #include "global_p.h" // Qt #include #include #include #include // Wayland #include namespace KWayland { namespace Server { class DataDeviceInterface; class TextInputInterface; class SeatInterface::Private : public Global::Private { public: Private(SeatInterface *q, Display *d); void bind(wl_client *client, uint32_t version, uint32_t id) override; void sendCapabilities(wl_resource *r); void sendName(wl_resource *r); QVector pointersForSurface(SurfaceInterface *surface) const; QVector keyboardsForSurface(SurfaceInterface *surface) const; QVector touchsForSurface(SurfaceInterface *surface) const; DataDeviceInterface *dataDeviceForSurface(SurfaceInterface *surface) const; TextInputInterface *textInputForSurface(SurfaceInterface *surface) const; void registerDataDevice(DataDeviceInterface *dataDevice); void registerTextInput(TextInputInterface *textInput); void endDrag(quint32 serial); void cancelPreviousSelection(DataDeviceInterface *newlySelectedDataDevice); QString name; bool pointer = false; bool keyboard = false; bool touch = false; QList resources; quint32 timestamp = 0; QVector pointers; QVector keyboards; QVector touchs; QVector dataDevices; QVector textInputs; DataDeviceInterface *currentSelection = nullptr; // Pointer related members struct Pointer { enum class State { Released, Pressed }; QHash buttonSerials; QHash buttonStates; QPointF pos; struct Focus { SurfaceInterface *surface = nullptr; QVector pointers; QMetaObject::Connection destroyConnection; QPointF offset = QPointF(); QMatrix4x4 transformation; quint32 serial = 0; }; Focus focus; QPointer gestureSurface; }; Pointer globalPointer; void updatePointerButtonSerial(quint32 button, quint32 serial); void updatePointerButtonState(quint32 button, Pointer::State state); // Keyboard related members struct Keyboard { enum class State { Released, Pressed }; QHash states; struct Keymap { int fd = -1; quint32 size = 0; bool xkbcommonCompatible = false; }; Keymap keymap; struct Modifiers { quint32 depressed = 0; quint32 latched = 0; quint32 locked = 0; quint32 group = 0; quint32 serial = 0; }; Modifiers modifiers; struct Focus { SurfaceInterface *surface = nullptr; QVector keyboards; QMetaObject::Connection destroyConnection; quint32 serial = 0; DataDeviceInterface *selection = nullptr; }; Focus focus; quint32 lastStateSerial = 0; struct { qint32 charactersPerSecond = 0; qint32 delay = 0; } keyRepeat; }; Keyboard keys; bool updateKey(quint32 key, Keyboard::State state); struct TextInput { struct Focus { SurfaceInterface *surface = nullptr; QMetaObject::Connection destroyConnection; quint32 serial = 0; TextInputInterface *textInput = nullptr; }; Focus focus; }; TextInput textInput; // Touch related members struct Touch { struct Focus { SurfaceInterface *surface = nullptr; QVector touchs; QMetaObject::Connection destroyConnection; QPointF offset = QPointF(); QPointF firstTouchPos; }; Focus focus; QMap ids; }; Touch globalTouch; struct Drag { enum class Mode { None, Pointer, Touch }; Mode mode = Mode::None; DataDeviceInterface *source = nullptr; DataDeviceInterface *target = nullptr; SurfaceInterface *surface = nullptr; PointerInterface *sourcePointer = nullptr; TouchInterface *sourceTouch = nullptr; QMatrix4x4 transformation; QMetaObject::Connection destroyConnection; QMetaObject::Connection dragSourceDestroyConnection; }; Drag drag; static SeatInterface *get(wl_resource *native) { auto s = cast(native); return s ? s->q : nullptr; } private: void getPointer(wl_client *client, wl_resource *resource, uint32_t id); void getKeyboard(wl_client *client, wl_resource *resource, uint32_t id); void getTouch(wl_client *client, wl_resource *resource, uint32_t id); void updateSelection(DataDeviceInterface *dataDevice, bool set); static Private *cast(wl_resource *r); static void unbind(wl_resource *r); // interface static void getPointerCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getKeyboardCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getTouchCallback(wl_client *client, wl_resource *resource, uint32_t id); static void releaseCallback(wl_client *client, wl_resource *resource); static const struct wl_seat_interface s_interface; static const quint32 s_version; static const qint32 s_pointerVersion; static const qint32 s_touchVersion; static const qint32 s_keyboardVersion; SeatInterface *q; }; } } #endif diff --git a/src/server/server_decoration_interface.cpp b/src/server/server_decoration_interface.cpp index 9ba1694..471b50d 100644 --- a/src/server/server_decoration_interface.cpp +++ b/src/server/server_decoration_interface.cpp @@ -1,277 +1,263 @@ -/**************************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "server_decoration_interface.h" #include "display.h" #include "global_p.h" #include "logging.h" #include "resource_p.h" #include "surface_interface.h" #include #include namespace KWayland { namespace Server { class ServerSideDecorationManagerInterface::Private : public Global::Private { public: Private(ServerSideDecorationManagerInterface *q, Display *d); Mode defaultMode = Mode::None; QVector resources; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); void create(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); ServerSideDecorationManagerInterface *q; static const struct org_kde_kwin_server_decoration_manager_interface s_interface; static const quint32 s_version; }; const quint32 ServerSideDecorationManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_server_decoration_manager_interface ServerSideDecorationManagerInterface::Private::s_interface = { createCallback }; #endif void ServerSideDecorationManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { reinterpret_cast(wl_resource_get_user_data(resource))->create(client, resource, id, surface); } void ServerSideDecorationManagerInterface::Private::create(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { // TODO: send error? qCWarning(KWAYLAND_SERVER) << "ServerSideDecorationInterface requested for non existing SurfaceInterface"; return; } ServerSideDecorationInterface *decoration = new ServerSideDecorationInterface(q, s, resource); decoration->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!decoration->resource()) { wl_resource_post_no_memory(resource); delete decoration; return; } decoration->setMode(defaultMode); emit q->decorationCreated(decoration); } ServerSideDecorationManagerInterface::Mode ServerSideDecorationManagerInterface::defaultMode() const { return d_func()->defaultMode; } ServerSideDecorationManagerInterface::Private::Private(ServerSideDecorationManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_server_decoration_manager_interface, s_version) , q(q) { } namespace { static uint32_t modeWayland(ServerSideDecorationManagerInterface::Mode mode) { switch (mode) { case ServerSideDecorationManagerInterface::Mode::None: return ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE; break; case ServerSideDecorationManagerInterface::Mode::Client: return ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT; break; case ServerSideDecorationManagerInterface::Mode::Server: return ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER; break; default: Q_UNREACHABLE(); } } } void ServerSideDecorationManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_server_decoration_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_kwin_server_decoration_manager_send_default_mode(resource, modeWayland(defaultMode)); c->flush(); } void ServerSideDecorationManagerInterface::Private::unbind(wl_resource *resource) { cast(resource)->resources.removeAll(resource); } ServerSideDecorationManagerInterface::ServerSideDecorationManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } ServerSideDecorationManagerInterface::~ServerSideDecorationManagerInterface() = default; ServerSideDecorationManagerInterface::Private *ServerSideDecorationManagerInterface::d_func() const { return reinterpret_cast(d.data()); } void ServerSideDecorationManagerInterface::setDefaultMode(Mode mode) { Q_D(); d->defaultMode = mode; const uint32_t wlMode = modeWayland(mode); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); it++) { org_kde_kwin_server_decoration_manager_send_default_mode(*it, wlMode); } } class ServerSideDecorationInterface::Private : public Resource::Private { public: Private(ServerSideDecorationInterface *q, ServerSideDecorationManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); ServerSideDecorationManagerInterface::Mode mode = ServerSideDecorationManagerInterface::Mode::None; SurfaceInterface *surface; static ServerSideDecorationInterface *get(SurfaceInterface *s); private: static void requestModeCallback(wl_client *client, wl_resource *resource, uint32_t mode); ServerSideDecorationInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_kwin_server_decoration_interface s_interface; static QVector s_all; }; #ifndef K_DOXYGEN const struct org_kde_kwin_server_decoration_interface ServerSideDecorationInterface::Private::s_interface = { resourceDestroyedCallback, requestModeCallback }; QVector ServerSideDecorationInterface::Private::s_all; #endif void ServerSideDecorationInterface::Private::requestModeCallback(wl_client *client, wl_resource *resource, uint32_t mode) { Q_UNUSED(client) ServerSideDecorationManagerInterface::Mode m = ServerSideDecorationManagerInterface::Mode::None; switch (mode) { case ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE: m = ServerSideDecorationManagerInterface::Mode::None; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT: m = ServerSideDecorationManagerInterface::Mode::Client; break; case ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER: m = ServerSideDecorationManagerInterface::Mode::Server; break; default: // invalid mode qCWarning(KWAYLAND_SERVER) << "Invalid mode:" << mode; return; } emit cast(resource)->q_func()->modeRequested(m); } ServerSideDecorationInterface *ServerSideDecorationInterface::Private::get(SurfaceInterface *s) { auto it = std::find_if(s_all.constBegin(), s_all.constEnd(), [s] (Private *p) { return p->surface == s; }); if (it == s_all.constEnd()) { return nullptr; } return (*it)->q_func(); } ServerSideDecorationInterface::Private::Private(ServerSideDecorationInterface *q, ServerSideDecorationManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_server_decoration_interface, &s_interface) , surface(surface) { s_all << this; } ServerSideDecorationInterface::Private::~Private() { s_all.removeAll(this); } ServerSideDecorationInterface::ServerSideDecorationInterface(ServerSideDecorationManagerInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : Resource(new Private(this, parent, surface, parentResource)) { } ServerSideDecorationInterface::~ServerSideDecorationInterface() = default; void ServerSideDecorationInterface::setMode(ServerSideDecorationManagerInterface::Mode mode) { Q_D(); d->mode = mode; org_kde_kwin_server_decoration_send_mode(resource(), modeWayland(mode)); client()->flush(); } ServerSideDecorationManagerInterface::Mode ServerSideDecorationInterface::mode() const { Q_D(); return d->mode; } SurfaceInterface *ServerSideDecorationInterface::surface() const { Q_D(); return d->surface; } ServerSideDecorationInterface::Private *ServerSideDecorationInterface::d_func() const { return reinterpret_cast(d.data()); } ServerSideDecorationInterface *ServerSideDecorationInterface::get(SurfaceInterface *s) { return Private::get(s); } } } diff --git a/src/server/server_decoration_interface.h b/src/server/server_decoration_interface.h index 4bf8040..75fd81e 100644 --- a/src/server/server_decoration_interface.h +++ b/src/server/server_decoration_interface.h @@ -1,151 +1,137 @@ -/**************************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_SERVER_DECORATION_INTERFACE_H #define KWAYLAND_SERVER_SERVER_DECORATION_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class ServerSideDecorationInterface; class SurfaceInterface; /** * @brief Manager to create ServerSideDecorationInterface. * * @since 5.6 **/ class KWAYLANDSERVER_EXPORT ServerSideDecorationManagerInterface : public Global { Q_OBJECT public: virtual ~ServerSideDecorationManagerInterface(); /** * Decoration mode used for SurfaceInterfaces. **/ enum class Mode { /** * Undecorated: neither client, nor server provide decoration. Example: popups. **/ None, /** * The decoration is part of the surface. **/ Client, /** * The surface gets embedded into a decoration frame provided by the Server. **/ Server }; /** * Sets the default @p mode which is pushed to the Clients on creating a ServerSideDecorationInterface. * @param mode The new default mode. * @see defaultMode **/ void setDefaultMode(Mode mode); /** * @returns the current default mode. * @see setDefaultMode **/ Mode defaultMode() const; Q_SIGNALS: /** * Emitted whenever a new ServerSideDecorationInterface is created. **/ void decorationCreated(KWayland::Server::ServerSideDecorationInterface*); private: explicit ServerSideDecorationManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; /** * @brief Representing how a SurfaceInterface should be decorated. * * Created by ServerSideDecorationManagerInterface and emitted with decorationCreated signal. * * @since 5.6 **/ class KWAYLANDSERVER_EXPORT ServerSideDecorationInterface : public Resource { Q_OBJECT public: virtual ~ServerSideDecorationInterface(); /** * Sets the @p mode on the SurfaceInterface. A client might refuse the provided @p mode, * in that case modeRequested will be emitted. * @see mode * @see modeRequested **/ void setMode(ServerSideDecorationManagerInterface::Mode mode); /** * @returns the currently set mode, not the requested mode. * @see setMode * @see modeRequested **/ ServerSideDecorationManagerInterface::Mode mode() const; /** * @returns The SurfaceInterface this ServerSideDecorationInterface references. **/ SurfaceInterface *surface() const; /** * @returns The ServerSideDecorationInterface for the given @p surface, @c nullptr if there is none. **/ static ServerSideDecorationInterface *get(SurfaceInterface *surface); Q_SIGNALS: /** * The client requested the provided mode. * The server needs to acknowledge the requested mode by setting it through setMode. * @see setMode * @see mode **/ void modeRequested(KWayland::Server::ServerSideDecorationManagerInterface::Mode); private: explicit ServerSideDecorationInterface(ServerSideDecorationManagerInterface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class ServerSideDecorationManagerInterface; class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::ServerSideDecorationInterface*) Q_DECLARE_METATYPE(KWayland::Server::ServerSideDecorationManagerInterface::Mode) #endif diff --git a/src/server/server_decoration_palette_interface.cpp b/src/server/server_decoration_palette_interface.cpp index 56e2833..3c99bec 100644 --- a/src/server/server_decoration_palette_interface.cpp +++ b/src/server/server_decoration_palette_interface.cpp @@ -1,216 +1,202 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "server_decoration_palette_interface.h" #include "display.h" #include "surface_interface.h" #include "global_p.h" #include "resource_p.h" #include "logging.h" #include #include namespace KWayland { namespace Server { class ServerSideDecorationPaletteManagerInterface::Private : public Global::Private { public: Private(ServerSideDecorationPaletteManagerInterface *q, Display *d); QVector palettes; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); ServerSideDecorationPaletteManagerInterface *q; static const struct org_kde_kwin_server_decoration_palette_manager_interface s_interface; static const quint32 s_version; }; const quint32 ServerSideDecorationPaletteManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_server_decoration_palette_manager_interface ServerSideDecorationPaletteManagerInterface::Private::s_interface = { createCallback }; #endif void ServerSideDecorationPaletteManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { // TODO: send error? qCWarning(KWAYLAND_SERVER) << "ServerSideDecorationPaletteInterface requested for non existing SurfaceInterface"; return; } auto palette = new ServerSideDecorationPaletteInterface(p->q, s, resource); palette->create(p->display->getConnection(client), wl_resource_get_version(resource), id); if (!palette->resource()) { wl_resource_post_no_memory(resource); delete palette; return; } p->palettes.append(palette); QObject::connect(palette, &QObject::destroyed, p->q, [=]() { p->palettes.removeOne(palette); }); emit p->q->paletteCreated(palette); } ServerSideDecorationPaletteManagerInterface::Private::Private(ServerSideDecorationPaletteManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_server_decoration_palette_manager_interface, s_version) , q(q) { } void ServerSideDecorationPaletteManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_server_decoration_palette_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); } void ServerSideDecorationPaletteManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } class ServerSideDecorationPaletteInterface::Private : public Resource::Private { public: Private(ServerSideDecorationPaletteInterface *q, ServerSideDecorationPaletteManagerInterface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); SurfaceInterface *surface; QString palette; private: static void setPaletteCallback(wl_client *client, wl_resource *resource, const char * palette); ServerSideDecorationPaletteInterface *q_func() { return reinterpret_cast(q); } static ServerSideDecorationPaletteInterface *get(SurfaceInterface *s); static const struct org_kde_kwin_server_decoration_palette_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_server_decoration_palette_interface ServerSideDecorationPaletteInterface::Private::s_interface = { setPaletteCallback, resourceDestroyedCallback }; #endif void ServerSideDecorationPaletteInterface::Private::setPaletteCallback(wl_client *client, wl_resource *resource, const char * palette) { Q_UNUSED(client); auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); if (p->palette == QLatin1String(palette)) { return; } p->palette = QString::fromUtf8(palette); emit p->q_func()->paletteChanged(p->palette); } ServerSideDecorationPaletteInterface::Private::Private(ServerSideDecorationPaletteInterface *q, ServerSideDecorationPaletteManagerInterface *c, SurfaceInterface *s, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_server_decoration_palette_interface, &s_interface), surface(s) { } ServerSideDecorationPaletteInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } ServerSideDecorationPaletteManagerInterface::ServerSideDecorationPaletteManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } ServerSideDecorationPaletteManagerInterface::~ServerSideDecorationPaletteManagerInterface() { } ServerSideDecorationPaletteManagerInterface::Private *ServerSideDecorationPaletteManagerInterface::d_func() const { return reinterpret_cast(d.data()); } ServerSideDecorationPaletteInterface* ServerSideDecorationPaletteManagerInterface::paletteForSurface(SurfaceInterface *surface) { Q_D(); for (ServerSideDecorationPaletteInterface* menu: d->palettes) { if (menu->surface() == surface) { return menu; } } return nullptr; } ServerSideDecorationPaletteInterface::ServerSideDecorationPaletteInterface(ServerSideDecorationPaletteManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource): Resource(new Private(this, parent, s, parentResource)) { } ServerSideDecorationPaletteInterface::Private *ServerSideDecorationPaletteInterface::d_func() const { return reinterpret_cast(d.data()); } ServerSideDecorationPaletteInterface::~ServerSideDecorationPaletteInterface() {} QString ServerSideDecorationPaletteInterface::palette() const { Q_D(); return d->palette; } SurfaceInterface* ServerSideDecorationPaletteInterface::surface() const { Q_D(); return d->surface; } }//namespace } diff --git a/src/server/server_decoration_palette_interface.h b/src/server/server_decoration_palette_interface.h index 7f22a97..5540516 100644 --- a/src/server/server_decoration_palette_interface.h +++ b/src/server/server_decoration_palette_interface.h @@ -1,106 +1,92 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_DECORATION_PALETTE_INTERFACE_H #define KWAYLAND_SERVER_DECORATION_PALETTE_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class SurfaceInterface; class ServerSideDecorationPaletteInterface; /** * Allows a client to specify a preferred palette to use for server-side window decorations * * This global can be used for clients to bind ServerSideDecorationPaletteInterface instances * and notifies when a new one is created * @since 5.42 */ class KWAYLANDSERVER_EXPORT ServerSideDecorationPaletteManagerInterface : public Global { Q_OBJECT public: virtual ~ServerSideDecorationPaletteManagerInterface(); /** * Returns any existing palette for a given surface * This returns a null pointer if no ServerSideDecorationPaletteInterface exists. */ ServerSideDecorationPaletteInterface* paletteForSurface(SurfaceInterface *); Q_SIGNALS: /** * Emitted whenever a new ServerSideDecorationPaletteInterface is created. **/ void paletteCreated(KWayland::Server::ServerSideDecorationPaletteInterface*); private: explicit ServerSideDecorationPaletteManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; /** * Provides the palette * This interface is attached to a wl_surface and informs the server of a requested palette * @since 5.42 */ class KWAYLANDSERVER_EXPORT ServerSideDecorationPaletteInterface : public Resource { Q_OBJECT public: virtual ~ServerSideDecorationPaletteInterface(); /** * @returns the palette or an empty string if unset */ QString palette() const; /** * @returns The SurfaceInterface this ServerSideDecorationPaletteInterface references. **/ SurfaceInterface *surface() const; Q_SIGNALS: /** * Emitted when the palette changes or is first received */ void paletteChanged(const QString &palette); private: explicit ServerSideDecorationPaletteInterface(ServerSideDecorationPaletteManagerInterface *parent, SurfaceInterface *s, wl_resource *parentResource); friend class ServerSideDecorationPaletteManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/shadow_interface.cpp b/src/server/shadow_interface.cpp index 341910f..3e07627 100644 --- a/src/server/shadow_interface.cpp +++ b/src/server/shadow_interface.cpp @@ -1,409 +1,395 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "shadow_interface.h" #include "buffer_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include #include namespace KWayland { namespace Server { class ShadowManagerInterface::Private : public Global::Private { public: Private(ShadowManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createShadow(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface); static void destroyCallback(wl_client *client, wl_resource *resource); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } ShadowManagerInterface *q; static const struct org_kde_kwin_shadow_manager_interface s_interface; static const quint32 s_version; }; const quint32 ShadowManagerInterface::Private::s_version = 2; #ifndef K_DOXYGEN const struct org_kde_kwin_shadow_manager_interface ShadowManagerInterface::Private::s_interface = { createCallback, unsetCallback, destroyCallback }; #endif ShadowManagerInterface::Private::Private(ShadowManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_shadow_manager_interface, s_version) , q(q) { } void ShadowManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_shadow_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void ShadowManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } void ShadowManagerInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void ShadowManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { cast(resource)->createShadow(client, resource, id, surface); } void ShadowManagerInterface::Private::createShadow(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } ShadowInterface *shadow = new ShadowInterface(q, resource); shadow->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!shadow->resource()) { wl_resource_post_no_memory(resource); delete shadow; return; } s->d_func()->setShadow(QPointer(shadow)); } void ShadowManagerInterface::Private::unsetCallback(wl_client *client, wl_resource *resource, wl_resource *surface) { Q_UNUSED(client) Q_UNUSED(resource) SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } s->d_func()->setShadow(QPointer()); } ShadowManagerInterface::ShadowManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } ShadowManagerInterface::~ShadowManagerInterface() = default; class ShadowInterface::Private : public Resource::Private { public: Private(ShadowInterface *q, ShadowManagerInterface *c, wl_resource *parentResource); ~Private(); struct State { enum Flags { None = 0, LeftBuffer = 1 << 0, TopLeftBuffer = 1 << 1, TopBuffer = 1 << 2, TopRightBuffer = 1 << 3, RightBuffer = 1 << 4, BottomRightBuffer = 1 << 5, BottomBuffer = 1 << 6, BottomLeftBuffer = 1 << 7, Offset = 1 << 8 }; BufferInterface *left = nullptr; BufferInterface *topLeft = nullptr; BufferInterface *top = nullptr; BufferInterface *topRight = nullptr; BufferInterface *right = nullptr; BufferInterface *bottomRight = nullptr; BufferInterface *bottom = nullptr; BufferInterface *bottomLeft = nullptr; QMarginsF offset; Flags flags = Flags::None; }; State current; State pending; private: void commit(); void attach(State::Flags flag, wl_resource *buffer); ShadowInterface *q_func() { return reinterpret_cast(q); } static void commitCallback(wl_client *client, wl_resource *resource); static void attachLeftCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachTopLeftCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachTopCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachTopRightCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachRightCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachBottomRightCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachBottomCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void attachBottomLeftCallback(wl_client *client, wl_resource *resource, wl_resource *buffer); static void offsetLeftCallback(wl_client *client, wl_resource *resource, wl_fixed_t offset); static void offsetTopCallback(wl_client *client, wl_resource *resource, wl_fixed_t offset); static void offsetRightCallback(wl_client *client, wl_resource *resource, wl_fixed_t offset); static void offsetBottomCallback(wl_client *client, wl_resource *resource, wl_fixed_t offset); static const struct org_kde_kwin_shadow_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_shadow_interface ShadowInterface::Private::s_interface = { commitCallback, attachLeftCallback, attachTopLeftCallback, attachTopCallback, attachTopRightCallback, attachRightCallback, attachBottomRightCallback, attachBottomCallback, attachBottomLeftCallback, offsetLeftCallback, offsetTopCallback, offsetRightCallback, offsetBottomCallback, resourceDestroyedCallback }; #endif void ShadowInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->commit(); } void ShadowInterface::Private::commit() { #define BUFFER( __FLAG__, __PART__ ) \ if (pending.flags & State::Flags::__FLAG__##Buffer) { \ if (current.__PART__) { \ current.__PART__->unref(); \ } \ if (pending.__PART__) { \ pending.__PART__->ref(); \ } \ current.__PART__ = pending.__PART__; \ } BUFFER(Left, left) BUFFER(TopLeft, topLeft) BUFFER(Top, top) BUFFER(TopRight, topRight) BUFFER(Right, right) BUFFER(BottomRight, bottomRight) BUFFER(Bottom, bottom) BUFFER(BottomLeft, bottomLeft) #undef BUFFER if (pending.flags & State::Offset) { current.offset = pending.offset; } pending = State(); } void ShadowInterface::Private::attach(ShadowInterface::Private::State::Flags flag, wl_resource *buffer) { BufferInterface *b = BufferInterface::get(buffer); if (b) { QObject::connect(b, &BufferInterface::aboutToBeDestroyed, q, [this](BufferInterface *buffer) { #define PENDING( __PART__ ) \ if (pending.__PART__ == buffer) { \ pending.__PART__ = nullptr; \ } PENDING(left) PENDING(topLeft) PENDING(top) PENDING(topRight) PENDING(right) PENDING(bottomRight) PENDING(bottom) PENDING(bottomLeft) #undef PENDING #define CURRENT( __PART__ ) \ if (current.__PART__ == buffer) { \ current.__PART__->unref(); \ current.__PART__ = nullptr; \ } CURRENT(left) CURRENT(topLeft) CURRENT(top) CURRENT(topRight) CURRENT(right) CURRENT(bottomRight) CURRENT(bottom) CURRENT(bottomLeft) #undef CURRENT } ); } switch (flag) { case State::LeftBuffer: pending.left = b; break; case State::TopLeftBuffer: pending.topLeft = b; break; case State::TopBuffer: pending.top = b; break; case State::TopRightBuffer: pending.topRight = b; break; case State::RightBuffer: pending.right = b; break; case State::BottomRightBuffer: pending.bottomRight = b; break; case State::BottomBuffer: pending.bottom = b; break; case State::BottomLeftBuffer: pending.bottomLeft = b; break; default: Q_UNREACHABLE(); break; } pending.flags = State::Flags(pending.flags | flag); } #define ATTACH( __PART__ ) \ void ShadowInterface::Private::attach##__PART__##Callback(wl_client *client, wl_resource *resource, wl_resource *buffer) \ { \ Q_UNUSED(client) \ Private *p = cast(resource); \ p->attach(State::__PART__##Buffer, buffer); \ } ATTACH(Left) ATTACH(TopLeft) ATTACH(Top) ATTACH(TopRight) ATTACH(Right) ATTACH(BottomRight) ATTACH(Bottom) ATTACH(BottomLeft) #undef ATTACH #define OFFSET( __PART__ ) \ void ShadowInterface::Private::offset##__PART__##Callback(wl_client *client, wl_resource *resource, wl_fixed_t offset) \ { \ Q_UNUSED(client) \ Q_UNUSED(resource) \ Private *p = cast(resource); \ p->pending.flags = State::Flags(p->pending.flags | State::Offset); \ p->pending.offset.set##__PART__(wl_fixed_to_double(offset)); \ } OFFSET(Left) OFFSET(Top) OFFSET(Right) OFFSET(Bottom) #undef OFFSET ShadowInterface::Private::Private(ShadowInterface *q, ShadowManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_shadow_interface, &s_interface) { } ShadowInterface::Private::~Private() { #define CURRENT( __PART__ ) \ if (current.__PART__) { \ current.__PART__->unref(); \ } CURRENT(left) CURRENT(topLeft) CURRENT(top) CURRENT(topRight) CURRENT(right) CURRENT(bottomRight) CURRENT(bottom) CURRENT(bottomLeft) #undef CURRENT } ShadowInterface::ShadowInterface(ShadowManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } ShadowInterface::~ShadowInterface() = default; QMarginsF ShadowInterface::offset() const { Q_D(); return d->current.offset; } #define BUFFER( __PART__ ) \ BufferInterface *ShadowInterface::__PART__() const \ { \ Q_D(); \ return d->current.__PART__; \ } BUFFER(left) BUFFER(topLeft) BUFFER(top) BUFFER(topRight) BUFFER(right) BUFFER(bottomRight) BUFFER(bottom) BUFFER(bottomLeft) ShadowInterface::Private *ShadowInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/shadow_interface.h b/src/server/shadow_interface.h index 8940b4e..6f2bfd0 100644 --- a/src/server/shadow_interface.h +++ b/src/server/shadow_interface.h @@ -1,85 +1,71 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_SHADOW_INTERFACE_H #define KWAYLAND_SERVER_SHADOW_INTERFACE_H #include "global.h" #include "resource.h" #include #include #include namespace KWayland { namespace Server { class BufferInterface; class Display; /** * TODO */ class KWAYLANDSERVER_EXPORT ShadowManagerInterface : public Global { Q_OBJECT public: virtual ~ShadowManagerInterface(); private: explicit ShadowManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * TODO */ class KWAYLANDSERVER_EXPORT ShadowInterface : public Resource { Q_OBJECT public: virtual ~ShadowInterface(); BufferInterface *left() const; BufferInterface *topLeft() const; BufferInterface *top() const; BufferInterface *topRight() const; BufferInterface *right() const; BufferInterface *bottomRight() const; BufferInterface *bottom() const; BufferInterface *bottomLeft() const; QMarginsF offset() const; private: explicit ShadowInterface(ShadowManagerInterface *parent, wl_resource *parentResource); friend class ShadowManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/shell_interface.cpp b/src/server/shell_interface.cpp index 800efc7..61363c4 100644 --- a/src/server/shell_interface.cpp +++ b/src/server/shell_interface.cpp @@ -1,481 +1,467 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "shell_interface.h" #include "generic_shell_surface_p.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include #include namespace KWayland { namespace Server { class ShellInterface::Private : public Global::Private { public: Private(ShellInterface *q, Display *d); QList surfaces; private: static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); ShellInterface *q; static const struct wl_shell_interface s_interface; static const quint32 s_version; }; const quint32 ShellInterface::Private::s_version = 1; ShellInterface::Private::Private(ShellInterface *q, Display *d) : Global::Private(d, &wl_shell_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct wl_shell_interface ShellInterface::Private::s_interface = { createSurfaceCallback }; #endif class ShellSurfaceInterface::Private : public Resource::Private, public GenericShellSurface { public: Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource); void ping(); void commit() override; QScopedPointer pingTimer; quint32 pingSerial = 0; enum class WindowMode { Fullscreen, Toplevel, Maximized, Popup }; WindowMode windowMode = WindowMode::Toplevel; QPoint transientOffset; QPointer transientFor; bool acceptsKeyboardFocus = true; void setWindowMode(WindowMode newWindowMode); ShellSurfaceInterface *q_func() { return reinterpret_cast(q); } private: // interface callbacks static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setToplevelCallback(wl_client *client, wl_resource *resource); static void setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, int32_t x, int32_t y, uint32_t flags); static void setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, uint32_t framerate, wl_resource *output); static void setPopupCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, wl_resource *parent, int32_t x, int32_t y, uint32_t flags); static void setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output); void pong(quint32 serial); void setAcceptsFocus(quint32 flags); static const struct wl_shell_surface_interface s_interface; }; ShellInterface::ShellInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } ShellInterface::~ShellInterface() = default; void ShellInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&wl_shell_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, nullptr); } void ShellInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void ShellInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](ShellSurfaceInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), WL_SHELL_ERROR_ROLE, "ShellSurface already created"); return; } ShellSurfaceInterface *shellSurface = new ShellSurfaceInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &ShellSurfaceInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } /********************************* * ShellSurfaceInterface *********************************/ ShellSurfaceInterface::Private::Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource) : Resource::Private(q, shell, parentResource, &wl_shell_surface_interface, &s_interface) , GenericShellSurface(q, surface) , pingTimer(new QTimer) { pingTimer->setSingleShot(true); pingTimer->setInterval(1000); } #ifndef K_DOXYGEN const struct wl_shell_surface_interface ShellSurfaceInterface::Private::s_interface = { pongCallback, moveCallback, resizeCallback, setToplevelCallback, setTransientCallback, setFullscreenCallback, setPopupCallback, setMaximizedCallback, setTitleCallback, setAppIdCallback }; #endif ShellSurfaceInterface::ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource) : Resource(new Private(this, shell, parent, parentResource)) { Q_D(); connect(d->pingTimer.data(), &QTimer::timeout, this, &ShellSurfaceInterface::pingTimeout); auto unsetSurface = [this] { Q_D(); d->surface = nullptr; }; connect(parent, &Resource::unbound, this, unsetSurface); connect(parent, &QObject::destroyed, this, unsetSurface); } ShellSurfaceInterface::~ShellSurfaceInterface() = default; void ShellSurfaceInterface::Private::commit() { } void ShellSurfaceInterface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->pong(serial); } void ShellSurfaceInterface::Private::pong(quint32 serial) { if (pingTimer->isActive() && serial == pingSerial) { pingTimer->stop(); Q_Q(ShellSurfaceInterface); emit q->pongReceived(); } } void ShellSurfaceInterface::ping() { Q_D(); if (!d->resource) { return; } d->ping(); } void ShellSurfaceInterface::Private::ping() { if (pingTimer->isActive()) { return; } pingSerial = global->display()->nextSerial(); wl_shell_surface_send_ping(resource, pingSerial); client->flush(); pingTimer->start(); } void ShellSurfaceInterface::setPingTimeout(uint msec) { Q_D(); d->pingTimer->setInterval(msec); } bool ShellSurfaceInterface::isPinged() const { Q_D(); return d->pingTimer->isActive(); } void ShellSurfaceInterface::requestSize(const QSize &size) { Q_D(); if (!d->resource) { return; } // TODO: what about the edges? wl_shell_surface_send_configure(d->resource, 0, size.width(), size.height()); d->client->flush(); } namespace { template <> Qt::Edges edgesToQtEdges(wl_shell_surface_resize edges) { Qt::Edges qtEdges; switch (edges) { case WL_SHELL_SURFACE_RESIZE_TOP: qtEdges = Qt::TopEdge; break; case WL_SHELL_SURFACE_RESIZE_BOTTOM: qtEdges = Qt::BottomEdge; break; case WL_SHELL_SURFACE_RESIZE_LEFT: qtEdges = Qt::LeftEdge; break; case WL_SHELL_SURFACE_RESIZE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case WL_SHELL_SURFACE_RESIZE_RIGHT: qtEdges = Qt::RightEdge; break; case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case WL_SHELL_SURFACE_RESIZE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } void ShellSurfaceInterface::Private::setToplevelCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->setWindowMode(WindowMode::Toplevel); } void ShellSurfaceInterface::Private::setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, int32_t x, int32_t y, uint32_t flags) { Q_UNUSED(flags) auto s = cast(resource); Q_ASSERT(client == *s->client); auto surface = SurfaceInterface::get(parent); if (surface && s->surface == surface) { wl_resource_post_error(surface->resource(), WL_SHELL_ERROR_ROLE, "Cannot be a transient to itself"); return; } s->transientFor = QPointer(surface); s->transientOffset = QPoint(x, y); emit s->q_func()->transientChanged(!s->transientFor.isNull()); emit s->q_func()->transientOffsetChanged(s->transientOffset); emit s->q_func()->transientForChanged(); s->setAcceptsFocus(flags); } void ShellSurfaceInterface::Private::setAcceptsFocus(quint32 flags) { const bool acceptsFocus = !(flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE); if (acceptsFocus != acceptsKeyboardFocus) { acceptsKeyboardFocus = acceptsFocus; Q_Q(ShellSurfaceInterface); emit q->acceptsKeyboardFocusChanged(); } } void ShellSurfaceInterface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, uint32_t framerate, wl_resource *output) { Q_UNUSED(method) Q_UNUSED(framerate) Q_UNUSED(output) auto s = cast(resource); Q_ASSERT(client == *s->client); // TODO: add method, framerate and output s->setWindowMode(WindowMode::Fullscreen); } void ShellSurfaceInterface::Private::setWindowMode(WindowMode newWindowMode) { if (windowMode == newWindowMode) { return; } const WindowMode oldWindowMode = windowMode; windowMode = newWindowMode; Q_Q(ShellSurfaceInterface); if (oldWindowMode == WindowMode::Fullscreen || newWindowMode == WindowMode::Fullscreen) { emit q->fullscreenChanged(windowMode == WindowMode::Fullscreen); } if (oldWindowMode == WindowMode::Toplevel || newWindowMode == WindowMode::Toplevel) { emit q->toplevelChanged(windowMode == WindowMode::Toplevel); } if (oldWindowMode == WindowMode::Maximized || newWindowMode == WindowMode::Maximized) { emit q->maximizedChanged(windowMode == WindowMode::Maximized); } if (oldWindowMode == WindowMode::Popup || newWindowMode == WindowMode::Popup) { emit q->popupChanged(windowMode == WindowMode::Popup); } } void ShellSurfaceInterface::Private::setPopupCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, wl_resource *parent, int32_t x, int32_t y, uint32_t flags) { Q_UNUSED(seat) Q_UNUSED(serial) Q_UNUSED(flags) auto s = cast(resource); Q_ASSERT(client == *s->client); // TODO: what about seat and serial? s->transientFor = QPointer(SurfaceInterface::get(parent)); s->transientOffset = QPoint(x, y); s->setWindowMode(WindowMode::Popup); emit s->q_func()->transientChanged(!s->transientFor.isNull()); emit s->q_func()->transientOffsetChanged(s->transientOffset); emit s->q_func()->transientForChanged(); // we ignore the flags as Qt requests keyboard focus for popups // if we would honor the flag this could break compositors // compare QtWayland (5.6), file qwaylandwlshellsurface.cpp:208 s->setAcceptsFocus(WL_SHELL_SURFACE_TRANSIENT_INACTIVE); } void ShellSurfaceInterface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output) { Q_UNUSED(output) auto s = cast(resource); Q_ASSERT(client == *s->client); s->setWindowMode(WindowMode::Maximized); } SurfaceInterface *ShellSurfaceInterface::surface() const { Q_D(); return d->surface; } ShellInterface *ShellSurfaceInterface::shell() const { Q_D(); return reinterpret_cast(d->global); } QString ShellSurfaceInterface::title() const { Q_D(); return d->title; } QByteArray ShellSurfaceInterface::windowClass() const { Q_D(); return d->windowClass; } bool ShellSurfaceInterface::isFullscreen() const { Q_D(); return d->windowMode == Private::WindowMode::Fullscreen; } bool ShellSurfaceInterface::isToplevel() const { Q_D(); return d->windowMode == Private::WindowMode::Toplevel; } bool ShellSurfaceInterface::isMaximized() const { Q_D(); return d->windowMode == Private::WindowMode::Maximized; } bool ShellSurfaceInterface::isPopup() const { Q_D(); return d->windowMode == Private::WindowMode::Popup; } bool ShellSurfaceInterface::isTransient() const { Q_D(); return !d->transientFor.isNull(); } QPoint ShellSurfaceInterface::transientOffset() const { Q_D(); return d->transientOffset; } bool ShellSurfaceInterface::acceptsKeyboardFocus() const { Q_D(); return d->acceptsKeyboardFocus; } void ShellSurfaceInterface::popupDone() { Q_D(); if (isPopup() && d->resource) { wl_shell_surface_send_popup_done(d->resource); } } QPointer< SurfaceInterface > ShellSurfaceInterface::transientFor() const { Q_D(); return d->transientFor; } ShellSurfaceInterface::Private *ShellSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/shell_interface.h b/src/server/shell_interface.h index 75757ae..3b483b0 100644 --- a/src/server/shell_interface.h +++ b/src/server/shell_interface.h @@ -1,318 +1,304 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SHELL_INTERFACE_H #define WAYLAND_SERVER_SHELL_INTERFACE_H #include #include #include "seat_interface.h" #include "global.h" #include "resource.h" class QSize; struct wl_resource; namespace KWayland { namespace Server { class Display; class SeatInterface; class SurfaceInterface; class ShellSurfaceInterface; template class GenericShellSurface; /** * @brief Global for the wl_shell interface. * * @see ShellSurfaceInterface * @see SurfaceInterface **/ class KWAYLANDSERVER_EXPORT ShellInterface : public Global { Q_OBJECT public: virtual ~ShellInterface(); Q_SIGNALS: /** * Emitted whenever a new ShellSurfaceInterface gets created for a SurfaceInterface. **/ void surfaceCreated(KWayland::Server::ShellSurfaceInterface*); private: friend class Display; explicit ShellInterface(Display *display, QObject *parent); class Private; }; /** * @brief Resource for a wl_shell_surface. * * The ShellSurfaceInterface represents a "normal window". It gets created for a * SurfaceInterface, thus has visible content. Through the ShellSurfaceInterface the * client can specify further meta-information about how the SurfaceInterface should be * represented. * * @see SurfaceInterface * @see ShellInterface **/ class KWAYLANDSERVER_EXPORT ShellSurfaceInterface : public Resource { Q_OBJECT /** * The window title **/ Q_PROPERTY(QString title READ title NOTIFY titleChanged) /** * The window class, representing the desktop file name. **/ Q_PROPERTY(QByteArray windowClass READ windowClass NOTIFY windowClassChanged) /** * Whether the window is fullscreen. **/ Q_PROPERTY(bool fullscreen READ isFullscreen NOTIFY fullscreenChanged) /** * Whether the window is a normal toplevel window (not a child). **/ Q_PROPERTY(bool toplevel READ isToplevel NOTIFY toplevelChanged) /** * Whether the window is maximized. **/ Q_PROPERTY(bool maximized READ isMaximized NOTIFY maximizedChanged) /** * Whether the ShellSurfaceInterface is a popup for another SurfaceInterface. * * Popup implies transient. * @since 5.5 **/ Q_PROPERTY(bool popup READ isPopup NOTIFY popupChanged) /** * Whether the ShellSurfaceInterface is a transient for another SurfaceInterface. * * Popup implies transient. * @since 5.5 **/ Q_PROPERTY(bool transient READ isTransient NOTIFY transientChanged) /** * Offset of the upper left corner in the parent SurfaceInterface's coordinate system. * @since 5.5 **/ Q_PROPERTY(QPoint transientOffset READ transientOffset NOTIFY transientOffsetChanged) /** * Whether the ShellSurfaceInterface can accept keyboard focus. * * By default ShellSurfaceInterface accepts keyboard focus, only transient surfaces * might not want keyboard focus. * * @since 5.5 **/ Q_PROPERTY(bool acceptsKeyboardFocus READ acceptsKeyboardFocus NOTIFY acceptsKeyboardFocusChanged) public: virtual ~ShellSurfaceInterface(); /** * Pings the client. * The client is required to send a pong. If that is not received in the times tamp * set through setPingTimeout the signal @link pingTimeout @endlink will be emitted. * If a pong is received the signal @link pongReceived @endlink will be emitted. * * @see setPingTimeout * @see pingTimeout * @see pongReceived * @see isPinged **/ void ping(); /** * Sets the ping time out for @link ping @endlink requests to @p msec. * * @param msec The time out in msec * @see ping * @see isPinged **/ void setPingTimeout(uint msec); /** * @returns whether the ShellSurfaceInterface got pinged, but no pong received. * @see ping **/ bool isPinged() const; /** * Requests that the ShellSurfaceInterface resizes the SurfaceInterface to @p size. **/ void requestSize(const QSize &size); /** * @return The SurfaceInterface this ShellSurfaceInterface got created for. **/ SurfaceInterface *surface() const; /** * @returns The ShellInterface which created this ShellSurfaceInterface. **/ ShellInterface *shell() const; QString title() const; QByteArray windowClass() const; bool isFullscreen() const; bool isToplevel() const; bool isMaximized() const; /** * @returns @c true if the ShellSurfaceInterface is a popup. * @see isTransient * @see transientOffset * @see transientFor * @since 5.5 **/ bool isPopup() const; /** * @returns @c true if the ShellSurfaceInterface is a transient or popup for another SurfaceInterface. * @see isPopup * @see transientOffset * @see transientFor * @since 5.5 **/ bool isTransient() const; /** * In case the ShellSurfaceInterface is a transient this is the offset of the ShellSurfaceInterface * in the coordinate system of the SurfaceInterface this surface is a transient for. * * @returns offset in parent coordinate system. * @see isTransient * @see transientFor * @since 5.5 **/ QPoint transientOffset() const; /** * The SurfaceInterface for which this ShellSurfaceInterface is a transient. * This is only relevant if the ShellSurfaceInterface is either a transient or a * popup. * * The transientOffset is in the local coordinate system of the SurfaceInterface * returned by this method. * * @returns The SurfaceInterface for which this Surface is a transient * @see isTransient * @see isPopup * @see transientOffset * @since 5.5 **/ QPointer transientFor() const; /** * Whether the ShellSurfaceInterface can accept keyboard focus. * * This is only relevant for transient and popup windows. By default all ShellSurfaces * accept keyboard focus. * * @returns Whether the ShellSurfaceInterface can accept keyboard focus. * @see isTransient() * @see acceptsKeyboardFocusChanged * @since 5.5 **/ bool acceptsKeyboardFocus() const; /** * Sends a popup done event to the shell surface. * This is only relevant for popup windows. It indicates that the popup grab * got canceled. This happens when e.g. the user clicks outside of any surface * of the same client as this ShellSurfaceInterface. It is the task of the * compositor to send the popupDone event appropriately. * * @see isPopup * @since 5.33 **/ void popupDone(); Q_SIGNALS: /** * Emitted whenever the title changes. **/ void titleChanged(const QString&); /** * Emitted whenever the window class changes. **/ void windowClassChanged(const QByteArray&); /** * Emitted when the ping timed out. * @see ping * @see pingTimeout * @see isPinged **/ void pingTimeout(); /** * Emitted when the server received a pong for this ShellSurfaceInterface. **/ void pongReceived(); void fullscreenChanged(bool); void toplevelChanged(bool); void maximizedChanged(bool); /** * @since 5.5 **/ void popupChanged(bool); /** * @since 5.5 **/ void transientChanged(bool); /** * @since 5.5 **/ void transientOffsetChanged(const QPoint&); /** * @since 5.5 **/ void transientForChanged(); /** * @since 5.5 **/ void acceptsKeyboardFocusChanged(); /** * The surface requested a window move. * * @param seat The SeatInterface on which the surface requested the move * @param serial The serial of the implicit mouse grab which triggered the move * @since 5.5 **/ void moveRequested(KWayland::Server::SeatInterface *seat, quint32 serial); /** * The surface requested a window resize. * * @param seat The SeatInterface on which the surface requested the resize * @param serial The serial of the implicit mouse grab which triggered the resize * @param edges A hint which edges are involved in the resize * @since 5.5 **/ void resizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges); private: friend class ShellInterface; explicit ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource); friend class GenericShellSurface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/slide_interface.cpp b/src/server/slide_interface.cpp index f9bec1a..12043b5 100644 --- a/src/server/slide_interface.cpp +++ b/src/server/slide_interface.cpp @@ -1,216 +1,202 @@ -/**************************************************************************** -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "slide_interface.h" #include "display.h" #include "global_p.h" #include "surface_interface.h" #include "resource_p.h" #include "surface_interface_p.h" #include #include namespace KWayland { namespace Server { class SlideManagerInterface::Private : public Global::Private { public: Private(SlideManagerInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void createSlide(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void unsetCallback(wl_client *client, wl_resource *resource, wl_resource * surface); SlideManagerInterface *q; static const struct org_kde_kwin_slide_manager_interface s_interface; //initializing here doesn't link static const quint32 s_version; }; const quint32 SlideManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct org_kde_kwin_slide_manager_interface SlideManagerInterface::Private::s_interface = { createCallback, unsetCallback }; #endif void SlideManagerInterface::Private::createCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { cast(resource)->createSlide(client, resource, id, surface); } void SlideManagerInterface::Private::createSlide(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { SurfaceInterface *s = SurfaceInterface::get(surface); if (!s) { return; } SlideInterface *slide = new SlideInterface(q, resource); slide->create(display->getConnection(client), wl_resource_get_version(resource), id); if (!slide->resource()) { wl_resource_post_no_memory(resource); delete slide; return; } s->d_func()->setSlide(QPointer(slide)); } void SlideManagerInterface::Private::unsetCallback(wl_client *client, wl_resource *resource, wl_resource * surface) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(surface) // TODO: implement } SlideManagerInterface::Private::Private(SlideManagerInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_slide_manager_interface, s_version) , q(q) { } void SlideManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_slide_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void SlideManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } SlideManagerInterface::SlideManagerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } SlideManagerInterface::~SlideManagerInterface() = default; class SlideInterface::Private : public Resource::Private { public: Private(SlideInterface *q, SlideManagerInterface *c, wl_resource *parentResource); ~Private(); SlideInterface::Location pendingLocation; SlideInterface::Location currentLocation; uint32_t pendingOffset; uint32_t currentOffset; private: static void commitCallback(wl_client *client, wl_resource *resource); static void setLocationCallback(wl_client *client, wl_resource *resource, uint32_t location); static void setOffsetCallback(wl_client *client, wl_resource *resource, int32_t offset); SlideInterface *q_func() { return reinterpret_cast(q); } static const struct org_kde_kwin_slide_interface s_interface; }; #ifndef K_DOXYGEN const struct org_kde_kwin_slide_interface SlideInterface::Private::s_interface = { commitCallback, setLocationCallback, setOffsetCallback, resourceDestroyedCallback }; #endif void SlideInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); p->currentLocation = p->pendingLocation; p->currentOffset = p->pendingOffset; } void SlideInterface::Private::setLocationCallback(wl_client *client, wl_resource *resource, uint32_t location) { Q_UNUSED(client) Private *p = cast(resource); p->pendingLocation = (SlideInterface::Location)location; } void SlideInterface::Private::setOffsetCallback(wl_client *client, wl_resource *resource, int32_t offset) { Q_UNUSED(client) Private *p = cast(resource); p->pendingOffset = offset; } SlideInterface::Private::Private(SlideInterface *q, SlideManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &org_kde_kwin_slide_interface, &s_interface) { } SlideInterface::Private::~Private() = default; SlideInterface::SlideInterface(SlideManagerInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } SlideInterface::~SlideInterface() = default; SlideInterface::Location SlideInterface::location() const { Q_D(); return d->currentLocation; } qint32 SlideInterface::offset() const { Q_D(); return d->currentOffset; } SlideInterface::Private *SlideInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/slide_interface.h b/src/server/slide_interface.h index 07fc513..209efa3 100644 --- a/src/server/slide_interface.h +++ b/src/server/slide_interface.h @@ -1,89 +1,75 @@ -/**************************************************************************** -Copyright 2015 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_SLIDE_INTERFACE_H #define KWAYLAND_SERVER_SLIDE_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; /** * TODO */ class KWAYLANDSERVER_EXPORT SlideManagerInterface : public Global { Q_OBJECT public: virtual ~SlideManagerInterface(); private: explicit SlideManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * TODO */ class KWAYLANDSERVER_EXPORT SlideInterface : public Resource { Q_OBJECT public: enum Location { Left = 0, /**< Slide from the left edge of the screen */ Top, /**< Slide from the top edge of the screen */ Right, /**< Slide from the bottom edge of the screen */ Bottom /**< Slide from the bottom edge of the screen */ }; virtual ~SlideInterface(); /** * @returns the location the window will be slided from */ Location location() const; /** * @returns the offset from the screen edge the window will * be slided from */ qint32 offset() const; private: explicit SlideInterface(SlideManagerInterface *parent, wl_resource *parentResource); friend class SlideManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/subcompositor_interface.cpp b/src/server/subcompositor_interface.cpp index 2b11933..f50b4ed 100644 --- a/src/server/subcompositor_interface.cpp +++ b/src/server/subcompositor_interface.cpp @@ -1,378 +1,364 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "subcompositor_interface.h" #include "subsurface_interface_p.h" #include "global_p.h" #include "display.h" #include "surface_interface_p.h" // Wayland #include namespace KWayland { namespace Server { class SubCompositorInterface::Private : public Global::Private { public: Private(SubCompositorInterface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; void subsurface(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent); static void unbind(wl_resource *resource); static void destroyCallback(wl_client *client, wl_resource *resource); static void subsurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *parent); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } SubCompositorInterface *q; static const struct wl_subcompositor_interface s_interface; static const quint32 s_version; }; const quint32 SubCompositorInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct wl_subcompositor_interface SubCompositorInterface::Private::s_interface = { destroyCallback, subsurfaceCallback }; #endif SubCompositorInterface::Private::Private(SubCompositorInterface *q, Display *d) : Global::Private(d, &wl_subcompositor_interface, s_version) , q(q) { } void SubCompositorInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_subcompositor_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); } void SubCompositorInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } void SubCompositorInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Q_UNUSED(resource) wl_resource_destroy(resource); } void SubCompositorInterface::Private::subsurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface, wl_resource *sparent) { cast(resource)->subsurface(client, resource, id, surface, sparent); } void SubCompositorInterface::Private::subsurface(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *nativeSurface, wl_resource *nativeParentSurface) { Q_UNUSED(client) SurfaceInterface *surface = SurfaceInterface::get(nativeSurface); SurfaceInterface *parentSurface = SurfaceInterface::get(nativeParentSurface); if (!surface || !parentSurface) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Surface or parent surface not found"); return; } if (surface == parentSurface) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Cannot become sub composite to same surface"); return; } // TODO: add check that surface is not already used in an interface (e.g. Shell) // TODO: add check that parentSurface is not a child of surface SubSurfaceInterface *s = new SubSurfaceInterface(q, resource); s->d_func()->create(display->getConnection(client), wl_resource_get_version(resource), id, surface, parentSurface); if (!s->resource()) { wl_resource_post_no_memory(resource); delete s; return; } emit q->subSurfaceCreated(s); } SubCompositorInterface::SubCompositorInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } SubCompositorInterface::~SubCompositorInterface() = default; #ifndef K_DOXYGEN const struct wl_subsurface_interface SubSurfaceInterface::Private::s_interface = { resourceDestroyedCallback, setPositionCallback, placeAboveCallback, placeBelowCallback, setSyncCallback, setDeSyncCallback }; #endif SubSurfaceInterface::Private::Private(SubSurfaceInterface *q, SubCompositorInterface *compositor, wl_resource *parentResource) : Resource::Private(q, compositor, parentResource, &wl_subsurface_interface, &s_interface) { } SubSurfaceInterface::Private::~Private() { // no need to notify the surface as it's tracking a QPointer which will be reset automatically if (parent) { Q_Q(SubSurfaceInterface); reinterpret_cast(parent->d.data())->removeChild(QPointer(q)); } } void SubSurfaceInterface::Private::create(ClientConnection *client, quint32 version, quint32 id, SurfaceInterface *s, SurfaceInterface *p) { create(client, version, id); if (!resource) { return; } surface = s; parent = p; Q_Q(SubSurfaceInterface); surface->d_func()->subSurface = QPointer(q); // copy current state to subSurfacePending state // it's the reference for all new pending state which needs to be committed surface->d_func()->subSurfacePending = surface->d_func()->current; surface->d_func()->subSurfacePending.blurIsSet = false; surface->d_func()->subSurfacePending.bufferIsSet = false; surface->d_func()->subSurfacePending.childrenChanged = false; surface->d_func()->subSurfacePending.contrastIsSet = false; surface->d_func()->subSurfacePending.callbacks.clear(); surface->d_func()->subSurfacePending.inputIsSet = false; surface->d_func()->subSurfacePending.inputIsInfinite = true; surface->d_func()->subSurfacePending.opaqueIsSet = false; surface->d_func()->subSurfacePending.shadowIsSet = false; surface->d_func()->subSurfacePending.slideIsSet = false; parent->d_func()->addChild(QPointer(q)); QObject::connect(surface.data(), &QObject::destroyed, q, [this] { // from spec: "If the wl_surface associated with the wl_subsurface is destroyed, // the wl_subsurface object becomes inert. Note, that destroying either object // takes effect immediately." if (parent) { Q_Q(SubSurfaceInterface); reinterpret_cast(parent->d.data())->removeChild(QPointer(q)); } } ); } void SubSurfaceInterface::Private::commit() { if (scheduledPosChange) { scheduledPosChange = false; pos = scheduledPos; scheduledPos = QPoint(); Q_Q(SubSurfaceInterface); emit q->positionChanged(pos); } if (surface) { surface->d_func()->commitSubSurface(); } } void SubSurfaceInterface::Private::setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { Q_UNUSED(client) // TODO: is this a fixed position? cast(resource)->setPosition(QPoint(x, y)); } void SubSurfaceInterface::Private::setPosition(const QPoint &p) { Q_Q(SubSurfaceInterface); if (!q->isSynchronized()) { // workaround for https://bugreports.qt.io/browse/QTBUG-52118 // apply directly as Qt doesn't commit the parent surface pos = p; emit q->positionChanged(pos); return; } if (scheduledPos == p) { return; } scheduledPos = p; scheduledPosChange = true; } void SubSurfaceInterface::Private::placeAboveCallback(wl_client *client, wl_resource *resource, wl_resource *sibling) { Q_UNUSED(client) cast(resource)->placeAbove(SurfaceInterface::get(sibling)); } void SubSurfaceInterface::Private::placeAbove(SurfaceInterface *sibling) { if (parent.isNull()) { // TODO: raise error return; } Q_Q(SubSurfaceInterface); if (!parent->d_func()->raiseChild(QPointer(q), sibling)) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling"); } } void SubSurfaceInterface::Private::placeBelowCallback(wl_client *client, wl_resource *resource, wl_resource *sibling) { Q_UNUSED(client) cast(resource)->placeBelow(SurfaceInterface::get(sibling)); } void SubSurfaceInterface::Private::placeBelow(SurfaceInterface *sibling) { if (parent.isNull()) { // TODO: raise error return; } Q_Q(SubSurfaceInterface); if (!parent->d_func()->lowerChild(QPointer(q), sibling)) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Incorrect sibling"); } } void SubSurfaceInterface::Private::setSyncCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->setMode(Mode::Synchronized); } void SubSurfaceInterface::Private::setDeSyncCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->setMode(Mode::Desynchronized); } void SubSurfaceInterface::Private::setMode(Mode m) { if (mode == m) { return; } if (m == Mode::Desynchronized && (!parent->subSurface() || !parent->subSurface()->isSynchronized())) { // no longer synchronized, this is like calling commit if (surface) { surface->d_func()->commit(); surface->d_func()->commitSubSurface(); } } mode = m; Q_Q(SubSurfaceInterface); emit q->modeChanged(m); } SubSurfaceInterface::SubSurfaceInterface(SubCompositorInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { Q_UNUSED(parent) } SubSurfaceInterface::~SubSurfaceInterface() = default; QPoint SubSurfaceInterface::position() const { Q_D(); return d->pos; } QPointer SubSurfaceInterface::surface() { // TODO: remove with ABI break (KF6) Q_D(); return d->surface; } QPointer SubSurfaceInterface::surface() const { Q_D(); return d->surface; } QPointer SubSurfaceInterface::parentSurface() { // TODO: remove with ABI break (KF6) Q_D(); return d->parent; } QPointer SubSurfaceInterface::parentSurface() const { Q_D(); return d->parent; } SubSurfaceInterface::Mode SubSurfaceInterface::mode() const { Q_D(); return d->mode; } bool SubSurfaceInterface::isSynchronized() const { Q_D(); if (d->mode == Mode::Synchronized) { return true; } if (d->parent.isNull()) { // that shouldn't happen, but let's assume false return false; } if (!d->parent->subSurface().isNull()) { // follow parent's mode return d->parent->subSurface()->isSynchronized(); } // parent is no subsurface, thus parent is in desync mode and this surface is in desync mode return false; } QPointer SubSurfaceInterface::mainSurface() const { Q_D(); if (!d->parent) { return QPointer(); } if (d->parent->d_func()->subSurface) { return d->parent->d_func()->subSurface->mainSurface(); } return d->parent; } SubSurfaceInterface::Private *SubSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/subcompositor_interface.h b/src/server/subcompositor_interface.h index 74e7684..c85ecf0 100644 --- a/src/server/subcompositor_interface.h +++ b/src/server/subcompositor_interface.h @@ -1,130 +1,116 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SUBCOMPOSITOR_INTERFACE_H #define WAYLAND_SERVER_SUBCOMPOSITOR_INTERFACE_H #include #include #include #include "global.h" #include "resource.h" struct wl_resource; namespace KWayland { namespace Server { class Display; class SurfaceInterface; class SubSurfaceInterface; /** * @todo Add documentation */ class KWAYLANDSERVER_EXPORT SubCompositorInterface : public Global { Q_OBJECT public: virtual ~SubCompositorInterface(); Q_SIGNALS: void subSurfaceCreated(KWayland::Server::SubSurfaceInterface*); private: explicit SubCompositorInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * @todo Add documentation */ class KWAYLANDSERVER_EXPORT SubSurfaceInterface : public Resource { Q_OBJECT Q_PROPERTY(QPoint position READ position NOTIFY positionChanged) Q_PROPERTY(KWayland::Server::SubSurfaceInterface::Mode mode READ mode NOTIFY modeChanged) public: virtual ~SubSurfaceInterface(); QPoint position() const; enum class Mode { Synchronized, Desynchronized }; Mode mode() const; /** * Whether this SubSurfaceInterface is in synchronized mode. * A SubSurface is in synchronized mode if either {@link mode} is * @c Mode::Synchronized or if the parent surface is in synchronized * mode. If a SubSurfaceInterface is in synchronized mode all child * SubSurfaceInterfaces are also in synchronized mode ignoring the actual mode. * @returns Whether this SubSurfaceInterface is in synchronized mode. * @see mode * @since 5.22 **/ bool isSynchronized() const; // TODO: remove with ABI break (KF6) QPointer surface(); /** * @returns The surface this SubSurfaceInterface was created on. * @since 5.22 **/ QPointer surface() const; // TODO: remove with ABI break (KF6) QPointer parentSurface(); /** * @returns The parent surface for which this SubSurfaceInterface is a child * @since 5.22 **/ QPointer parentSurface() const; /** * @returns the main surface for the sub-surface tree, that is the first surface without a parent * @since 5.22 **/ QPointer mainSurface() const; Q_SIGNALS: void positionChanged(const QPoint&); void modeChanged(KWayland::Server::SubSurfaceInterface::Mode); private: friend class SubCompositorInterface; friend class SurfaceInterface; explicit SubSurfaceInterface(SubCompositorInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::SubSurfaceInterface::Mode) #endif diff --git a/src/server/subsurface_interface_p.h b/src/server/subsurface_interface_p.h index c74a0c7..b86e7ea 100644 --- a/src/server/subsurface_interface_p.h +++ b/src/server/subsurface_interface_p.h @@ -1,73 +1,59 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SUBSURFACE_INTERFACE_P_H #define WAYLAND_SERVER_SUBSURFACE_INTERFACE_P_H #include "subcompositor_interface.h" #include "resource_p.h" // Qt #include // Wayland #include namespace KWayland { namespace Server { class SubSurfaceInterface::Private : public Resource::Private { public: Private(SubSurfaceInterface *q, SubCompositorInterface *compositor, wl_resource *parentResource); ~Private(); using Resource::Private::create; void create(ClientConnection *client, quint32 version, quint32 id, SurfaceInterface *surface, SurfaceInterface *parent); void commit(); QPoint pos = QPoint(0, 0); QPoint scheduledPos = QPoint(); bool scheduledPosChange = false; Mode mode = Mode::Synchronized; QPointer surface; QPointer parent; private: SubSurfaceInterface *q_func() { return reinterpret_cast(q); } void setMode(Mode mode); void setPosition(const QPoint &pos); void placeAbove(SurfaceInterface *sibling); void placeBelow(SurfaceInterface *sibling); static void setPositionCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static void placeAboveCallback(wl_client *client, wl_resource *resource, wl_resource *sibling); static void placeBelowCallback(wl_client *client, wl_resource *resource, wl_resource *sibling); static void setSyncCallback(wl_client *client, wl_resource *resource); static void setDeSyncCallback(wl_client *client, wl_resource *resource); static const struct wl_subsurface_interface s_interface; }; } } #endif diff --git a/src/server/surface_interface.cpp b/src/server/surface_interface.cpp index 05988ac..0690b19 100644 --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -1,971 +1,957 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "surface_interface.h" #include "surface_interface_p.h" #include "buffer_interface.h" #include "clientconnection.h" #include "compositor_interface.h" #include "idleinhibit_interface_p.h" #include "pointerconstraints_interface_p.h" #include "region_interface.h" #include "subcompositor_interface.h" #include "subsurface_interface_p.h" #include "surfacerole_p.h" // Qt #include // Wayland #include // std #include namespace KWayland { namespace Server { SurfaceInterface::Private::Private(SurfaceInterface *q, CompositorInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &wl_surface_interface, &s_interface) { } SurfaceInterface::Private::~Private() { destroy(); } void SurfaceInterface::Private::addChild(QPointer< SubSurfaceInterface > child) { // protocol is not precise on how to handle the addition of new sub surfaces pending.children.append(child); subSurfacePending.children.append(child); current.children.append(child); Q_Q(SurfaceInterface); emit q->subSurfaceTreeChanged(); QObject::connect(child.data(), &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::connect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); } void SurfaceInterface::Private::removeChild(QPointer< SubSurfaceInterface > child) { // protocol is not precise on how to handle the addition of new sub surfaces pending.children.removeAll(child); subSurfacePending.children.removeAll(child); current.children.removeAll(child); Q_Q(SurfaceInterface); emit q->subSurfaceTreeChanged(); QObject::disconnect(child.data(), &SubSurfaceInterface::positionChanged, q, &SurfaceInterface::subSurfaceTreeChanged); if (!child->surface().isNull()) { QObject::disconnect(child->surface().data(), &SurfaceInterface::damaged, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::disconnect(child->surface().data(), &SurfaceInterface::unmapped, q, &SurfaceInterface::subSurfaceTreeChanged); QObject::disconnect(child->surface().data(), &SurfaceInterface::subSurfaceTreeChanged, q, &SurfaceInterface::subSurfaceTreeChanged); } } bool SurfaceInterface::Private::raiseChild(QPointer subsurface, SurfaceInterface *sibling) { Q_Q(SurfaceInterface); auto it = std::find(pending.children.begin(), pending.children.end(), subsurface); if (it == pending.children.end()) { return false; } if (pending.children.count() == 1) { // nothing to do return true; } if (sibling == q) { // it's to the parent, so needs to become last item pending.children.append(*it); pending.children.erase(it); pending.childrenChanged = true; return true; } if (!sibling->subSurface()) { // not a sub surface return false; } auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); if (siblingIt == pending.children.end() || siblingIt == it) { // not a sibling return false; } auto value = (*it); pending.children.erase(it); // find the iterator again siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); pending.children.insert(++siblingIt, value); pending.childrenChanged = true; return true; } bool SurfaceInterface::Private::lowerChild(QPointer subsurface, SurfaceInterface *sibling) { Q_Q(SurfaceInterface); auto it = std::find(pending.children.begin(), pending.children.end(), subsurface); if (it == pending.children.end()) { return false; } if (pending.children.count() == 1) { // nothing to do return true; } if (sibling == q) { // it's to the parent, so needs to become first item auto value = *it; pending.children.erase(it); pending.children.prepend(value); pending.childrenChanged = true; return true; } if (!sibling->subSurface()) { // not a sub surface return false; } auto siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); if (siblingIt == pending.children.end() || siblingIt == it) { // not a sibling return false; } auto value = (*it); pending.children.erase(it); // find the iterator again siblingIt = std::find(pending.children.begin(), pending.children.end(), sibling->subSurface()); pending.children.insert(siblingIt, value); pending.childrenChanged = true; return true; } void SurfaceInterface::Private::setShadow(const QPointer &shadow) { pending.shadow = shadow; pending.shadowIsSet = true; } void SurfaceInterface::Private::setBlur(const QPointer &blur) { pending.blur = blur; pending.blurIsSet = true; } void SurfaceInterface::Private::setSlide(const QPointer &slide) { pending.slide = slide; pending.slideIsSet = true; } void SurfaceInterface::Private::setContrast(const QPointer &contrast) { pending.contrast = contrast; pending.contrastIsSet = true; } void SurfaceInterface::Private::installPointerConstraint(LockedPointerInterface *lock) { Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); lockedPointer = QPointer(lock); auto cleanUp = [this]() { lockedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); }; if (lock->lifeTime() == LockedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(lock, &LockedPointerInterface::lockedChanged, q_func(), [this, cleanUp] { if (lockedPointer.isNull() || lockedPointer->isLocked()) { return; } cleanUp(); } ); } constrainsUnboundConnection = QObject::connect(lock, &LockedPointerInterface::unbound, q_func(), [this, cleanUp] { if (lockedPointer.isNull()) { return; } cleanUp(); } ); emit q_func()->pointerConstraintsChanged(); } void SurfaceInterface::Private::installPointerConstraint(ConfinedPointerInterface *confinement) { Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); confinedPointer = QPointer(confinement); auto cleanUp = [this]() { confinedPointer.clear(); disconnect(constrainsOneShotConnection); constrainsOneShotConnection = QMetaObject::Connection(); disconnect(constrainsUnboundConnection); constrainsUnboundConnection = QMetaObject::Connection(); emit q_func()->pointerConstraintsChanged(); }; if (confinement->lifeTime() == ConfinedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(confinement, &ConfinedPointerInterface::confinedChanged, q_func(), [this, cleanUp] { if (confinedPointer.isNull() || confinedPointer->isConfined()) { return; } cleanUp(); } ); } constrainsUnboundConnection = QObject::connect(confinement, &ConfinedPointerInterface::unbound, q_func(), [this, cleanUp] { if (confinedPointer.isNull()) { return; } cleanUp(); } ); emit q_func()->pointerConstraintsChanged(); } void SurfaceInterface::Private::installIdleInhibitor(IdleInhibitorInterface *inhibitor) { idleInhibitors << inhibitor; QObject::connect(inhibitor, &IdleInhibitorInterface::aboutToBeUnbound, q, [this, inhibitor] { idleInhibitors.removeOne(inhibitor); if (idleInhibitors.isEmpty()) { emit q_func()->inhibitsIdleChanged(); } } ); if (idleInhibitors.count() == 1) { emit q_func()->inhibitsIdleChanged(); } } #ifndef K_DOXYGEN const struct wl_surface_interface SurfaceInterface::Private::s_interface = { resourceDestroyedCallback, attachCallback, damageCallback, frameCallback, opaqueRegionCallback, inputRegionCallback, commitCallback, bufferTransformCallback, bufferScaleCallback, damageBufferCallback }; #endif SurfaceInterface::SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } SurfaceInterface::~SurfaceInterface() = default; void SurfaceInterface::frameRendered(quint32 msec) { Q_D(); // notify all callbacks const bool needsFlush = !d->current.callbacks.isEmpty(); while (!d->current.callbacks.isEmpty()) { wl_resource *r = d->current.callbacks.takeFirst(); wl_callback_send_done(r, msec); wl_resource_destroy(r); } for (auto it = d->current.children.constBegin(); it != d->current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull() || subSurface->d_func()->surface.isNull()) { continue; } subSurface->d_func()->surface->frameRendered(msec); } if (needsFlush) { client()->flush(); } } void SurfaceInterface::Private::destroy() { // copy all existing callbacks to new list and clear existing lists // the wl_resource_destroy on the callback resource goes into destroyFrameCallback // which would modify the list we are iterating on QList callbacksToDestroy; callbacksToDestroy << current.callbacks; current.callbacks.clear(); callbacksToDestroy << pending.callbacks; pending.callbacks.clear(); callbacksToDestroy << subSurfacePending.callbacks; subSurfacePending.callbacks.clear(); for (auto it = callbacksToDestroy.constBegin(), end = callbacksToDestroy.constEnd(); it != end; it++) { wl_resource_destroy(*it); } if (current.buffer) { current.buffer->unref(); } } void SurfaceInterface::Private::swapStates(State *source, State *target, bool emitChanged) { Q_Q(SurfaceInterface); bool bufferChanged = source->bufferIsSet; const bool opaqueRegionChanged = source->opaqueIsSet; const bool inputRegionChanged = source->inputIsSet; const bool scaleFactorChanged = source->scaleIsSet && (target->scale != source->scale); const bool transformChanged = source->transformIsSet && (target->transform != source->transform); const bool shadowChanged = source->shadowIsSet; const bool blurChanged = source->blurIsSet; const bool contrastChanged = source->contrastIsSet; const bool slideChanged = source->slideIsSet; const bool childrenChanged = source->childrenChanged; bool sizeChanged = false; auto buffer = target->buffer; if (bufferChanged) { // TODO: is the reffing correct for subsurfaces? QSize oldSize; if (target->buffer) { oldSize = target->buffer->size(); if (emitChanged) { target->buffer->unref(); QObject::disconnect(target->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged); } else { delete target->buffer; target->buffer = nullptr; } } if (source->buffer) { if (emitChanged) { source->buffer->ref(); QObject::connect(source->buffer, &BufferInterface::sizeChanged, q, &SurfaceInterface::sizeChanged); } const QSize newSize = source->buffer->size(); sizeChanged = newSize.isValid() && newSize != oldSize; } if (!target->buffer && !source->buffer && emitChanged) { // null buffer set on a not mapped surface, don't emit unmapped bufferChanged = false; } buffer = source->buffer; } // copy values if (bufferChanged) { target->buffer = buffer; target->offset = source->offset; target->damage = source->damage; target->bufferDamage = source->bufferDamage; target->bufferIsSet = source->bufferIsSet; } if (childrenChanged) { target->childrenChanged = source->childrenChanged; target->children = source->children; } target->callbacks.append(source->callbacks); if (shadowChanged) { target->shadow = source->shadow; target->shadowIsSet = true; } if (blurChanged) { target->blur = source->blur; target->blurIsSet = true; } if (contrastChanged) { target->contrast = source->contrast; target->contrastIsSet = true; } if (slideChanged) { target->slide = source->slide; target->slideIsSet = true; } if (inputRegionChanged) { target->input = source->input; target->inputIsInfinite = source->inputIsInfinite; target->inputIsSet = true; } if (opaqueRegionChanged) { target->opaque = source->opaque; target->opaqueIsSet = true; } if (scaleFactorChanged) { target->scale = source->scale; target->scaleIsSet = true; } if (transformChanged) { target->transform = source->transform; target->transformIsSet = true; } if (!lockedPointer.isNull()) { lockedPointer->d_func()->commit(); } if (!confinedPointer.isNull()) { confinedPointer->d_func()->commit(); } *source = State{}; source->children = target->children; if (opaqueRegionChanged) { emit q->opaqueChanged(target->opaque); } if (inputRegionChanged) { emit q->inputChanged(target->input); } if (scaleFactorChanged) { emit q->scaleChanged(target->scale); if (buffer && !sizeChanged) { emit q->sizeChanged(); } } if (transformChanged) { emit q->transformChanged(target->transform); } if (bufferChanged && emitChanged) { if (target->buffer && (!target->damage.isEmpty() || !target->bufferDamage.isEmpty())) { const QRegion windowRegion = QRegion(0, 0, q->size().width(), q->size().height()); if (!windowRegion.isEmpty()) { QRegion bufferDamage; if (!target->bufferDamage.isEmpty()) { typedef OutputInterface::Transform Tr; const Tr tr = target->transform; const qint32 sc = target->scale; if (tr == Tr::Rotated90 || tr == Tr::Rotated270 || tr == Tr::Flipped90 || tr == Tr::Flipped270) { // calculate transformed + scaled buffer damage for (const auto &rect : target->bufferDamage) { const auto add = QRegion(rect.x() / sc, rect.y() / sc, rect.height() / sc, rect.width() / sc); bufferDamage = bufferDamage.united(add); } } else if (sc != 1) { // calculate scaled buffer damage for (const auto &rect : target->bufferDamage) { const auto add = QRegion(rect.x() / sc, rect.y() / sc, rect.width() / sc, rect.height() / sc); bufferDamage = bufferDamage.united(add); } } else { bufferDamage = target->bufferDamage; } } target->damage = windowRegion.intersected(target->damage.united(bufferDamage)); if (emitChanged) { subSurfaceIsMapped = true; trackedDamage = trackedDamage.united(target->damage); emit q->damaged(target->damage); // workaround for https://bugreports.qt.io/browse/QTBUG-52092 // if the surface is a sub-surface, but the main surface is not yet mapped, fake frame rendered if (subSurface) { const auto mainSurface = subSurface->mainSurface(); if (!mainSurface || !mainSurface->buffer()) { q->frameRendered(0); } } } } } else if (!target->buffer && emitChanged) { subSurfaceIsMapped = false; emit q->unmapped(); } } if (!emitChanged) { return; } if (sizeChanged) { emit q->sizeChanged(); } if (shadowChanged) { emit q->shadowChanged(); } if (blurChanged) { emit q->blurChanged(); } if (contrastChanged) { emit q->contrastChanged(); } if (slideChanged) { emit q->slideOnShowHideChanged(); } if (childrenChanged) { emit q->subSurfaceTreeChanged(); } } void SurfaceInterface::Private::commit() { Q_Q(SurfaceInterface); if (!subSurface.isNull() && subSurface->isSynchronized()) { swapStates(&pending, &subSurfacePending, false); } else { swapStates(&pending, ¤t, true); if (!subSurface.isNull()) { subSurface->d_func()->commit(); } // commit all subSurfaces to apply position changes // "The cached state is applied to the sub-surface immediately after the parent surface's state is applied" for (auto it = current.children.constBegin(); it != current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull()) { continue; } subSurface->d_func()->commit(); } } if (role) { role->commit(); } emit q->committed(); } void SurfaceInterface::Private::commitSubSurface() { if (subSurface.isNull() || !subSurface->isSynchronized()) { return; } swapStates(&subSurfacePending, ¤t, true); // "The cached state is applied to the sub-surface immediately after the parent surface's state is applied" for (auto it = current.children.constBegin(); it != current.children.constEnd(); ++it) { const auto &subSurface = *it; if (subSurface.isNull() || !subSurface->isSynchronized()) { continue; } subSurface->d_func()->commit(); } } void SurfaceInterface::Private::damage(const QRect &rect) { pending.damage = pending.damage.united(rect); } void SurfaceInterface::Private::damageBuffer(const QRect &rect) { if (!pending.bufferIsSet || (pending.bufferIsSet && !pending.buffer)) { // TODO: should we send an error? return; } pending.bufferDamage = pending.bufferDamage.united(rect); } void SurfaceInterface::Private::setScale(qint32 scale) { pending.scale = scale; pending.scaleIsSet = true; } void SurfaceInterface::Private::setTransform(OutputInterface::Transform transform) { pending.transform = transform; } void SurfaceInterface::Private::addFrameCallback(uint32_t callback) { wl_resource *r = client->createResource(&wl_callback_interface, 1, callback); if (!r) { wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(r, nullptr, this, destroyFrameCallback); pending.callbacks << r; } void SurfaceInterface::Private::attachBuffer(wl_resource *buffer, const QPoint &offset) { pending.bufferIsSet = true; pending.offset = offset; if (pending.buffer) { delete pending.buffer; } if (!buffer) { // got a null buffer, deletes content in next frame pending.buffer = nullptr; pending.damage = QRegion(); pending.bufferDamage = QRegion(); return; } Q_Q(SurfaceInterface); pending.buffer = new BufferInterface(buffer, q); QObject::connect(pending.buffer, &BufferInterface::aboutToBeDestroyed, q, [this](BufferInterface *buffer) { if (pending.buffer == buffer) { pending.buffer = nullptr; } if (subSurfacePending.buffer == buffer) { subSurfacePending.buffer = nullptr; } if (current.buffer == buffer) { current.buffer->unref(); current.buffer = nullptr; } } ); } void SurfaceInterface::Private::destroyFrameCallback(wl_resource *r) { auto s = cast(r); s->current.callbacks.removeAll(r); s->pending.callbacks.removeAll(r); s->subSurfacePending.callbacks.removeAll(r); } void SurfaceInterface::Private::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy) { Q_UNUSED(client) cast(resource)->attachBuffer(buffer, QPoint(sx, sy)); } void SurfaceInterface::Private::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) cast(resource)->damage(QRect(x, y, width, height)); } void SurfaceInterface::Private::damageBufferCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) cast(resource)->damageBuffer(QRect(x, y, width, height)); } void SurfaceInterface::Private::frameCallback(wl_client *client, wl_resource *resource, uint32_t callback) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->addFrameCallback(callback); } void SurfaceInterface::Private::opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto r = RegionInterface::get(region); s->setOpaque(r ? r->region() : QRegion()); } void SurfaceInterface::Private::setOpaque(const QRegion ®ion) { pending.opaqueIsSet = true; pending.opaque = region; } void SurfaceInterface::Private::inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto r = RegionInterface::get(region); s->setInput(r ? r->region() : QRegion(), !r); } void SurfaceInterface::Private::setInput(const QRegion ®ion, bool isInfinite) { pending.inputIsSet = true; pending.inputIsInfinite = isInfinite; pending.input = region; } void SurfaceInterface::Private::commitCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) cast(resource)->commit(); } void SurfaceInterface::Private::bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform) { Q_UNUSED(client) cast(resource)->setTransform(OutputInterface::Transform(transform)); } void SurfaceInterface::Private::bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale) { Q_UNUSED(client) cast(resource)->setScale(scale); } QRegion SurfaceInterface::damage() const { Q_D(); return d->current.damage; } QRegion SurfaceInterface::opaque() const { Q_D(); return d->current.opaque; } QRegion SurfaceInterface::input() const { Q_D(); return d->current.input; } bool SurfaceInterface::inputIsInfitine() const { return inputIsInfinite(); } bool SurfaceInterface::inputIsInfinite() const { Q_D(); return d->current.inputIsInfinite; } qint32 SurfaceInterface::scale() const { Q_D(); return d->current.scale; } OutputInterface::Transform SurfaceInterface::transform() const { Q_D(); return d->current.transform; } BufferInterface *SurfaceInterface::buffer() { Q_D(); return d->current.buffer; } QPoint SurfaceInterface::offset() const { Q_D(); return d->current.offset; } SurfaceInterface *SurfaceInterface::get(wl_resource *native) { return Private::get(native); } SurfaceInterface *SurfaceInterface::get(quint32 id, const ClientConnection *client) { return Private::get(id, client); } QList< QPointer< SubSurfaceInterface > > SurfaceInterface::childSubSurfaces() const { Q_D(); return d->current.children; } QPointer< SubSurfaceInterface > SurfaceInterface::subSurface() const { Q_D(); return d->subSurface; } QSize SurfaceInterface::size() const { Q_D(); // TODO: apply transform to the buffer size if (d->current.buffer) { return d->current.buffer->size() / scale(); } return QSize(); } QPointer< ShadowInterface > SurfaceInterface::shadow() const { Q_D(); return d->current.shadow; } QPointer< BlurInterface > SurfaceInterface::blur() const { Q_D(); return d->current.blur; } QPointer< ContrastInterface > SurfaceInterface::contrast() const { Q_D(); return d->current.contrast; } QPointer< SlideInterface > SurfaceInterface::slideOnShowHide() const { Q_D(); return d->current.slide; } bool SurfaceInterface::isMapped() const { Q_D(); if (d->subSurface) { // from spec: // "A sub-surface becomes mapped, when a non-NULL wl_buffer is applied and the parent surface is mapped." return d->subSurfaceIsMapped && !d->subSurface->parentSurface().isNull() && d->subSurface->parentSurface()->isMapped(); } return d->current.buffer != nullptr; } QRegion SurfaceInterface::trackedDamage() const { Q_D(); return d->trackedDamage; } void SurfaceInterface::resetTrackedDamage() { Q_D(); d->trackedDamage = QRegion(); } QVector SurfaceInterface::outputs() const { Q_D(); return d->outputs; } void SurfaceInterface::setOutputs(const QVector &outputs) { Q_D(); QVector removedOutputs = d->outputs; for (auto it = outputs.constBegin(), end = outputs.constEnd(); it != end; ++it) { const auto o = *it; removedOutputs.removeOne(o); } for (auto it = removedOutputs.constBegin(), end = removedOutputs.constEnd(); it != end; ++it) { const auto resources = (*it)->clientResources(client()); for (wl_resource *r : resources) { wl_surface_send_leave(d->resource, r); } disconnect(d->outputDestroyedConnections.take(*it)); } QVector addedOutputsOutputs = outputs; for (auto it = d->outputs.constBegin(), end = d->outputs.constEnd(); it != end; ++it) { const auto o = *it; addedOutputsOutputs.removeOne(o); } for (auto it = addedOutputsOutputs.constBegin(), end = addedOutputsOutputs.constEnd(); it != end; ++it) { const auto o = *it; const auto resources = o->clientResources(client()); for (wl_resource *r : resources) { wl_surface_send_enter(d->resource, r); } d->outputDestroyedConnections[o] = connect(o, &Global::aboutToDestroyGlobal, this, [this, o] { Q_D(); auto outputs = d->outputs; if (outputs.removeOne(o)) { setOutputs(outputs); }}); } // TODO: send enter when the client binds the OutputInterface another time d->outputs = outputs; } SurfaceInterface *SurfaceInterface::surfaceAt(const QPointF &position) { if (!isMapped()) { return nullptr; } Q_D(); // go from top to bottom. Top most child is last in list QListIterator> it(d->current.children); it.toBack(); while (it.hasPrevious()) { const auto ¤t = it.previous(); auto surface = current->surface(); if (surface.isNull()) { continue; } if (auto s = surface->surfaceAt(position - current->position())) { return s; } } // check whether the geometry contains the pos if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position)) { return this; } return nullptr; } SurfaceInterface *SurfaceInterface::inputSurfaceAt(const QPointF &position) { // TODO: Most of this is very similar to SurfaceInterface::surfaceAt // Is there a way to reduce the code duplication? if (!isMapped()) { return nullptr; } Q_D(); // go from top to bottom. Top most child is last in list QListIterator> it(d->current.children); it.toBack(); while (it.hasPrevious()) { const auto ¤t = it.previous(); auto surface = current->surface(); if (surface.isNull()) { continue; } if (auto s = surface->inputSurfaceAt(position - current->position())) { return s; } } // check whether the geometry and input region contain the pos if (!size().isEmpty() && QRectF(QPoint(0, 0), size()).contains(position) && (inputIsInfinite() || input().contains(position.toPoint()))) { return this; } return nullptr; } QPointer SurfaceInterface::lockedPointer() const { Q_D(); return d->lockedPointer; } QPointer SurfaceInterface::confinedPointer() const { Q_D(); return d->confinedPointer; } bool SurfaceInterface::inhibitsIdle() const { Q_D(); return !d->idleInhibitors.isEmpty(); } void SurfaceInterface::setDataProxy(SurfaceInterface *surface) { Q_D(); d->dataProxy = surface; } SurfaceInterface* SurfaceInterface::dataProxy() const { Q_D(); return d->dataProxy; } SurfaceInterface::Private *SurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/surface_interface.h b/src/server/surface_interface.h index 5b112a8..632bbc8 100644 --- a/src/server/surface_interface.h +++ b/src/server/surface_interface.h @@ -1,389 +1,375 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SURFACE_INTERFACE_H #define WAYLAND_SERVER_SURFACE_INTERFACE_H #include "resource.h" #include "output_interface.h" #include #include #include #include namespace KWayland { namespace Server { class BlurManagerInterface; class BlurInterface; class BufferInterface; class ConfinedPointerInterface; class ContrastInterface; class ContrastManagerInterface; class CompositorInterface; class IdleInhibitManagerUnstableV1Interface; class LockedPointerInterface; class PointerConstraintsUnstableV1Interface; class ShadowManagerInterface; class ShadowInterface; class SlideInterface; class SubSurfaceInterface; /** * @brief Resource representing a wl_surface. * * The SurfaceInterface gets created by the CompositorInterface. A SurfaceInterface normally * takes up a role by being "attached" to either a ShellSurfaceInterface, a SubSurfaceInterface * or a Cursor. * * The implementation of the SurfaceInterface does not only wrap the features exposed by wl_surface, * but goes further by integrating the information added to a SurfaceInterface by other interfaces. * This should make interacting from the server easier, it only needs to monitor the SurfaceInterface * and does not need to track each specific interface. * * The SurfaceInterface takes care of reference/unreferencing the BufferInterface attached to it. * As long as a BufferInterface is attached, the released signal won't be sent. If the BufferInterface * is no longer needed by the SurfaceInterface, it will get unreferenced and might be automatically * deleted (if it's no longer referenced). * * @see CompositorInterface * @see BufferInterface * @see SubSurfaceInterface * @see BlurInterface * @see ContrastInterface * @see ShadowInterface * @see SlideInterface **/ class KWAYLANDSERVER_EXPORT SurfaceInterface : public Resource { Q_OBJECT /** * The current damage region. **/ Q_PROPERTY(QRegion damage READ damage NOTIFY damaged) /** * The opaque region for a translucent buffer. **/ Q_PROPERTY(QRegion opaque READ opaque NOTIFY opaqueChanged) /** * The current input region. **/ Q_PROPERTY(QRegion input READ input NOTIFY inputChanged) Q_PROPERTY(qint32 scale READ scale NOTIFY scaleChanged) Q_PROPERTY(KWayland::Server::OutputInterface::Transform transform READ transform NOTIFY transformChanged) Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) public: virtual ~SurfaceInterface(); void frameRendered(quint32 msec); QRegion damage() const; QRegion opaque() const; QRegion input() const; #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 5) /** * Use Surface::inputIsInfinite instead. * @deprecated Since 5.5, use inputIsInfinite */ KWAYLANDSERVER_DEPRECATED_VERSION(5, 5, "Use SurfaceInterface::inputIsInfinite()") bool inputIsInfitine() const; #endif /** * Replaces Surface::inputIsInfitine instead. * @since 5.5 */ bool inputIsInfinite() const; qint32 scale() const; OutputInterface::Transform transform() const; /** * @returns the current BufferInterface, might be @c nullptr. **/ BufferInterface *buffer(); QPoint offset() const; /** * The size of the Surface in global compositor space. * @see For buffer size use BufferInterface::size * from SurfaceInterface::buffer * @since 5.3 **/ QSize size() const; /** * @returns The SubSurface for this Surface in case there is one. **/ QPointer subSurface() const; /** * @returns Children in stacking order from bottom (first) to top (last). **/ QList> childSubSurfaces() const; /** * @returns The Shadow for this Surface. * @since 5.4 **/ QPointer shadow() const; /** * @returns The Blur for this Surface. * @since 5.5 **/ QPointer blur() const; /** * @returns The Slide for this Surface. * @since 5.5 **/ QPointer slideOnShowHide() const; /** * @returns The Contrast for this Surface. * @since 5.5 **/ QPointer contrast() const; /** * Whether the SurfaceInterface is currently considered to be mapped. * A SurfaceInterface is mapped if it has a non-null BufferInterface attached. * If the SurfaceInterface references a SubSurfaceInterface it is only considered * mapped if it has a BufferInterface attached and the parent SurfaceInterface is mapped. * * @returns Whether the SurfaceInterface is currently mapped * @since 5.22 **/ bool isMapped() const; /** * Returns the tracked damage since the last call to {@link resetTrackedDamage}. * In contrast to {@link damage} this method does not reset the damage when * a new BufferInterface gets committed. This allows a compositor to properly * track the damage over multiple commits even if it didn't render each new * BufferInterface. * * The damage gets reset whenever {@link resetTrackedDamage} is called. * This allows a compositor to properly track the change in its rendering scene * for this SurfaceInterface. After it updates its internal state (e.g. by creating * an OpenGL texture from the BufferInterface) it can invoke {@link resetTrackedDamage} * and the damage tracker will start to track further damage changes. * * @returns Combined damage since last call to resetTrackedDamage * @see damage * @see resetTrackedDamage * @since 5.22 **/ QRegion trackedDamage() const; /** * Reset the damage tracking. The compositor should invoke this method once it updated * it's internal state and processed the current damage. * @see trackedDamage * @since 5.22 **/ void resetTrackedDamage(); /** * Finds the SurfaceInterface at the given @p position in surface-local coordinates. * This can be either a descendant SurfaceInterface honoring the stacking order or * the SurfaceInterface itself if its geometry contains the given @p position. * * If no such SurfaceInterface is found, e.g. because the SurfaceInterface is unmapped, * @c nullptr is returned. * * @param position The position in surface-local coordinates * @returns Child surface at the given @p position or surface itself at the position, might be @c nullptr * @since 5.22 **/ SurfaceInterface *surfaceAt(const QPointF &position); /** * Finds the input receiving SurfaceInterface at the given @p position in surface-local coordinates. * This can be either a descendant SurfaceInterface honoring the stacking order or * the SurfaceInterface itself if its geometry contains the given @p position. * * If no such SurfaceInterface is found, e.g. because the SurfaceInterface is unmapped or there is no * input region containing the position, * @c nullptr is returned. * * @param position The position in surface-local coordinates * @returns Input receiving child surface at the given @p position or surface itself at the position, might be @c nullptr * @since 5.52 **/ SurfaceInterface *inputSurfaceAt(const QPointF &position); /** * Sets the @p outputs this SurfaceInterface overlaps with, may be empty. * * The compositor should update whenever the SurfaceInterface becomes visible on * an OutputInterface by e.g. getting (un)mapped, resized, moved, etc. * * @see outputs * @since 5.27 **/ void setOutputs(const QVector &outputs); /** * @returns All OutputInterfaces the SurfaceInterface is on. * @see setOutputs * @since 5.27 **/ QVector outputs() const; /** * Pointer confinement installed on this SurfaceInterface. * @see pointerConstraintsChanged * @since 5.29 **/ QPointer confinedPointer() const; /** * Pointer lock installed on this SurfaceInterface. * @see pointerConstraintsChanged * @since 5.29 **/ QPointer lockedPointer() const; /** * @returns Whether this SurfaceInterface wants idle to be inhibited on the Output it is shown * @see inhibitsIdleChanged * @since 5.41 **/ bool inhibitsIdle() const; /** * @returns The SurfaceInterface for the @p native resource. **/ static SurfaceInterface *get(wl_resource *native); /** * @returns The SurfaceInterface with given @p id for @p client, if it exists, otherwise @c nullptr. * @since 5.3 **/ static SurfaceInterface *get(quint32 id, const ClientConnection *client); /** * Set @p surface as a data proxy for this SurfaceInterface. This enables * the proxy to conduct drags on the surface's client behalf. * * Setting a data proxy is only allowed when the client owning this surface * has not created a data device itself. * @since 5.56 **/ void setDataProxy(SurfaceInterface *surface); /** * Returns the data proxy of this SurfaceInterface or null if there * is none set. * @since 5.56 **/ SurfaceInterface* dataProxy() const; Q_SIGNALS: /** * Emitted whenever the SurfaceInterface got damaged. * The signal is only emitted during the commit of state. * A damage means that a new BufferInterface got attached. * * @see buffer * @see damage **/ void damaged(const QRegion&); void opaqueChanged(const QRegion&); void inputChanged(const QRegion&); void scaleChanged(qint32); void transformChanged(KWayland::Server::OutputInterface::Transform); /** * Emitted when the Surface removes its content **/ void unmapped(); /** * @since 5.3 **/ void sizeChanged(); /** * @since 5.4 **/ void shadowChanged(); /** * @since 5.5 **/ void blurChanged(); /** * @since 5.5 **/ void slideOnShowHideChanged(); /** * @since 5.5 **/ void contrastChanged(); /** * Emitted whenever the tree of sub-surfaces changes in a way which requires a repaint. * @since 5.22 **/ void subSurfaceTreeChanged(); /** * Emitted whenever a pointer constraint get (un)installed on this SurfaceInterface. * * The pointer constraint does not get activated, the compositor needs to activate * the lock/confinement. * * @see confinedPointer * @see lockedPointer * @since 5.29 **/ void pointerConstraintsChanged(); /** * Emitted whenever the SurfaceInterface starts/ends to inhibit idle. * @see inhibitsIdle * @since 5.41 **/ void inhibitsIdleChanged(); /** * Emitted when the Surface has been committed. * * This signal is emitted after all the relevant damage and xyzChanged signals * for this commit are emitted. * @since 5.54 **/ void committed(); private: friend class CompositorInterface; friend class SubSurfaceInterface; friend class ShadowManagerInterface; friend class BlurManagerInterface; friend class SlideManagerInterface; friend class ContrastManagerInterface; friend class IdleInhibitManagerUnstableV1Interface; friend class PointerConstraintsUnstableV1Interface; friend class SurfaceRole; explicit SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::SurfaceInterface*) #endif diff --git a/src/server/surface_interface_p.h b/src/server/surface_interface_p.h index ebe2d61..44f24b4 100644 --- a/src/server/surface_interface_p.h +++ b/src/server/surface_interface_p.h @@ -1,151 +1,137 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_SURFACE_INTERFACE_P_H #define WAYLAND_SERVER_SURFACE_INTERFACE_P_H #include "surface_interface.h" #include "resource_p.h" // Qt #include #include // Wayland #include namespace KWayland { namespace Server { class IdleInhibitorInterface; class SurfaceRole; class SurfaceInterface::Private : public Resource::Private { public: struct State { QRegion damage = QRegion(); QRegion bufferDamage = QRegion(); QRegion opaque = QRegion(); QRegion input = QRegion(); bool inputIsSet = false; bool opaqueIsSet = false; bool bufferIsSet = false; bool shadowIsSet = false; bool blurIsSet = false; bool contrastIsSet = false; bool slideIsSet = false; bool inputIsInfinite = true; bool childrenChanged = false; bool scaleIsSet = false; bool transformIsSet = false; qint32 scale = 1; OutputInterface::Transform transform = OutputInterface::Transform::Normal; QList callbacks = QList(); QPoint offset = QPoint(); BufferInterface *buffer = nullptr; // stacking order: bottom (first) -> top (last) QList> children; QPointer shadow; QPointer blur; QPointer contrast; QPointer slide; }; Private(SurfaceInterface *q, CompositorInterface *c, wl_resource *parentResource); ~Private(); void destroy(); void addChild(QPointer subsurface); void removeChild(QPointer subsurface); bool raiseChild(QPointer subsurface, SurfaceInterface *sibling); bool lowerChild(QPointer subsurface, SurfaceInterface *sibling); void setShadow(const QPointer &shadow); void setBlur(const QPointer &blur); void setContrast(const QPointer &contrast); void setSlide(const QPointer &slide); void installPointerConstraint(LockedPointerInterface *lock); void installPointerConstraint(ConfinedPointerInterface *confinement); void installIdleInhibitor(IdleInhibitorInterface *inhibitor); void commitSubSurface(); void commit(); SurfaceRole *role = nullptr; State current; State pending; State subSurfacePending; QPointer subSurface; QRegion trackedDamage; // workaround for https://bugreports.qt.io/browse/QTBUG-52192 // A subsurface needs to be considered mapped even if it doesn't have a buffer attached // Otherwise Qt's sub-surfaces will never be visible and the client will freeze due to // waiting on the frame callback of the never visible surface bool subSurfaceIsMapped = true; QVector outputs; QPointer lockedPointer; QPointer confinedPointer; QHash outputDestroyedConnections; QVector idleInhibitors; SurfaceInterface *dataProxy = nullptr; private: QMetaObject::Connection constrainsOneShotConnection; QMetaObject::Connection constrainsUnboundConnection; SurfaceInterface *q_func() { return reinterpret_cast(q); } void swapStates(State *source, State *target, bool emitChanged); void damage(const QRect &rect); void damageBuffer(const QRect &rect); void setScale(qint32 scale); void setTransform(OutputInterface::Transform transform); void addFrameCallback(uint32_t callback); void attachBuffer(wl_resource *buffer, const QPoint &offset); void setOpaque(const QRegion ®ion); void setInput(const QRegion ®ion, bool isInfinite); static void destroyFrameCallback(wl_resource *r); static void attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy); static void damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void frameCallback(wl_client *client, wl_resource *resource, uint32_t callback); static void opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static void inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); static void commitCallback(wl_client *client, wl_resource *resource); // since version 2 static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform); // since version 3 static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale); // since version 4 static void damageBufferCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct wl_surface_interface s_interface; }; } } #endif diff --git a/src/server/surfacerole.cpp b/src/server/surfacerole.cpp index 22b29f5..ca5906b 100644 --- a/src/server/surfacerole.cpp +++ b/src/server/surfacerole.cpp @@ -1,45 +1,31 @@ -/**************************************************************************** -Copyright 2019 Vlad Zahorodnii +/* + SPDX-FileCopyrightText: 2019 Vlad Zahorodnii -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "surfacerole_p.h" #include "surface_interface_p.h" #include "surface_interface.h" namespace KWayland { namespace Server { SurfaceRole::SurfaceRole(SurfaceInterface *surface) : m_surface(surface) { m_surface->d_func()->role = this; } SurfaceRole::~SurfaceRole() { // Lifetime of the surface role is not bounded to the associated surface. if (m_surface) { m_surface->d_func()->role = nullptr; } } } } diff --git a/src/server/surfacerole_p.h b/src/server/surfacerole_p.h index 1c53749..20ac300 100644 --- a/src/server/surfacerole_p.h +++ b/src/server/surfacerole_p.h @@ -1,50 +1,36 @@ -/**************************************************************************** -Copyright 2019 Vlad Zahorodnii - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2019 Vlad Zahorodnii + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_SURFACEROLE_P_H #define KWAYLAND_SERVER_SURFACEROLE_P_H #include namespace KWayland { namespace Server { class SurfaceInterface; class SurfaceRole { public: explicit SurfaceRole(SurfaceInterface *surface); virtual ~SurfaceRole(); virtual void commit() = 0; private: QPointer m_surface; Q_DISABLE_COPY(SurfaceRole) }; } } #endif // KWAYLAND_SERVER_SURFACEROLE_P_H diff --git a/src/server/textinput_interface.cpp b/src/server/textinput_interface.cpp index 21cadf1..1eba08a 100644 --- a/src/server/textinput_interface.cpp +++ b/src/server/textinput_interface.cpp @@ -1,273 +1,259 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_interface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "seat_interface_p.h" #include "surface_interface.h" #include #include #include namespace KWayland { namespace Server { void TextInputInterface::Private::showInputPanelCallback(wl_client *client, wl_resource *resource) { auto p = cast(resource); Q_ASSERT(*p->client == client); emit p->q_func()->requestShowInputPanel(); } void TextInputInterface::Private::hideInputPanelCallback(wl_client *client, wl_resource *resource) { auto p = cast(resource); Q_ASSERT(*p->client == client); emit p->q_func()->requestHideInputPanel(); } void TextInputInterface::Private::setSurroundingTextCallback(wl_client *client, wl_resource *resource, const char * text, int32_t cursor, int32_t anchor) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->surroundingText = QByteArray(text); // TODO: make qint32 p->surroundingTextCursorPosition = cursor; p->surroundingTextSelectionAnchor = anchor; emit p->q_func()->surroundingTextChanged(); } void TextInputInterface::Private::setContentTypeCallback(wl_client *client, wl_resource *resource, uint32_t hint, uint32_t purpose) { auto p = cast(resource); Q_ASSERT(*p->client == client); const auto contentHints = p->convertContentHint(hint); const auto contentPurpose = p->convertContentPurpose(purpose); if (contentHints != p->contentHints || contentPurpose != p->contentPurpose) { p->contentHints = contentHints; p->contentPurpose = contentPurpose; emit p->q_func()->contentTypeChanged(); } } void TextInputInterface::Private::setCursorRectangleCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { auto p = cast(resource); Q_ASSERT(*p->client == client); const QRect rect = QRect(x, y, width, height); if (p->cursorRectangle != rect) { p->cursorRectangle = rect; emit p->q_func()->cursorRectangleChanged(p->cursorRectangle); } } void TextInputInterface::Private::setPreferredLanguageCallback(wl_client *client, wl_resource *resource, const char * language) { auto p = cast(resource); Q_ASSERT(*p->client == client); const QByteArray preferredLanguage = QByteArray(language); if (p->preferredLanguage != preferredLanguage) { p->preferredLanguage = preferredLanguage; emit p->q_func()->preferredLanguageChanged(p->preferredLanguage); } } TextInputInterface::Private::Private(TextInputInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) { } TextInputInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } QByteArray TextInputInterface::preferredLanguage() const { Q_D(); return d->preferredLanguage; } TextInputInterface::ContentHints TextInputInterface::contentHints() const { Q_D(); return d->contentHints; } TextInputInterface::ContentPurpose TextInputInterface::contentPurpose() const { Q_D(); return d->contentPurpose; } QByteArray TextInputInterface::surroundingText() const { Q_D(); return d->surroundingText; } qint32 TextInputInterface::surroundingTextCursorPosition() const { Q_D(); return d->surroundingTextCursorPosition; } qint32 TextInputInterface::surroundingTextSelectionAnchor() const { Q_D(); return d->surroundingTextSelectionAnchor; } void TextInputInterface::preEdit(const QByteArray &text, const QByteArray &commit) { Q_D(); d->preEdit(text, commit); } void TextInputInterface::commit(const QByteArray &text) { Q_D(); d->commit(text); } void TextInputInterface::keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) Q_D(); d->keysymPressed(keysym, modifiers); } void TextInputInterface::keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_D(); d->keysymReleased(keysym, modifiers); } void TextInputInterface::deleteSurroundingText(quint32 beforeLength, quint32 afterLength) { Q_D(); d->deleteSurroundingText(beforeLength, afterLength); } void TextInputInterface::setCursorPosition(qint32 index, qint32 anchor) { Q_D(); d->setCursorPosition(index, anchor); } void TextInputInterface::setTextDirection(Qt::LayoutDirection direction) { Q_D(); d->setTextDirection(direction); } void TextInputInterface::setPreEditCursor(qint32 index) { Q_D(); d->setPreEditCursor(index); } void TextInputInterface::setInputPanelState(bool visible, const QRect &overlappedSurfaceArea) { Q_D(); if (d->inputPanelVisible == visible && d->overlappedSurfaceArea == overlappedSurfaceArea) { // not changed return; } d->inputPanelVisible = visible; d->overlappedSurfaceArea = overlappedSurfaceArea; d->sendInputPanelState(); } void TextInputInterface::setLanguage(const QByteArray &languageTag) { Q_D(); if (d->language == languageTag) { // not changed return; } d->language = languageTag; d->sendLanguage(); } TextInputInterfaceVersion TextInputInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion(); } QPointer TextInputInterface::surface() const { Q_D(); return d->surface; } QRect TextInputInterface::cursorRectangle() const { Q_D(); return d->cursorRectangle; } bool TextInputInterface::isEnabled() const { Q_D(); return d->enabled; } TextInputInterface::Private *TextInputInterface::d_func() const { return reinterpret_cast(d.data()); } TextInputInterface::TextInputInterface(Private *p, QObject *parent) : Resource(p, parent) { } TextInputInterface::~TextInputInterface() = default; TextInputManagerInterface::TextInputManagerInterface(Private *d, QObject *parent) : Global(d, parent) { } TextInputManagerInterface::~TextInputManagerInterface() = default; TextInputInterfaceVersion TextInputManagerInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } TextInputManagerInterface::Private *TextInputManagerInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/textinput_interface.h b/src/server/textinput_interface.h index 005bf81..c164c7e 100644 --- a/src/server/textinput_interface.h +++ b/src/server/textinput_interface.h @@ -1,439 +1,425 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_TEXTINPUT_INTERFACE_H #define KWAYLAND_SERVER_TEXTINPUT_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class SeatInterface; class SurfaceInterface; class TextInputInterface; /** * Enum describing the different InterfaceVersion encapsulated in this implementation * * @since 5.23 **/ enum class TextInputInterfaceVersion { /** * wl_text_input as the non-standardized version **/ UnstableV0, /** * not supported version **/ UnstableV1, /** * zwp_text_input_v2 as used by Qt 5.7 **/ UnstableV2 }; /** * @brief Represent the Global for the interface. * * The class can represent different interfaces. Which concrete interface is represented * can be determined through {@link interfaceVersion}. * * To create a TextInputManagerInterface use {@link Display::createTextInputManager} * * @since 5.23 **/ class KWAYLANDSERVER_EXPORT TextInputManagerInterface : public Global { Q_OBJECT public: virtual ~TextInputManagerInterface(); /** * @returns The interface version used by this TextInputManagerInterface **/ TextInputInterfaceVersion interfaceVersion() const; protected: class Private; explicit TextInputManagerInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; /** * @brief Represents a generic Resource for a text input object. * * This class does not directly correspond to a Wayland resource, but is a generic contract * for any interface which implements a text input, e.g. the unstable wl_text_input interface. * * It does not expose the actual interface to cover up the fact that the interface is unstable * and might change. If one needs to know the actual used protocol, use the method {@link interfaceVersion}. * * A TextInputInterface gets created by the {@link TextInputManagerInterface}. The individual * instances are not exposed directly. The SeatInterface provides access to the currently active * TextInputInterface. This is evaluated automatically based on which SurfaceInterface has * keyboard focus. * * @see TextInputManagerInterface * @see SeatInterface * @since 5.23 **/ class KWAYLANDSERVER_EXPORT TextInputInterface : public Resource { Q_OBJECT public: virtual ~TextInputInterface(); /** * ContentHint allows to modify the behavior of the text input. **/ enum class ContentHint : uint32_t { /** * no special behaviour */ None = 0, /** * suggest word completions */ AutoCompletion = 1 << 0, /** * suggest word corrections */ AutoCorrection = 1 << 1, /** * switch to uppercase letters at the start of a sentence */ AutoCapitalization = 1 << 2, /** * prefer lowercase letters */ LowerCase = 1 << 3, /** * prefer uppercase letters */ UpperCase = 1 << 4, /** * prefer casing for titles and headings (can be language dependent) */ TitleCase = 1 << 5, /** * characters should be hidden */ HiddenText = 1 << 6, /** * typed text should not be stored */ SensitiveData = 1 << 7, /** * just latin characters should be entered */ Latin = 1 << 8, /** * the text input is multi line */ MultiLine = 1 << 9 }; Q_DECLARE_FLAGS(ContentHints, ContentHint) /** * The ContentPurpose allows to specify the primary purpose of a text input. * * This allows an input method to show special purpose input panels with * extra characters or to disallow some characters. */ enum class ContentPurpose : uint32_t { /** * default input, allowing all characters */ Normal, /** * allow only alphabetic characters **/ Alpha, /** * allow only digits */ Digits, /** * input a number (including decimal separator and sign) */ Number, /** * input a phone number */ Phone, /** * input an URL */ Url, /** * input an email address **/ Email, /** * input a name of a person */ Name, /** * input a password */ Password, /** * input a date */ Date, /** * input a time */ Time, /** * input a date and time */ DateTime, /** * input for a terminal */ Terminal }; /** * @returns The interface version used by this TextInputInterface **/ TextInputInterfaceVersion interfaceVersion() const; /** * The preferred language as a RFC-3066 format language tag. * * This can be used by the server to show a language specific virtual keyboard layout. * @see preferredLanguageChanged **/ QByteArray preferredLanguage() const; /** * @see cursorRectangleChanged **/ QRect cursorRectangle() const; /** * @see contentTypeChanged **/ ContentPurpose contentPurpose() const; /** *@see contentTypeChanged **/ ContentHints contentHints() const; /** * @returns The plain surrounding text around the input position. * @see surroundingTextChanged * @see surroundingTextCursorPosition * @see surroundingTextSelectionAnchor **/ QByteArray surroundingText() const; /** * @returns The byte offset of current cursor position within the {@link surroundingText} * @see surroundingText * @see surroundingTextChanged **/ qint32 surroundingTextCursorPosition() const; /** * The byte offset of the selection anchor within the {@link surroundingText}. * * If there is no selected text this is the same as cursor. * @return The byte offset of the selection anchor * @see surroundingText * @see surroundingTextChanged **/ qint32 surroundingTextSelectionAnchor() const; /** * @return The surface the TextInputInterface is enabled on * @see isEnabled * @see enabledChanged **/ QPointer surface() const; /** * @return Whether the TextInputInterface is currently enabled for a SurfaceInterface. * @see surface * @see enabledChanged **/ bool isEnabled() const; /** * Notify when a new composing @p text (pre-edit) should be set around the * current cursor position. Any previously set composing text should * be removed. * * The @p commitText can be used to replace the preedit text on reset * (for example on unfocus). * * @param text The new utf8-encoded pre-edit text * @param commitText Utf8-encoded text to replace preedit text on reset * @see commit * @see preEditCursor **/ void preEdit(const QByteArray &text, const QByteArray &commitText); /** * Notify when @p text should be inserted into the editor widget. * The text to commit could be either just a single character after a key press or the * result of some composing ({@link preEdit}). It could be also an empty text * when some text should be removed (see {@link deleteSurroundingText}) or when * the input cursor should be moved (see {@link cursorPosition}). * * Any previously set composing text should be removed. * @param text The utf8-encoded text to be inserted into the editor widget * @see preEdit * @see deleteSurroundingText **/ void commit(const QByteArray &text); /** * Sets the cursor position inside the composing text (as byte offset) relative to the * start of the composing text. When @p index is a negative number no cursor is shown. * * The Client applies the @p index together with {@link preEdit}. * @param index The cursor position relative to the start of the composing text * @see preEdit **/ void setPreEditCursor(qint32 index); /** * Notify when the text around the current cursor position should be deleted. * * The Client processes this event together with the commit string * * @param beforeLength length of text before current cursor position. * @param afterLength length of text after current cursor position. * @see commit **/ void deleteSurroundingText(quint32 beforeLength, quint32 afterLength); /** * Notify when the cursor @p index or @p anchor position should be modified. * * The Client applies this together with the commit string. **/ void setCursorPosition(qint32 index, qint32 anchor); /** * Sets the text @p direction of input text. **/ void setTextDirection(Qt::LayoutDirection direction); void keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers = Qt::NoModifier); void keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers = Qt::NoModifier); /** * Informs the client about changes in the visibility of the input panel (virtual keyboard). * * The @p overlappedSurfaceArea defines the area overlapped by the input panel (virtual keyboard) * on the SurfaceInterface having the text focus in surface local coordinates. * * @param visible Whether the input panel is currently visible * @param overlappedSurfaceArea The overlapping area in surface local coordinates **/ void setInputPanelState(bool visible, const QRect &overlappedSurfaceArea); /** * Sets the language of the input text. The @p languageTag is a RFC-3066 format language tag. **/ void setLanguage(const QByteArray &languageTag); Q_SIGNALS: /** * Requests input panels (virtual keyboard) to show. * @see requestHideInputPanel **/ void requestShowInputPanel(); /** * Requests input panels (virtual keyboard) to hide. * @see requestShowInputPanel **/ void requestHideInputPanel(); /** * Invoked by the client when the input state should be * reset, for example after the text was changed outside of the normal * input method flow. **/ void requestReset(); /** * Emitted whenever the preferred @p language changes. * @see preferredLanguage **/ void preferredLanguageChanged(const QByteArray &language); /** * @see cursorRectangle **/ void cursorRectangleChanged(const QRect &rect); /** * Emitted when the {@link contentPurpose} and/or {@link contentHints} changes. * @see contentPurpose * @see contentHints **/ void contentTypeChanged(); /** * Emitted when the {@link surroundingText}, {@link surroundingTextCursorPosition} * and/or {@link surroundingTextSelectionAnchor} changed. * @see surroundingText * @see surroundingTextCursorPosition * @see surroundingTextSelectionAnchor **/ void surroundingTextChanged(); /** * Emitted whenever this TextInputInterface gets enabled or disabled for a SurfaceInterface. * @see isEnabled * @see surface **/ void enabledChanged(); protected: class Private; explicit TextInputInterface(Private *p, QObject *parent = nullptr); private: friend class TextInputManagerUnstableV0Interface; friend class TextInputManagerUnstableV2Interface; friend class SeatInterface; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::TextInputInterfaceVersion) Q_DECLARE_METATYPE(KWayland::Server::TextInputInterface *) Q_DECLARE_METATYPE(KWayland::Server::TextInputInterface::ContentHint) Q_DECLARE_METATYPE(KWayland::Server::TextInputInterface::ContentHints) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::TextInputInterface::ContentHints) Q_DECLARE_METATYPE(KWayland::Server::TextInputInterface::ContentPurpose) #endif diff --git a/src/server/textinput_interface_p.h b/src/server/textinput_interface_p.h index be4a0ad..ba30ec8 100644 --- a/src/server/textinput_interface_p.h +++ b/src/server/textinput_interface_p.h @@ -1,157 +1,143 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_TEXTINPUT_INTERFACE_P_H #define KWAYLAND_SERVER_TEXTINPUT_INTERFACE_P_H #include "textinput_interface.h" #include "resource_p.h" #include "global_p.h" #include #include #include namespace KWayland { namespace Server { class TextInputManagerUnstableV0Interface; class TextInputManagerUnstableV2Interface; class TextInputManagerInterface::Private : public Global::Private { public: QVector inputs; TextInputInterfaceVersion interfaceVersion; protected: Private(TextInputInterfaceVersion interfaceVersion, TextInputManagerInterface *q, Display *d, const wl_interface *interface, quint32 version); TextInputManagerInterface *q; }; class TextInputInterface::Private : public Resource::Private { public: ~Private(); virtual void sendEnter(SurfaceInterface *surface, quint32 serial) = 0; virtual void sendLeave(quint32 serial, SurfaceInterface *surface) = 0; virtual void preEdit(const QByteArray &text, const QByteArray &commit) = 0; virtual void commit(const QByteArray &text) = 0; virtual void deleteSurroundingText(quint32 beforeLength, quint32 afterLength) = 0; virtual void setTextDirection(Qt::LayoutDirection direction) = 0; virtual void setPreEditCursor(qint32 index) = 0; virtual void setCursorPosition(qint32 index, qint32 anchor) = 0; virtual void keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) = 0; virtual void keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) = 0; virtual TextInputInterfaceVersion interfaceVersion() const = 0; virtual void sendInputPanelState() = 0; virtual void sendLanguage() = 0; virtual TextInputInterface::ContentHints convertContentHint(uint32_t hint) const = 0; virtual TextInputInterface::ContentPurpose convertContentPurpose(uint32_t purpose) const = 0; QByteArray preferredLanguage; QRect cursorRectangle; TextInputInterface::ContentHints contentHints = TextInputInterface::ContentHint::None; TextInputInterface::ContentPurpose contentPurpose = TextInputInterface::ContentPurpose::Normal; SeatInterface *seat = nullptr; QPointer surface; bool enabled = false; QByteArray surroundingText; qint32 surroundingTextCursorPosition = 0; qint32 surroundingTextSelectionAnchor = 0; bool inputPanelVisible = false; QRect overlappedSurfaceArea; QByteArray language; protected: Private(TextInputInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); static void showInputPanelCallback(wl_client *client, wl_resource *resource); static void hideInputPanelCallback(wl_client *client, wl_resource *resource); static void setSurroundingTextCallback(wl_client *client, wl_resource *resource, const char * text, int32_t cursor, int32_t anchor); static void setContentTypeCallback(wl_client *client, wl_resource *resource, uint32_t hint, uint32_t purpose); static void setCursorRectangleCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setPreferredLanguageCallback(wl_client *client, wl_resource *resource, const char * language); private: TextInputInterface *q_func() { return reinterpret_cast(q); } }; class TextInputUnstableV0Interface : public TextInputInterface { Q_OBJECT public: virtual ~TextInputUnstableV0Interface(); Q_SIGNALS: /** * @internal **/ void requestActivate(KWayland::Server::SeatInterface *seat, KWayland::Server::SurfaceInterface *surface); private: explicit TextInputUnstableV0Interface(TextInputManagerUnstableV0Interface *parent, wl_resource *parentResource); friend class TextInputManagerUnstableV0Interface; class Private; }; class TextInputUnstableV2Interface : public TextInputInterface { Q_OBJECT public: virtual ~TextInputUnstableV2Interface(); private: explicit TextInputUnstableV2Interface(TextInputManagerUnstableV2Interface *parent, wl_resource *parentResource); friend class TextInputManagerUnstableV2Interface; class Private; }; class TextInputManagerUnstableV0Interface : public TextInputManagerInterface { Q_OBJECT public: explicit TextInputManagerUnstableV0Interface(Display *display, QObject *parent = nullptr); virtual ~TextInputManagerUnstableV0Interface(); private: class Private; }; class TextInputManagerUnstableV2Interface : public TextInputManagerInterface { Q_OBJECT public: explicit TextInputManagerUnstableV2Interface(Display *display, QObject *parent = nullptr); virtual ~TextInputManagerUnstableV2Interface(); private: class Private; }; } } #endif diff --git a/src/server/textinput_interface_v0.cpp b/src/server/textinput_interface_v0.cpp index 2c5a8b8..b1b8f0d 100644 --- a/src/server/textinput_interface_v0.cpp +++ b/src/server/textinput_interface_v0.cpp @@ -1,441 +1,427 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_interface_p.h" #include "display.h" #include "resource_p.h" #include "seat_interface_p.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class TextInputUnstableV0Interface::Private : public TextInputInterface::Private { public: Private(TextInputInterface *q, TextInputManagerUnstableV0Interface *c, wl_resource *parentResource); ~Private(); void activate(SeatInterface *seat, SurfaceInterface *surface); void deactivate(); void sendEnter(SurfaceInterface *surface, quint32 serial) override; void sendLeave(quint32 serial, SurfaceInterface *surface) override; void preEdit(const QByteArray &text, const QByteArray &commit) override; void commit(const QByteArray &text) override; void deleteSurroundingText(quint32 beforeLength, quint32 afterLength) override; void setTextDirection(Qt::LayoutDirection direction) override; void setPreEditCursor(qint32 index) override; void setCursorPosition(qint32 index, qint32 anchor) override; void keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) override; void keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) override; TextInputInterfaceVersion interfaceVersion() const override { return TextInputInterfaceVersion::UnstableV0; } void sendInputPanelState() override; void sendLanguage() override; private: static const struct wl_text_input_interface s_interface; TextInputUnstableV0Interface *q_func() { return reinterpret_cast(q); } static void activateCallback(wl_client *client, wl_resource *resource, wl_resource * seat, wl_resource * surface); static void deactivateCallback(wl_client *client, wl_resource *resource, wl_resource * seat); static void resetCallback(wl_client *client, wl_resource *resource); static void setSurroundingTextUintCallback(wl_client *client, wl_resource *resource, const char * text, uint32_t cursor, uint32_t anchor); static void commitStateCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void invokeActionCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t index); // helpers TextInputInterface::ContentHints convertContentHint(uint32_t hint) const override; TextInputInterface::ContentPurpose convertContentPurpose(uint32_t purpose) const override; quint32 latestState = 0; }; #ifndef K_DOXYGEN const struct wl_text_input_interface TextInputUnstableV0Interface::Private::s_interface = { activateCallback, deactivateCallback, showInputPanelCallback, hideInputPanelCallback, resetCallback, setSurroundingTextUintCallback, setContentTypeCallback, setCursorRectangleCallback, setPreferredLanguageCallback, commitStateCallback, invokeActionCallback }; #endif void TextInputUnstableV0Interface::Private::activate(SeatInterface *seat, SurfaceInterface *s) { surface = QPointer(s); enabled = true; emit q_func()->enabledChanged(); emit q_func()->requestActivate(seat, surface); } void TextInputUnstableV0Interface::Private::deactivate() { surface.clear(); enabled = false; emit q_func()->enabledChanged(); } void TextInputUnstableV0Interface::Private::sendEnter(SurfaceInterface *surface, quint32 serial) { Q_UNUSED(serial) if (!resource) { return; } wl_text_input_send_enter(resource, surface->resource()); } void TextInputUnstableV0Interface::Private::sendLeave(quint32 serial, SurfaceInterface *surface) { Q_UNUSED(serial) Q_UNUSED(surface) if (!resource) { return; } wl_text_input_send_leave(resource); } void TextInputUnstableV0Interface::Private::preEdit(const QByteArray &text, const QByteArray &commit) { if (!resource) { return; } wl_text_input_send_preedit_string(resource, latestState, text.constData(), commit.constData()); } void TextInputUnstableV0Interface::Private::commit(const QByteArray &text) { if (!resource) { return; } wl_text_input_send_commit_string(resource, latestState, text.constData()); } void TextInputUnstableV0Interface::Private::keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } wl_text_input_send_keysym(resource, latestState, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, 0); } void TextInputUnstableV0Interface::Private::keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } wl_text_input_send_keysym(resource, latestState, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, 0); } void TextInputUnstableV0Interface::Private::deleteSurroundingText(quint32 beforeLength, quint32 afterLength) { if (!resource) { return; } wl_text_input_send_delete_surrounding_text(resource, -1 * beforeLength, beforeLength + afterLength); } void TextInputUnstableV0Interface::Private::setCursorPosition(qint32 index, qint32 anchor) { if (!resource) { return; } wl_text_input_send_cursor_position(resource, index, anchor); } void TextInputUnstableV0Interface::Private::setTextDirection(Qt::LayoutDirection direction) { if (!resource) { return; } wl_text_input_text_direction wlDirection; switch (direction) { case Qt::LeftToRight: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_LTR; break; case Qt::RightToLeft: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_RTL; break; case Qt::LayoutDirectionAuto: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_AUTO; break; default: Q_UNREACHABLE(); break; } wl_text_input_send_text_direction(resource, latestState, wlDirection); } void TextInputUnstableV0Interface::Private::setPreEditCursor(qint32 index) { if (!resource) { return; } wl_text_input_send_preedit_cursor(resource, index); } void TextInputUnstableV0Interface::Private::sendInputPanelState() { if (!resource) { return; } wl_text_input_send_input_panel_state(resource, inputPanelVisible); } void TextInputUnstableV0Interface::Private::sendLanguage() { if (!resource) { return; } wl_text_input_send_language(resource, latestState, language.constData()); } TextInputUnstableV0Interface::Private::Private(TextInputInterface *q, TextInputManagerUnstableV0Interface *c, wl_resource *parentResource) : TextInputInterface::Private(q, c, parentResource, &wl_text_input_interface, &s_interface) { } TextInputUnstableV0Interface::Private::~Private() = default; void TextInputUnstableV0Interface::Private::activateCallback(wl_client *client, wl_resource *resource, wl_resource *seat, wl_resource *surface) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->activate(SeatInterface::get(seat), SurfaceInterface::get(surface)); } void TextInputUnstableV0Interface::Private::deactivateCallback(wl_client *client, wl_resource *resource, wl_resource *seat) { Q_UNUSED(seat) auto p = cast(resource); Q_ASSERT(*p->client == client); p->deactivate(); } void TextInputUnstableV0Interface::Private::resetCallback(wl_client *client, wl_resource *resource) { auto p = cast(resource); Q_ASSERT(*p->client == client); emit p->q_func()->requestReset(); } void TextInputUnstableV0Interface::Private::setSurroundingTextUintCallback(wl_client *client, wl_resource *resource, const char * text, uint32_t cursor, uint32_t anchor) { setSurroundingTextCallback(client, resource, text, cursor, anchor); } void TextInputUnstableV0Interface::Private::commitStateCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->latestState = serial; } void TextInputUnstableV0Interface::Private::invokeActionCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t index) { Q_UNUSED(button) Q_UNUSED(index) // TODO: implement auto p = cast(resource); Q_ASSERT(*p->client == client); } TextInputInterface::ContentHints TextInputUnstableV0Interface::Private::convertContentHint(uint32_t hint) const { const auto hints = wl_text_input_content_hint(hint); TextInputInterface::ContentHints ret = TextInputInterface::ContentHint::None; if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_COMPLETION) { ret |= TextInputInterface::ContentHint::AutoCompletion; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_CORRECTION) { ret |= TextInputInterface::ContentHint::AutoCorrection; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION) { ret |= TextInputInterface::ContentHint::AutoCapitalization; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_LOWERCASE) { ret |= TextInputInterface::ContentHint::LowerCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_UPPERCASE) { ret |= TextInputInterface::ContentHint::UpperCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_TITLECASE) { ret |= TextInputInterface::ContentHint::TitleCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_HIDDEN_TEXT) { ret |= TextInputInterface::ContentHint::HiddenText; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_SENSITIVE_DATA) { ret |= TextInputInterface::ContentHint::SensitiveData; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_LATIN) { ret |= TextInputInterface::ContentHint::Latin; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_MULTILINE) { ret |= TextInputInterface::ContentHint::MultiLine; } return ret; } TextInputInterface::ContentPurpose TextInputUnstableV0Interface::Private::convertContentPurpose(uint32_t purpose) const { const auto wlPurpose = wl_text_input_content_purpose(purpose); switch (wlPurpose) { case WL_TEXT_INPUT_CONTENT_PURPOSE_ALPHA: return TextInputInterface::ContentPurpose::Alpha; case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS: return TextInputInterface::ContentPurpose::Digits; case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER: return TextInputInterface::ContentPurpose::Number; case WL_TEXT_INPUT_CONTENT_PURPOSE_PHONE: return TextInputInterface::ContentPurpose::Phone; case WL_TEXT_INPUT_CONTENT_PURPOSE_URL: return TextInputInterface::ContentPurpose::Url; case WL_TEXT_INPUT_CONTENT_PURPOSE_EMAIL: return TextInputInterface::ContentPurpose::Email; case WL_TEXT_INPUT_CONTENT_PURPOSE_NAME: return TextInputInterface::ContentPurpose::Name; case WL_TEXT_INPUT_CONTENT_PURPOSE_PASSWORD: return TextInputInterface::ContentPurpose::Password; case WL_TEXT_INPUT_CONTENT_PURPOSE_DATE: return TextInputInterface::ContentPurpose::Date; case WL_TEXT_INPUT_CONTENT_PURPOSE_TIME: return TextInputInterface::ContentPurpose::Time; case WL_TEXT_INPUT_CONTENT_PURPOSE_DATETIME: return TextInputInterface::ContentPurpose::DateTime; case WL_TEXT_INPUT_CONTENT_PURPOSE_TERMINAL: return TextInputInterface::ContentPurpose::Terminal; case WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL: default: return TextInputInterface::ContentPurpose::Normal; } } TextInputUnstableV0Interface::TextInputUnstableV0Interface(TextInputManagerUnstableV0Interface *parent, wl_resource *parentResource) : TextInputInterface(new Private(this, parent, parentResource)) { } TextInputUnstableV0Interface::~TextInputUnstableV0Interface() = default; class TextInputManagerUnstableV0Interface::Private : public TextInputManagerInterface::Private { public: Private(TextInputManagerUnstableV0Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void createTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id); TextInputManagerUnstableV0Interface *q; static const struct wl_text_input_manager_interface s_interface; static const quint32 s_version; }; const quint32 TextInputManagerUnstableV0Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct wl_text_input_manager_interface TextInputManagerUnstableV0Interface::Private::s_interface = { createTextInputCallback }; #endif void TextInputManagerUnstableV0Interface::Private::createTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto m = cast(resource); auto *t = new TextInputUnstableV0Interface(m->q, resource); m->inputs << t; QObject::connect(t, &QObject::destroyed, m->q, [t, m] { m->inputs.removeAll(t); } ); QObject::connect(t, &TextInputUnstableV0Interface::requestActivate, m->q, [t, m] (SeatInterface *seat) { // TODO: disallow for other seat seat->d_func()->registerTextInput(t); t->d_func()->seat = seat; } ); t->d->create(m->display->getConnection(client), version, id); } TextInputManagerInterface::Private::Private(TextInputInterfaceVersion interfaceVersion, TextInputManagerInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } TextInputManagerUnstableV0Interface::Private::Private(TextInputManagerUnstableV0Interface *q, Display *d) : TextInputManagerInterface::Private(TextInputInterfaceVersion::UnstableV0, q, d, &wl_text_input_manager_interface, s_version) , q(q) { } void TextInputManagerUnstableV0Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_text_input_manager_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void TextInputManagerUnstableV0Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } TextInputManagerUnstableV0Interface::TextInputManagerUnstableV0Interface(Display *display, QObject *parent) : TextInputManagerInterface(new Private(this, display), parent) { } TextInputManagerUnstableV0Interface::~TextInputManagerUnstableV0Interface() = default; } } diff --git a/src/server/textinput_interface_v2.cpp b/src/server/textinput_interface_v2.cpp index 0f1cf0d..5646dd9 100644 --- a/src/server/textinput_interface_v2.cpp +++ b/src/server/textinput_interface_v2.cpp @@ -1,417 +1,403 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "textinput_interface_p.h" #include "display.h" #include "resource_p.h" #include "seat_interface_p.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class TextInputUnstableV2Interface::Private : public TextInputInterface::Private { public: Private(TextInputInterface *q, TextInputManagerUnstableV2Interface *c, wl_resource *parentResource); ~Private(); void sendEnter(SurfaceInterface *surface, quint32 serial) override; void sendLeave(quint32 serial, SurfaceInterface *surface) override; void preEdit(const QByteArray &text, const QByteArray &commit) override; void commit(const QByteArray &text) override; void deleteSurroundingText(quint32 beforeLength, quint32 afterLength) override; void setTextDirection(Qt::LayoutDirection direction) override; void setPreEditCursor(qint32 index) override; void setCursorPosition(qint32 index, qint32 anchor) override; void keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) override; void keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) override; TextInputInterfaceVersion interfaceVersion() const override { return TextInputInterfaceVersion::UnstableV2; } void sendInputPanelState() override; void sendLanguage() override; private: static const struct zwp_text_input_v2_interface s_interface; TextInputUnstableV2Interface *q_func() { return reinterpret_cast(q); } static void enableCallback(wl_client *client, wl_resource *resource, wl_resource * surface); static void disableCallback(wl_client *client, wl_resource *resource, wl_resource * surface); static void updateStateCallback(wl_client *client, wl_resource *resource, uint32_t serial, uint32_t reason); // helpers TextInputInterface::ContentHints convertContentHint(uint32_t hint) const override; TextInputInterface::ContentPurpose convertContentPurpose(uint32_t purpose) const override; void enable(SurfaceInterface *s); void disable(); }; #ifndef K_DOXYGEN const struct zwp_text_input_v2_interface TextInputUnstableV2Interface::Private::s_interface = { resourceDestroyedCallback, enableCallback, disableCallback, showInputPanelCallback, hideInputPanelCallback, setSurroundingTextCallback, setContentTypeCallback, setCursorRectangleCallback, setPreferredLanguageCallback, updateStateCallback }; #endif void TextInputUnstableV2Interface::Private::enable(SurfaceInterface *s) { surface = QPointer(s); enabled = true; emit q_func()->enabledChanged(); } void TextInputUnstableV2Interface::Private::disable() { surface.clear(); enabled = false; emit q_func()->enabledChanged(); } void TextInputUnstableV2Interface::Private::sendEnter(SurfaceInterface *surface, quint32 serial) { if (!resource || !surface || !surface->resource()) { return; } zwp_text_input_v2_send_enter(resource, serial, surface->resource()); } void TextInputUnstableV2Interface::Private::sendLeave(quint32 serial, SurfaceInterface *surface) { if (!resource || !surface || !surface->resource()) { return; } zwp_text_input_v2_send_leave(resource, serial, surface->resource()); } void TextInputUnstableV2Interface::Private::preEdit(const QByteArray &text, const QByteArray &commit) { if (!resource) { return; } zwp_text_input_v2_send_preedit_string(resource, text.constData(), commit.constData()); } void TextInputUnstableV2Interface::Private::commit(const QByteArray &text) { if (!resource) { return; } zwp_text_input_v2_send_commit_string(resource, text.constData()); } void TextInputUnstableV2Interface::Private::keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } zwp_text_input_v2_send_keysym(resource, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, 0); } void TextInputUnstableV2Interface::Private::keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } zwp_text_input_v2_send_keysym(resource, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, 0); } void TextInputUnstableV2Interface::Private::deleteSurroundingText(quint32 beforeLength, quint32 afterLength) { if (!resource) { return; } zwp_text_input_v2_send_delete_surrounding_text(resource, beforeLength, afterLength); } void TextInputUnstableV2Interface::Private::setCursorPosition(qint32 index, qint32 anchor) { if (!resource) { return; } zwp_text_input_v2_send_cursor_position(resource, index, anchor); } void TextInputUnstableV2Interface::Private::setTextDirection(Qt::LayoutDirection direction) { if (!resource) { return; } zwp_text_input_v2_text_direction wlDirection; switch (direction) { case Qt::LeftToRight: wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_LTR; break; case Qt::RightToLeft: wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_RTL; break; case Qt::LayoutDirectionAuto: wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_AUTO; break; default: Q_UNREACHABLE(); break; } zwp_text_input_v2_send_text_direction(resource, wlDirection); } void TextInputUnstableV2Interface::Private::setPreEditCursor(qint32 index) { if (!resource) { return; } zwp_text_input_v2_send_preedit_cursor(resource, index); } void TextInputUnstableV2Interface::Private::sendInputPanelState() { if (!resource) { return; } zwp_text_input_v2_send_input_panel_state(resource, inputPanelVisible ? ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_VISIBLE : ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_HIDDEN, overlappedSurfaceArea.x(), overlappedSurfaceArea.y(), overlappedSurfaceArea.width(), overlappedSurfaceArea.height()); } void TextInputUnstableV2Interface::Private::sendLanguage() { if (!resource) { return; } zwp_text_input_v2_send_language(resource, language.constData()); } TextInputUnstableV2Interface::Private::Private(TextInputInterface *q, TextInputManagerUnstableV2Interface *c, wl_resource *parentResource) : TextInputInterface::Private(q, c, parentResource, &zwp_text_input_v2_interface, &s_interface) { } TextInputUnstableV2Interface::Private::~Private() = default; void TextInputUnstableV2Interface::Private::enableCallback(wl_client *client, wl_resource *resource, wl_resource *surface) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->enable(SurfaceInterface::get(surface)); } void TextInputUnstableV2Interface::Private::disableCallback(wl_client *client, wl_resource *resource, wl_resource *surface) { Q_UNUSED(surface) auto p = cast(resource); Q_ASSERT(*p->client == client); p->disable(); } void TextInputUnstableV2Interface::Private::updateStateCallback(wl_client *client, wl_resource *resource, uint32_t serial, uint32_t reason) { auto p = cast(resource); Q_ASSERT(*p->client == client); Q_UNUSED(serial) // TODO: use other reason values reason if (reason == ZWP_TEXT_INPUT_V2_UPDATE_STATE_RESET) { emit p->q_func()->requestReset(); } } TextInputInterface::ContentHints TextInputUnstableV2Interface::Private::convertContentHint(uint32_t hint) const { const auto hints = zwp_text_input_v2_content_hint(hint); TextInputInterface::ContentHints ret = TextInputInterface::ContentHint::None; if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION) { ret |= TextInputInterface::ContentHint::AutoCompletion; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION) { ret |= TextInputInterface::ContentHint::AutoCorrection; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION) { ret |= TextInputInterface::ContentHint::AutoCapitalization; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE) { ret |= TextInputInterface::ContentHint::LowerCase; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE) { ret |= TextInputInterface::ContentHint::UpperCase; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_TITLECASE) { ret |= TextInputInterface::ContentHint::TitleCase; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT) { ret |= TextInputInterface::ContentHint::HiddenText; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA) { ret |= TextInputInterface::ContentHint::SensitiveData; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN) { ret |= TextInputInterface::ContentHint::Latin; } if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE) { ret |= TextInputInterface::ContentHint::MultiLine; } return ret; } TextInputInterface::ContentPurpose TextInputUnstableV2Interface::Private::convertContentPurpose(uint32_t purpose) const { const auto wlPurpose = zwp_text_input_v2_content_purpose(purpose); switch (wlPurpose) { case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_ALPHA: return TextInputInterface::ContentPurpose::Alpha; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS: return TextInputInterface::ContentPurpose::Digits; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER: return TextInputInterface::ContentPurpose::Number; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE: return TextInputInterface::ContentPurpose::Phone; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL: return TextInputInterface::ContentPurpose::Url; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL: return TextInputInterface::ContentPurpose::Email; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NAME: return TextInputInterface::ContentPurpose::Name; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PASSWORD: return TextInputInterface::ContentPurpose::Password; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE: return TextInputInterface::ContentPurpose::Date; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME: return TextInputInterface::ContentPurpose::Time; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME: return TextInputInterface::ContentPurpose::DateTime; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TERMINAL: return TextInputInterface::ContentPurpose::Terminal; case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL: default: return TextInputInterface::ContentPurpose::Normal; } } TextInputUnstableV2Interface::TextInputUnstableV2Interface(TextInputManagerUnstableV2Interface *parent, wl_resource *parentResource) : TextInputInterface(new Private(this, parent, parentResource)) { } TextInputUnstableV2Interface::~TextInputUnstableV2Interface() = default; class TextInputManagerUnstableV2Interface::Private : public TextInputManagerInterface::Private { public: Private(TextInputManagerUnstableV2Interface *q, Display *d); private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void getTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * seat); TextInputManagerUnstableV2Interface *q; static const struct zwp_text_input_manager_v2_interface s_interface; static const quint32 s_version; }; const quint32 TextInputManagerUnstableV2Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zwp_text_input_manager_v2_interface TextInputManagerUnstableV2Interface::Private::s_interface = { destroyCallback, getTextInputCallback }; #endif void TextInputManagerUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void TextInputManagerUnstableV2Interface::Private::getTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * seat) { SeatInterface *s = SeatInterface::get(seat); if (!s) { // TODO: send error return; } auto m = cast(resource); auto *t = new TextInputUnstableV2Interface(m->q, resource); t->d_func()->seat = s; m->inputs << t; QObject::connect(t, &QObject::destroyed, m->q, [t, m] { m->inputs.removeAll(t); } ); t->d->create(m->display->getConnection(client), version, id); s->d_func()->registerTextInput(t); } TextInputManagerUnstableV2Interface::Private::Private(TextInputManagerUnstableV2Interface *q, Display *d) : TextInputManagerInterface::Private(TextInputInterfaceVersion::UnstableV2, q, d, &zwp_text_input_manager_v2_interface, s_version) , q(q) { } void TextInputManagerUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zwp_text_input_manager_v2_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void TextInputManagerUnstableV2Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } TextInputManagerUnstableV2Interface::TextInputManagerUnstableV2Interface(Display *display, QObject *parent) : TextInputManagerInterface(new Private(this, display), parent) { } TextInputManagerUnstableV2Interface::~TextInputManagerUnstableV2Interface() = default; } } diff --git a/src/server/touch_interface.cpp b/src/server/touch_interface.cpp index 8f378ca..ad7096a 100644 --- a/src/server/touch_interface.cpp +++ b/src/server/touch_interface.cpp @@ -1,130 +1,116 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "touch_interface.h" #include "resource_p.h" #include "seat_interface.h" #include "display.h" #include "surface_interface.h" // Wayland #include namespace KWayland { namespace Server { class TouchInterface::Private : public Resource::Private { public: Private(SeatInterface *parent, wl_resource *parentResource, TouchInterface *q); SeatInterface *seat; QMetaObject::Connection destroyConnection; private: TouchInterface *q_func() { return reinterpret_cast(q); } static const struct wl_touch_interface s_interface; }; #ifndef K_DOXYGEN const struct wl_touch_interface TouchInterface::Private::s_interface = { resourceDestroyedCallback }; #endif TouchInterface::Private::Private(SeatInterface *parent, wl_resource *parentResource, TouchInterface *q) : Resource::Private(q, parent, parentResource, &wl_touch_interface, &s_interface) , seat(parent) { } TouchInterface::TouchInterface(SeatInterface *parent, wl_resource *parentResource) : Resource(new Private(parent, parentResource, this)) { } TouchInterface::~TouchInterface() = default; void TouchInterface::cancel() { Q_D(); if (!d->resource) { return; } wl_touch_send_cancel(d->resource); d->client->flush(); } void TouchInterface::frame() { Q_D(); if (!d->resource) { return; } wl_touch_send_frame(d->resource); d->client->flush(); } void TouchInterface::move(qint32 id, const QPointF &localPos) { Q_D(); if (!d->resource) { return; } if (d->seat->isDragTouch()) { // handled by DataDevice return; } wl_touch_send_motion(d->resource, d->seat->timestamp(), id, wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); d->client->flush(); } void TouchInterface::up(qint32 id, quint32 serial) { Q_D(); if (!d->resource) { return; } wl_touch_send_up(d->resource, serial, d->seat->timestamp(), id); d->client->flush(); } void TouchInterface::down(qint32 id, quint32 serial, const QPointF &localPos) { Q_D(); if (!d->resource) { return; } wl_touch_send_down(d->resource, serial, d->seat->timestamp(), d->seat->focusedTouchSurface()->resource(), id, wl_fixed_from_double(localPos.x()), wl_fixed_from_double(localPos.y())); d->client->flush(); } TouchInterface::Private *TouchInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/touch_interface.h b/src/server/touch_interface.h index 63b33c6..1139fc8 100644 --- a/src/server/touch_interface.h +++ b/src/server/touch_interface.h @@ -1,61 +1,47 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef WAYLAND_SERVER_TOUCH_INTERFACE_H #define WAYLAND_SERVER_TOUCH_INTERFACE_H #include #include "resource.h" namespace KWayland { namespace Server { class SeatInterface; /** * @brief Resource for the wl_touch interface. * **/ class KWAYLANDSERVER_EXPORT TouchInterface : public Resource { Q_OBJECT public: virtual ~TouchInterface(); private: void down(qint32 id, quint32 serial, const QPointF &localPos); void up(qint32 id, quint32 serial); void frame(); void cancel(); void move(qint32 id, const QPointF &localPos); friend class SeatInterface; explicit TouchInterface(SeatInterface *parent, wl_resource *parentResource); class Private; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::TouchInterface*) #endif diff --git a/src/server/xdgdecoration_interface.cpp b/src/server/xdgdecoration_interface.cpp index 9dd8d5b..e37fd57 100644 --- a/src/server/xdgdecoration_interface.cpp +++ b/src/server/xdgdecoration_interface.cpp @@ -1,252 +1,238 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgdecoration_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "xdgshell_interface.h" #include "xdgshell_stable_interface_p.h" #include "wayland-xdg-decoration-server-protocol.h" #include #include namespace KWayland { namespace Server { class XdgDecorationManagerInterface::Private : public Global::Private { public: Private(XdgDecorationManagerInterface *q, XdgShellInterface *shellInterface, Display *d); QHash m_decorations; KWayland::Server::XdgShellStableInterface *m_shellInterface; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void getToplevelDecorationCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * toplevel); XdgDecorationManagerInterface *q; static const struct zxdg_decoration_manager_v1_interface s_interface; static const quint32 s_version; }; const quint32 XdgDecorationManagerInterface::Private::s_version = 1; XdgDecorationManagerInterface::XdgDecorationManagerInterface(Display *display, XdgShellInterface *shellInterface, QObject *parent): Global(new Private(this, shellInterface, display), parent) { } XdgDecorationManagerInterface::~XdgDecorationManagerInterface() {} #ifndef K_DOXYGEN const struct zxdg_decoration_manager_v1_interface XdgDecorationManagerInterface::Private::s_interface = { destroyCallback, getToplevelDecorationCallback }; #endif void XdgDecorationManagerInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgDecorationManagerInterface::Private::getToplevelDecorationCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * toplevel) { auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); auto shell = p->m_shellInterface->getSurface(toplevel); if (!shell) { wl_resource_post_error(resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED, "No XDGToplevel found object"); return; } if (p->m_decorations.contains(shell)) { wl_resource_post_error(resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED, "XDGDecoration already exists for this surface"); return; } auto xdgDecoration = new XdgDecorationInterface(p->q, shell, resource); xdgDecoration->create(p->display->getConnection(client), wl_resource_get_version(resource), id); if (!xdgDecoration->resource()) { wl_resource_post_no_memory(resource); delete xdgDecoration; return; } p->m_decorations[shell] = xdgDecoration; QObject::connect(xdgDecoration, &QObject::destroyed, p->q, [=]() { p->m_decorations.remove(shell); }); emit p->q->xdgDecorationInterfaceCreated(xdgDecoration); } XdgDecorationManagerInterface::Private::Private(XdgDecorationManagerInterface *q, XdgShellInterface *shellInterface, Display *d) : Global::Private(d, &zxdg_decoration_manager_v1_interface, s_version) , q(q) { m_shellInterface = qobject_cast(shellInterface); Q_ASSERT(m_shellInterface); } void XdgDecorationManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_decoration_manager_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgDecorationManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } class XdgDecorationInterface::Private : public Resource::Private { public: Private(XdgDecorationInterface *q, XdgDecorationManagerInterface *c, XdgShellSurfaceInterface *s, wl_resource *parentResource); ~Private(); Mode m_requestedMode = Mode::Undefined; XdgShellSurfaceInterface* m_shell; private: static void setModeCallback(wl_client *client, wl_resource *resource, uint32_t mode); static void unsetModeCallback(wl_client *client, wl_resource *resource); XdgDecorationInterface *q_func() { return reinterpret_cast(q); } static const struct zxdg_toplevel_decoration_v1_interface s_interface; }; #ifndef K_DOXYGEN const struct zxdg_toplevel_decoration_v1_interface XdgDecorationInterface::Private::s_interface = { resourceDestroyedCallback, setModeCallback, unsetModeCallback }; #endif void XdgDecorationInterface::Private::setModeCallback(wl_client *client, wl_resource *resource, uint32_t mode_raw) { Q_UNUSED(client); auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); Mode mode = Mode::Undefined; switch (mode_raw) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: mode = Mode::ClientSide; break; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: mode = Mode::ServerSide; break; default: break; } p->m_requestedMode = mode; emit p->q_func()->modeRequested(p->m_requestedMode); } void XdgDecorationInterface::Private::unsetModeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); auto p = reinterpret_cast(wl_resource_get_user_data(resource)); Q_ASSERT(p); p->m_requestedMode = Mode::Undefined; emit p->q_func()->modeRequested(p->m_requestedMode); } XdgDecorationInterface::Private::Private(XdgDecorationInterface *q, XdgDecorationManagerInterface *c, XdgShellSurfaceInterface *shell, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_toplevel_decoration_v1_interface, &s_interface) , m_shell(shell) { } XdgDecorationInterface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } XdgDecorationInterface::XdgDecorationInterface(XdgDecorationManagerInterface *parent, XdgShellSurfaceInterface *shell, wl_resource *parentResource) : Resource(new Private(this, parent, shell, parentResource)) { } XdgDecorationInterface::~XdgDecorationInterface() {} void XdgDecorationInterface::configure(XdgDecorationInterface::Mode mode) { switch(mode) { case Mode::ClientSide: zxdg_toplevel_decoration_v1_send_configure(resource(), ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); break; case Mode::ServerSide: zxdg_toplevel_decoration_v1_send_configure(resource(), ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); break; default: //configure(Mode::Undefined) should no-op, as it semantically makes no sense break; } } XdgDecorationInterface::Mode XdgDecorationInterface::requestedMode() const { Q_D(); return d->m_requestedMode; } XdgShellSurfaceInterface* XdgDecorationInterface::surface() const { Q_D(); return d->m_shell; } XdgDecorationInterface::Private *XdgDecorationInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/xdgdecoration_interface.h b/src/server/xdgdecoration_interface.h index b5567eb..a795621 100644 --- a/src/server/xdgdecoration_interface.h +++ b/src/server/xdgdecoration_interface.h @@ -1,109 +1,95 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDG_DECORATION_UNSTABLE_V1_H #define KWAYLAND_SERVER_XDG_DECORATION_UNSTABLE_V1_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class XdgDecorationInterface; class XdgShellInterface; class XdgShellSurfaceInterface; /** * @brief The XdgDecorationManagerInterface class * @since 5.54 */ class KWAYLANDSERVER_EXPORT XdgDecorationManagerInterface : public Global { Q_OBJECT public: ~XdgDecorationManagerInterface() override; Q_SIGNALS: void xdgDecorationInterfaceCreated(XdgDecorationInterface *iface); private: explicit XdgDecorationManagerInterface(Display *display, XdgShellInterface *shellInterface, QObject *parent = nullptr); friend class Display; class Private; }; /** * @brief The XdgDecorationInterface class * @since 5.54 */ class KWAYLANDSERVER_EXPORT XdgDecorationInterface : public Resource { Q_OBJECT public: enum class Mode { Undefined, ClientSide, ServerSide }; Q_ENUM(Mode); ~XdgDecorationInterface() override; /** * Sets the mode the client should be using. * It should be followed by a call to XDGShellSurface::configure() * Once acked, it can relied upon to be applied in the next surface */ void configure(Mode requestedMode); /** * The last mode requested by the client */ Mode requestedMode() const; /** * The surface this decoration is attached to */ XdgShellSurfaceInterface *surface() const; Q_SIGNALS: /** * New mode requested by the client */ void modeRequested(KWayland::Server::XdgDecorationInterface::Mode requestedMode); private: explicit XdgDecorationInterface(XdgDecorationManagerInterface *parent, XdgShellSurfaceInterface *surface, wl_resource *parentResource); friend class XdgDecorationManagerInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/xdgforeign_interface.cpp b/src/server/xdgforeign_interface.cpp index 579a850..04f19ed 100644 --- a/src/server/xdgforeign_interface.cpp +++ b/src/server/xdgforeign_interface.cpp @@ -1,79 +1,65 @@ -/**************************************************************************** -Copyright 2017 Marco Martin +/* + SPDX-FileCopyrightText: 2017 Marco Martin -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgforeign_interface.h" #include "xdgforeign_v2_interface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include "wayland-xdg-foreign-unstable-v2-server-protocol.h" #include #include namespace KWayland { namespace Server { XdgForeignInterface::Private::Private(Display *display, XdgForeignInterface *q) : q(q) { exporter = new XdgExporterUnstableV2Interface(display, q); importer = new XdgImporterUnstableV2Interface(display, q); connect(importer, &XdgImporterUnstableV2Interface::transientChanged, q, &XdgForeignInterface::transientChanged); } XdgForeignInterface::XdgForeignInterface(Display *display, QObject *parent) : QObject(parent), d(new Private(display, this)) { } XdgForeignInterface::~XdgForeignInterface() { delete d->exporter; delete d->importer; delete d; } void XdgForeignInterface::create() { d->exporter->create(); d->importer->create(); } bool XdgForeignInterface::isValid() { return d->exporter->isValid() && d->importer->isValid(); } SurfaceInterface *XdgForeignInterface::transientFor(SurfaceInterface *surface) { return d->importer->transientFor(surface); } } } diff --git a/src/server/xdgforeign_interface.h b/src/server/xdgforeign_interface.h index c498314..89ad131 100644 --- a/src/server/xdgforeign_interface.h +++ b/src/server/xdgforeign_interface.h @@ -1,97 +1,83 @@ -/**************************************************************************** -Copyright 2017 Marco Martin +/* + SPDX-FileCopyrightText: 2017 Marco Martin -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGFOREIGN_INTERFACE_H #define KWAYLAND_SERVER_XDGFOREIGN_INTERFACE_H #include "global.h" #include "resource.h" #include namespace KWayland { namespace Server { class Display; class SurfaceInterface; class XdgExporterUnstableV2Interface; class XdgImporterUnstableV2Interface; /** * This class encapsulates the server side logic of the XdgForeign protocol. * a process can export a surface to be identifiable by a server-wide unique * string handle, and another process can in turn import that surface, and set it * as transient parent for one of its own surfaces. * This parent relationship is traced by the transientChanged signal and the * transientFor method. * * @since 5.40 */ class KWAYLANDSERVER_EXPORT XdgForeignInterface : public QObject { Q_OBJECT public: XdgForeignInterface(Display *display, QObject *parent = nullptr); ~XdgForeignInterface(); /** * Creates the native zxdg_exporter_v2 and zxdg_importer_v2 interfaces * and announces them to the client. */ void create(); /** * @returns true if theimporter and exporter are valid and functional */ bool isValid(); /** * If a client did import a surface and set one of its own as child of the * imported one, this returns the mapping. * @param surface the child surface we want to search an imported transientParent for. * @returns the transient parent of the surface, if found, nullptr otherwise. */ SurfaceInterface *transientFor(SurfaceInterface *surface); Q_SIGNALS: /** * A surface got a new imported transient parent * @param parent is the surface exported by one client and imported into another, which will act as parent. * @param child is the surface that the importer client did set as child of the surface * that it imported. * If one of the two paramenters is nullptr, it means that a previously relation is not * valid anymore and either one of the surfaces has been unmapped, or the parent surface * is not exported anymore. */ void transientChanged(KWayland::Server::SurfaceInterface *child, KWayland::Server::SurfaceInterface *parent); private: friend class Display; friend class XdgExporterUnstableV2Interface; friend class XdgImporterUnstableV2Interface; class Private; Private *d; }; } } #endif diff --git a/src/server/xdgforeign_v2_interface.cpp b/src/server/xdgforeign_v2_interface.cpp index 63d4357..fd553dd 100644 --- a/src/server/xdgforeign_v2_interface.cpp +++ b/src/server/xdgforeign_v2_interface.cpp @@ -1,466 +1,452 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgforeign_interface.h" #include "xdgforeign_v2_interface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "surface_interface_p.h" #include "wayland-xdg-foreign-unstable-v2-server-protocol.h" #include #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN XdgExporterUnstableV2Interface::Private : public Global::Private { public: Private(XdgExporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); XdgForeignInterface *foreignInterface; QHash exportedSurfaces; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); XdgExporterUnstableV2Interface *q; static const struct zxdg_exporter_v2_interface s_interface; static const quint32 s_version; }; const quint32 XdgExporterUnstableV2Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zxdg_exporter_v2_interface XdgExporterUnstableV2Interface::Private::s_interface = { destroyCallback, exportCallback }; #endif XdgExporterUnstableV2Interface::XdgExporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) : Global(new Private(this, display, parent), parent) { } XdgExporterUnstableV2Interface::~XdgExporterUnstableV2Interface() {} XdgExporterUnstableV2Interface::Private *XdgExporterUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } XdgExportedUnstableV2Interface *XdgExporterUnstableV2Interface::exportedSurface(const QString &handle) { Q_D(); auto it = d->exportedSurfaces.constFind(handle); if (it != d->exportedSurfaces.constEnd()) { return it.value(); } return nullptr; } void XdgExporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Q_UNUSED(resource) } void XdgExporterUnstableV2Interface::Private::exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); QPointer e = new XdgExportedUnstableV2Interface(s->q, surface); e->create(s->display->getConnection(client), wl_resource_get_version(resource), id); if (!e->resource()) { wl_resource_post_no_memory(resource); delete e; return; } const QString handle = QUuid::createUuid().toString(); //a surface not exported anymore connect(e.data(), &XdgExportedUnstableV2Interface::unbound, s->q, [s, handle]() { s->exportedSurfaces.remove(handle); emit s->q->surfaceUnexported(handle); }); //if the surface dies before this, this dies too connect(SurfaceInterface::get(surface), &Resource::unbound, s->q, [s, e, handle]() { if (e) { e->deleteLater(); } s->exportedSurfaces.remove(handle); emit s->q->surfaceUnexported(handle); }); s->exportedSurfaces[handle] = e; zxdg_exported_v2_send_handle(e->resource(), handle.toUtf8().constData()); emit s->q->surfaceExported(handle, e); } XdgExporterUnstableV2Interface::Private::Private(XdgExporterUnstableV2Interface *q, Display *d,XdgForeignInterface *foreignInterface) : Global::Private(d, &zxdg_exporter_v2_interface, s_version) , foreignInterface(foreignInterface) , q(q) { } void XdgExporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_exporter_v2_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void XdgExporterUnstableV2Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } class Q_DECL_HIDDEN XdgImporterUnstableV2Interface::Private : public Global::Private { public: Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface); XdgForeignInterface *foreignInterface; QHash importedSurfaces; //child->parent hash QHash parents; //parent->child hash QHash children; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char * handle); XdgImporterUnstableV2Interface *q; static const struct zxdg_importer_v2_interface s_interface; static const quint32 s_version; }; const quint32 XdgImporterUnstableV2Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zxdg_importer_v2_interface XdgImporterUnstableV2Interface::Private::s_interface = { destroyCallback, importCallback }; #endif XdgImporterUnstableV2Interface::XdgImporterUnstableV2Interface(Display *display, XdgForeignInterface *parent) : Global(new Private(this, display, parent), parent) { } XdgImporterUnstableV2Interface::~XdgImporterUnstableV2Interface() { } XdgImportedUnstableV2Interface *XdgImporterUnstableV2Interface::importedSurface(const QString &handle) { Q_D(); auto it = d->importedSurfaces.constFind(handle); if (it != d->importedSurfaces.constEnd()) { return it.value(); } return nullptr; } SurfaceInterface *XdgImporterUnstableV2Interface::transientFor(SurfaceInterface *surface) { Q_D(); auto it = d->parents.constFind(surface); if (it == d->parents.constEnd()) { return nullptr; } return SurfaceInterface::get((*it)->parentResource()); } XdgImporterUnstableV2Interface::Private *XdgImporterUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } void XdgImporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Q_UNUSED(resource) } void XdgImporterUnstableV2Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char *h) { auto s = cast(resource); Q_ASSERT(s->foreignInterface); const QString handle = QString::fromUtf8(h); XdgExportedUnstableV2Interface *exp = s->foreignInterface->d->exporter->exportedSurface(handle); if (!exp) { zxdg_imported_v2_send_destroyed(resource); return; } wl_resource *surface = exp->parentResource(); if (!surface) { zxdg_imported_v2_send_destroyed(resource); return; } QPointer imp = new XdgImportedUnstableV2Interface(s->q, surface); imp->create(s->display->getConnection(client), wl_resource_get_version(resource), id); //surface no longer exported connect(exp, &XdgExportedUnstableV2Interface::unbound, s->q, [s, imp, handle]() { //imp valid when the exported is deleted before the imported if (imp) { zxdg_imported_v2_send_destroyed(imp->resource()); imp->deleteLater(); } s->importedSurfaces.remove(handle); emit s->q->surfaceUnimported(handle); }); connect(imp.data(), &XdgImportedUnstableV2Interface::childChanged, s->q, [s, imp](SurfaceInterface *child) { //remove any previous association auto it = s->children.find(imp); if (it != s->children.end()) { s->parents.remove(*it); s->children.erase(it); } s->parents[child] = imp; s->children[imp] = child; SurfaceInterface *parent = SurfaceInterface::get(imp->parentResource()); emit s->q->transientChanged(child, parent); //child surface destroyed connect(child, &Resource::unbound, s->q, [s, child]() { auto it = s->parents.find(child); if (it != s->parents.end()) { KWayland::Server::XdgImportedUnstableV2Interface* parent = *it; s->children.remove(*it); s->parents.erase(it); emit s->q->transientChanged(nullptr, SurfaceInterface::get(parent->parentResource())); } }); }); //surface no longer imported connect(imp.data(), &XdgImportedUnstableV2Interface::unbound, s->q, [s, handle, imp]() { s->importedSurfaces.remove(handle); emit s->q->surfaceUnimported(handle); auto it = s->children.find(imp); if (it != s->children.end()) { KWayland::Server::SurfaceInterface* child = *it; s->parents.remove(*it); s->children.erase(it); emit s->q->transientChanged(child, nullptr); } }); if (!imp->resource()) { wl_resource_post_no_memory(resource); delete imp; return; } s->importedSurfaces[handle] = imp; emit s->q->surfaceImported(handle, imp); } XdgImporterUnstableV2Interface::Private::Private(XdgImporterUnstableV2Interface *q, Display *d, XdgForeignInterface *foreignInterface) : Global::Private(d, &zxdg_importer_v2_interface, s_version) , foreignInterface(foreignInterface) , q(q) { } void XdgImporterUnstableV2Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_importer_v2_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); // TODO: should we track? } void XdgImporterUnstableV2Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } class Q_DECL_HIDDEN XdgExportedUnstableV2Interface::Private : public Resource::Private { public: Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource); ~Private(); private: XdgExportedUnstableV2Interface *q_func() { return reinterpret_cast(q); } static const struct zxdg_exported_v2_interface s_interface; }; #ifndef K_DOXYGEN const struct zxdg_exported_v2_interface XdgExportedUnstableV2Interface::Private::s_interface = { resourceDestroyedCallback }; #endif XdgExportedUnstableV2Interface::XdgExportedUnstableV2Interface(XdgExporterUnstableV2Interface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } XdgExportedUnstableV2Interface::~XdgExportedUnstableV2Interface() {} XdgExportedUnstableV2Interface::Private *XdgExportedUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } XdgExportedUnstableV2Interface::Private::Private(XdgExportedUnstableV2Interface *q, XdgExporterUnstableV2Interface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_exported_v2_interface, &s_interface) { } XdgExportedUnstableV2Interface::Private::~Private() {} class Q_DECL_HIDDEN XdgImportedUnstableV2Interface::Private : public Resource::Private { public: Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource); ~Private(); QPointer parentOf; private: static void setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface); XdgImportedUnstableV2Interface *q_func() { return reinterpret_cast(q); } static const struct zxdg_imported_v2_interface s_interface; }; #ifndef K_DOXYGEN const struct zxdg_imported_v2_interface XdgImportedUnstableV2Interface::Private::s_interface = { resourceDestroyedCallback, setParentOfCallback }; #endif XdgImportedUnstableV2Interface::XdgImportedUnstableV2Interface(XdgImporterUnstableV2Interface *parent, wl_resource *parentResource) : Resource(new Private(this, parent, parentResource)) { } XdgImportedUnstableV2Interface::~XdgImportedUnstableV2Interface() {} XdgImportedUnstableV2Interface::Private *XdgImportedUnstableV2Interface::d_func() const { return reinterpret_cast(d.data()); } SurfaceInterface *XdgImportedUnstableV2Interface::child() const { Q_D(); - return d->parentOf.data(); + return d->parentOf.data(); } void XdgImportedUnstableV2Interface::Private::setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface) { Q_UNUSED(client) auto s = cast(resource); SurfaceInterface *surf = SurfaceInterface::get(surface); if (!surf) { return; } s->parentOf = surf; emit s->q_func()->childChanged(surf); } XdgImportedUnstableV2Interface::Private::Private(XdgImportedUnstableV2Interface *q, XdgImporterUnstableV2Interface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_imported_v2_interface, &s_interface) { } XdgImportedUnstableV2Interface::Private::~Private() {} } } diff --git a/src/server/xdgforeign_v2_interface_p.h b/src/server/xdgforeign_v2_interface_p.h index c3fce2e..e69232d 100644 --- a/src/server/xdgforeign_v2_interface_p.h +++ b/src/server/xdgforeign_v2_interface_p.h @@ -1,124 +1,110 @@ -/**************************************************************************** -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGFOREIGNV2_INTERFACE_P_H #define KWAYLAND_SERVER_XDGFOREIGNV2_INTERFACE_P_H #include "global.h" #include "resource.h" namespace KWayland { namespace Server { class Display; class SurfaceInterface; class XdgExportedUnstableV2Interface; class XdgImportedUnstableV2Interface; class Q_DECL_HIDDEN XdgForeignInterface::Private { public: Private(Display *display, XdgForeignInterface *q); XdgForeignInterface *q; XdgExporterUnstableV2Interface *exporter; XdgImporterUnstableV2Interface *importer; }; class Q_DECL_HIDDEN XdgExporterUnstableV2Interface : public Global { Q_OBJECT public: virtual ~XdgExporterUnstableV2Interface(); XdgExportedUnstableV2Interface *exportedSurface(const QString &handle); Q_SIGNALS: void surfaceExported(const QString &handle, XdgExportedUnstableV2Interface *exported); void surfaceUnexported(const QString &handle); private: explicit XdgExporterUnstableV2Interface(Display *display, XdgForeignInterface *parent = nullptr); friend class Display; friend class XdgForeignInterface; class Private; Private *d_func() const; }; class Q_DECL_HIDDEN XdgImporterUnstableV2Interface : public Global { Q_OBJECT public: virtual ~XdgImporterUnstableV2Interface(); XdgImportedUnstableV2Interface *importedSurface(const QString &handle); SurfaceInterface *transientFor(SurfaceInterface *surface); Q_SIGNALS: void surfaceImported(const QString &handle, XdgImportedUnstableV2Interface *imported); void surfaceUnimported(const QString &handle); void transientChanged(KWayland::Server::SurfaceInterface *child, KWayland::Server::SurfaceInterface *parent); private: explicit XdgImporterUnstableV2Interface(Display *display, XdgForeignInterface *parent = nullptr); friend class Display; friend class XdgForeignInterface; class Private; Private *d_func() const; }; class Q_DECL_HIDDEN XdgExportedUnstableV2Interface : public Resource { Q_OBJECT public: virtual ~XdgExportedUnstableV2Interface(); private: explicit XdgExportedUnstableV2Interface(XdgExporterUnstableV2Interface *parent, wl_resource *parentResource); friend class XdgExporterUnstableV2Interface; class Private; Private *d_func() const; }; class Q_DECL_HIDDEN XdgImportedUnstableV2Interface : public Resource { Q_OBJECT public: virtual ~XdgImportedUnstableV2Interface(); SurfaceInterface *child() const; Q_SIGNALS: void childChanged(KWayland::Server::SurfaceInterface *child); private: explicit XdgImportedUnstableV2Interface(XdgImporterUnstableV2Interface *parent, wl_resource *parentResource); friend class XdgImporterUnstableV2Interface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/xdgoutput_interface.cpp b/src/server/xdgoutput_interface.cpp index 31a419e..2f03d8e 100644 --- a/src/server/xdgoutput_interface.cpp +++ b/src/server/xdgoutput_interface.cpp @@ -1,316 +1,302 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgoutput_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "output_interface.h" #include #include namespace KWayland { namespace Server { class XdgOutputManagerInterface::Private : public Global::Private { public: Private(XdgOutputManagerInterface *q, Display *d); QHash outputs; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void getXdgOutputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * output); XdgOutputManagerInterface *q; static const struct zxdg_output_manager_v1_interface s_interface; static const quint32 s_version; }; const quint32 XdgOutputManagerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zxdg_output_manager_v1_interface XdgOutputManagerInterface::Private::s_interface = { destroyCallback, getXdgOutputCallback }; #endif class XdgOutputV1Interface: public Resource { public: XdgOutputV1Interface(XdgOutputManagerInterface *parent, wl_resource *parentResource); ~XdgOutputV1Interface(); void setLogicalSize(const QSize &size); void setLogicalPosition(const QPoint &pos); void done(); private: class Private; }; class XdgOutputInterface::Private { public: void resourceConnected(XdgOutputV1Interface *resource); void resourceDisconnected(XdgOutputV1Interface *resource); QPoint pos; QSize size; bool dirty = false; bool doneOnce = false; QList resources; }; XdgOutputManagerInterface::XdgOutputManagerInterface(Display *display, QObject *parent) : Global(new XdgOutputManagerInterface::Private(this, display), parent) { } XdgOutputManagerInterface::~XdgOutputManagerInterface() {} XdgOutputInterface* XdgOutputManagerInterface::createXdgOutput(OutputInterface *output, QObject *parent) { Q_D(); if (!d->outputs.contains(output)) { auto xdgOutput = new XdgOutputInterface(parent); d->outputs[output] = xdgOutput; //as XdgOutput lifespan is managed by user, delete our mapping when either //it or the relevant Output gets deleted connect(output, &QObject::destroyed, this, [this, output]() { Q_D(); d->outputs.remove(output); }); connect(xdgOutput, &QObject::destroyed, this, [this, output]() { Q_D(); d->outputs.remove(output); }); } return d->outputs[output]; } XdgOutputManagerInterface::Private* XdgOutputManagerInterface::d_func() const { return reinterpret_cast(d.data()); } void XdgOutputManagerInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgOutputManagerInterface::Private::getXdgOutputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * outputResource) { auto d = cast(resource); auto output = OutputInterface::get(outputResource); if (!output) { // output client is requesting XdgOutput for an Output that doesn't exist return; } if (!d->outputs.contains(output)) { return; //server hasn't created an XdgOutput for this output yet, give the client nothing } auto iface = new XdgOutputV1Interface(d->q, resource); iface->create(d->display->getConnection(client), wl_resource_get_version(resource), id); if (!iface->resource()) { wl_resource_post_no_memory(resource); delete iface; return; } auto xdgOutput = d->outputs[output]; xdgOutput->d->resourceConnected(iface); connect(iface, &XdgOutputV1Interface::unbound, xdgOutput, [xdgOutput, iface]() { xdgOutput->d->resourceDisconnected(iface); }); } XdgOutputManagerInterface::Private::Private(XdgOutputManagerInterface *qptr, Display *d) : Global::Private(d, &zxdg_output_manager_v1_interface, s_version) , q(qptr) { } void XdgOutputManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&zxdg_output_manager_v1_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgOutputManagerInterface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) } XdgOutputInterface::XdgOutputInterface(QObject *parent): QObject(parent), d(new XdgOutputInterface::Private) { } XdgOutputInterface::~XdgOutputInterface() {} void XdgOutputInterface::setLogicalSize(const QSize &size) { if (size == d->size) { return; } d->size = size; d->dirty = true; for(auto resource: d->resources) { resource->setLogicalSize(size); } } QSize XdgOutputInterface::logicalSize() const { return d->size; } void XdgOutputInterface::setLogicalPosition(const QPoint &pos) { if (pos == d->pos) { return; } d->pos = pos; d->dirty = true; for(auto resource: d->resources) { resource->setLogicalPosition(pos); } } QPoint XdgOutputInterface::logicalPosition() const { return d->pos; } void XdgOutputInterface::done() { d->doneOnce = true; if (!d->dirty) { return; } d->dirty = false; for(auto resource: d->resources) { resource->done(); } } void XdgOutputInterface::Private::resourceConnected(XdgOutputV1Interface *resource) { resource->setLogicalPosition(pos); resource->setLogicalSize(size); if (doneOnce) { resource->done(); } resources << resource; } void XdgOutputInterface::Private::resourceDisconnected(XdgOutputV1Interface *resource) { resources.removeOne(resource); } class XdgOutputV1Interface::Private : public Resource::Private { public: Private(XdgOutputV1Interface *q, XdgOutputManagerInterface *c, wl_resource *parentResource); ~Private(); private: XdgOutputV1Interface *q_func() { return reinterpret_cast(q); } static const struct zxdg_output_v1_interface s_interface; }; XdgOutputV1Interface::XdgOutputV1Interface(XdgOutputManagerInterface *parent, wl_resource *parentResource) :Resource(new XdgOutputV1Interface::Private(this, parent, parentResource)) {} XdgOutputV1Interface::~XdgOutputV1Interface() {} void XdgOutputV1Interface::setLogicalSize(const QSize &size) { if (!d->resource) { return; } zxdg_output_v1_send_logical_size(d->resource, size.width(), size.height()); } void XdgOutputV1Interface::setLogicalPosition(const QPoint &pos) { if (!d->resource) { return; } zxdg_output_v1_send_logical_position(d->resource, pos.x(), pos.y()); } void XdgOutputV1Interface::done() { if (!d->resource) { return; } zxdg_output_v1_send_done(d->resource); } #ifndef K_DOXYGEN const struct zxdg_output_v1_interface XdgOutputV1Interface::Private::s_interface = { resourceDestroyedCallback }; #endif XdgOutputV1Interface::Private::Private(XdgOutputV1Interface *q, XdgOutputManagerInterface *c, wl_resource *parentResource) : Resource::Private(q, c, parentResource, &zxdg_output_v1_interface, &s_interface) { } XdgOutputV1Interface::Private::~Private() { if (resource) { wl_resource_destroy(resource); resource = nullptr; } } } } diff --git a/src/server/xdgoutput_interface.h b/src/server/xdgoutput_interface.h index ec70fda..fde80b7 100644 --- a/src/server/xdgoutput_interface.h +++ b/src/server/xdgoutput_interface.h @@ -1,121 +1,107 @@ -/**************************************************************************** -Copyright 2018 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2018 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGOUTPUT_INTERFACE_H #define KWAYLAND_SERVER_XDGOUTPUT_INTERFACE_H #include "global.h" #include "resource.h" #include /* * In terms of protocol XdgOutputInterface are a resource * but for the sake of sanity, we should treat XdgOutputs as globals like Output is * Hence this doesn't match most of kwayland API paradigms. */ namespace KWayland { namespace Server { class Display; class OutputInterface; class XdgOutputInterface; /** * Global manager for XdgOutputs * @since 5.47 */ class KWAYLANDSERVER_EXPORT XdgOutputManagerInterface : public Global { Q_OBJECT public: virtual ~XdgOutputManagerInterface(); /** * Creates an XdgOutputInterface object for an existing Output * which exposes XDG specific properties of outputs * * @arg output the wl_output interface this XDG output is for * @parent the parent of the newly created object */ XdgOutputInterface* createXdgOutput(OutputInterface *output, QObject *parent); private: explicit XdgOutputManagerInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; /** * Extension to Output * Users should set all relevant values on creation and on future changes. * done() should be explicitly called after change batches including initial setting. * @since 5.47 */ class KWAYLANDSERVER_EXPORT XdgOutputInterface : public QObject { Q_OBJECT public: virtual ~XdgOutputInterface(); /** * Sets the size of this output in logical co-ordinates. * Users should call done() after setting all values */ void setLogicalSize(const QSize &size); /** * Returns the last set logical size on this output */ QSize logicalSize() const; /** * Sets the topleft position of this output in logical co-ordinates. * Users should call done() after setting all values * @see OutputInterface::setPosition */ void setLogicalPosition(const QPoint &pos); /** * Returns the last set logical position on this output */ QPoint logicalPosition() const; /** * Submit changes to all clients */ void done(); private: explicit XdgOutputInterface(QObject *parent); friend class XdgOutputManagerInterface; class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/xdgshell_interface.cpp b/src/server/xdgshell_interface.cpp index e361e7f..3d001b2 100644 --- a/src/server/xdgshell_interface.cpp +++ b/src/server/xdgshell_interface.cpp @@ -1,297 +1,283 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_interface_p.h" namespace KWayland { namespace Server { XdgShellInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } void XdgShellInterface::Private::setupTimer(quint32 serial) { QTimer *pingTimer = new QTimer(); pingTimer->setSingleShot(false); pingTimer->setInterval(1000); int attempt = 0; connect(pingTimer, &QTimer::timeout, q, [this, serial, attempt]() mutable { ++attempt; if (attempt == 1) { emit q->pingDelayed(serial); } else { emit q->pingTimeout(serial); auto timerIt = pingTimers.find(serial); if (timerIt != pingTimers.end()) { delete timerIt.value(); pingTimers.erase(timerIt); } } }); pingTimers.insert(serial, pingTimer); pingTimer->start(); } XdgShellInterface::XdgShellInterface(Private *d, QObject *parent) : Global(d, parent) { } XdgShellInterface::~XdgShellInterface() = default; XdgShellSurfaceInterface *XdgShellInterface::getSurface(wl_resource *native) { Q_UNUSED(native) return nullptr; } XdgShellInterfaceVersion XdgShellInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } quint32 XdgShellInterface::ping(XdgShellSurfaceInterface * surface) { return d_func()->ping(surface); } XdgShellInterface::Private *XdgShellInterface::d_func() const { return reinterpret_cast(d.data()); } XdgShellSurfaceInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellSurfaceInterface *q, Global *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) , GenericShellSurface(q, surface) , interfaceVersion(interfaceVersion) { } XdgShellSurfaceInterface::Private::~Private() = default; XdgShellSurfaceInterface::XdgShellSurfaceInterface(Private *p) : Resource(p) { } XdgShellSurfaceInterface::~XdgShellSurfaceInterface() = default; XdgShellInterfaceVersion XdgShellSurfaceInterface::interfaceVersion() const { Q_D(); return d->interfaceVersion; } quint32 XdgShellSurfaceInterface::configure(States states, const QSize &size) { Q_D(); return d->configure(states, size); } bool XdgShellSurfaceInterface::isConfigurePending() const { Q_D(); return !d->configureSerials.isEmpty(); } SurfaceInterface *XdgShellSurfaceInterface::surface() const { Q_D(); return d->surface; } QString XdgShellSurfaceInterface::title() const { Q_D(); return d->title; } QByteArray XdgShellSurfaceInterface::windowClass() const { Q_D(); return d->windowClass; } bool XdgShellSurfaceInterface::isTransient() const { Q_D(); return !d->parent.isNull(); } QPointer XdgShellSurfaceInterface::transientFor() const { Q_D(); return d->parent; } void XdgShellSurfaceInterface::close() { Q_D(); d->close(); } QRect XdgShellSurfaceInterface::windowGeometry() const { Q_D(); return d->windowGeometry(); } QSize XdgShellSurfaceInterface::minimumSize() const { Q_D(); return d->minimumSize(); } QSize XdgShellSurfaceInterface::maximumSize() const { Q_D(); return d->maximumSize(); } XdgShellSurfaceInterface::Private *XdgShellSurfaceInterface::d_func() const { return reinterpret_cast(d.data()); } XdgShellPopupInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellPopupInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation) : Resource::Private(q, c, parentResource, interface, implementation) , GenericShellSurface(q, surface) , interfaceVersion(interfaceVersion) { } XdgShellPopupInterface::Private::~Private() = default; XdgShellPopupInterface::XdgShellPopupInterface(Private *p) : Resource(p) { } XdgShellPopupInterface::~XdgShellPopupInterface() = default; SurfaceInterface *XdgShellPopupInterface::surface() const { Q_D(); return d->surface; } QPointer XdgShellPopupInterface::transientFor() const { Q_D(); return d->parent; } QSize XdgShellPopupInterface::initialSize() const { Q_D(); return d->initialSize; } QPoint XdgShellPopupInterface::transientOffset() const { QRect rect = anchorRect(); const QPoint center = rect.isEmpty() ? rect.topLeft() : rect.center(); rect = rect.adjusted(0,0,1,1); //compensate for the stupid QRect::right +1 fiasco switch(anchorEdge()) { case Qt::TopEdge | Qt::LeftEdge: return rect.topLeft(); case Qt::TopEdge: return QPoint(center.x(), rect.y()); case Qt::TopEdge | Qt::RightEdge: return rect.topRight(); case Qt::RightEdge: return QPoint(rect.right(), center.y()); case Qt::BottomEdge | Qt::RightEdge: return rect.bottomRight(); case Qt::BottomEdge: return QPoint(center.x(), rect.bottom()); case Qt::BottomEdge | Qt::LeftEdge: return rect.bottomLeft(); case Qt::LeftEdge: return QPoint(rect.left(), center.y()); default: return center; } Q_UNREACHABLE(); } QRect XdgShellPopupInterface::anchorRect() const { Q_D(); return d->anchorRect; } Qt::Edges XdgShellPopupInterface::anchorEdge() const { Q_D(); return d->anchorEdge; } Qt::Edges XdgShellPopupInterface::gravity() const { Q_D(); return d->gravity; } QPoint XdgShellPopupInterface::anchorOffset() const { Q_D(); return d->anchorOffset; } PositionerConstraints XdgShellPopupInterface::constraintAdjustments() const { Q_D(); return d->constraintAdjustments; } QRect XdgShellPopupInterface::windowGeometry() const { Q_D(); return d->windowGeometry(); } void XdgShellPopupInterface::popupDone() { Q_D(); return d->popupDone(); } quint32 XdgShellPopupInterface::configure(const QRect &rect) { Q_D(); return d->configure(rect); } XdgShellPopupInterface::Private *XdgShellPopupInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/xdgshell_interface.h b/src/server/xdgshell_interface.h index efba2a9..a0a8b0b 100644 --- a/src/server/xdgshell_interface.h +++ b/src/server/xdgshell_interface.h @@ -1,526 +1,512 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_H #define KWAYLAND_SERVER_XDGSHELL_INTERFACE_H #include "global.h" #include "resource.h" #include #include namespace KWayland { namespace Server { class OutputInterface; class SeatInterface; class SurfaceInterface; class XdgShellPopupInterface; class XdgShellSurfaceInterface; template class GenericShellSurface; /** * Enum describing the different InterfaceVersion encapsulated in this implementation. * * @since 5.25 **/ enum class XdgShellInterfaceVersion { /** * xdg_shell (unstable v5) **/ UnstableV5, /** * zxdg_shell_v6 (unstable v6) * @since 5.39 **/ UnstableV6, /** xdg_wm_base (stable) @since 5.48 */ Stable }; /** * Flags describing how a popup should be reposition if constrained * @since 5.39 */ enum class PositionerConstraint { /** * Slide the popup on the X axis until there is room */ SlideX = 1 << 0, /** * Slide the popup on the Y axis until there is room */ SlideY = 1 << 1, /** * Invert the anchor and gravity on the X axis */ FlipX = 1 << 2, /** * Invert the anchor and gravity on the Y axis */ FlipY = 1 << 3, /** * Resize the popup in the X axis */ ResizeX = 1 << 4, /** * Resize the popup in the Y axis */ ResizeY = 1 << 5 }; Q_DECLARE_FLAGS(PositionerConstraints, PositionerConstraint) /** * * @since 5.25 **/ class KWAYLANDSERVER_EXPORT XdgShellInterface : public Global { Q_OBJECT public: virtual ~XdgShellInterface(); /** * @returns The interface version used by this XdgShellInterface **/ XdgShellInterfaceVersion interfaceVersion() const; /** * @returns The XdgShellSurfaceInterface for the @p native resource. **/ //TODO KF6 make virtual XdgShellSurfaceInterface *getSurface(wl_resource *native); /** * Confirm the client is still alive and responding * * Will result in pong being emitted * * @returns unique identifier for this request * @since 5.39 */ quint32 ping(XdgShellSurfaceInterface * surface); Q_SIGNALS: void surfaceCreated(KWayland::Server::XdgShellSurfaceInterface *surface); /** * Emitted whenever a new popup got created. * * A popup only gets created in response to an action on the @p seat. * * * @param surface The popup xdg shell surface which got created * @param seat The seat on which an action triggered the popup * @param serial The serial of the action on the seat * * XDGV5 only * Use both xdgPopupCreated and XdgShellPopupInterface::grabbed to cover both XDGV5 and XDGV6 **/ void popupCreated(KWayland::Server::XdgShellPopupInterface *surface, KWayland::Server::SeatInterface *seat, quint32 serial); /* * Emitted whenever a new popup gets created. * * @param surface The popup xdg shell surface which got created * @since 5.39 */ void xdgPopupCreated(KWayland::Server::XdgShellPopupInterface *surface); /* * Emitted in response to a ping request * * @param serial unique identifier for the request * @since 5.39 */ void pongReceived(quint32 serial); /* * Emitted when the application takes more than expected * to answer to a ping, this will always be emitted before * eventuallt pingTimeout gets emitted * * @param serial unique identifier for the request * @since 5.39 */ void pingDelayed(quint32 serial); /* * Emitted when the application doesn't answer to a ping * and the serve gave up on it * * @param serial unique identifier for the request * @since 5.39 */ void pingTimeout(quint32 serial); protected: class Private; explicit XdgShellInterface(Private *d, QObject *parent = nullptr); private: Private *d_func() const; }; /** * * @since 5.25 **/ class KWAYLANDSERVER_EXPORT XdgShellSurfaceInterface : public Resource { Q_OBJECT public: virtual ~XdgShellSurfaceInterface(); /** * @returns The interface version used by this XdgShellSurfaceInterface **/ XdgShellInterfaceVersion interfaceVersion() const; /** * States the Surface can be in **/ enum class State { /** * The Surface is maximized. **/ Maximized = 1 << 0, /** * The Surface is fullscreen. **/ Fullscreen = 1 << 1, /** * The Surface is currently being resized by the Compositor. **/ Resizing = 1 << 2, /** * The Surface is considered active. Does not imply keyboard focus. **/ Activated = 1 << 3 }; Q_DECLARE_FLAGS(States, State) /** * Sends a configure event to the Surface. * This tells the Surface the current @p states it is in and the @p size it should have. * If @p size has width and height at @c 0, the Surface can choose the size. * * The Surface acknowledges the configure event with {@link configureAcknowledged}. * * @param states The states the surface is in * @param size The requested size * @returns The serial of the configure event * @see configureAcknowledged * @see isConfigurePending **/ quint32 configure(States states, const QSize &size = QSize(0, 0)); /** * @returns @c true if there is a not yet acknowledged configure event. * @see configure * @see configureAcknowledged **/ bool isConfigurePending() const; /** * @return The SurfaceInterface this XdgSurfaceV5Interface got created for. **/ SurfaceInterface *surface() const; /** * @returns The title of this surface. * @see titleChanged **/ QString title() const; QByteArray windowClass() const; /** * @returns Whether this Surface is a transient for another Surface, that is it has a parent. * @see transientFor **/ bool isTransient() const; /** * @returns the parent surface if the surface is a transient for another surface * @see isTransient **/ QPointer transientFor() const; /** * Request the client to close the window. **/ void close(); /** * @brief windowGeometry * The geometry of the window within the buffer * * If invalid, the geometry of the bufer should be used instead * @since 5.59 */ QRect windowGeometry() const; /** * @returns The minimum size for the window specified by the client. * @since 5.65 */ QSize minimumSize() const; /** * @returns The maximum size for the window specified by the client. * @since 5.65 */ QSize maximumSize() const; Q_SIGNALS: /** * Emitted whenever the title changes. * * @see title **/ void titleChanged(const QString&); /** * Emitted whenever the window class changes. * * @see windowClass **/ void windowClassChanged(const QByteArray&); /** * The surface requested a window move. * * @param seat The SeatInterface on which the surface requested the move * @param serial The serial of the implicit mouse grab which triggered the move **/ void moveRequested(KWayland::Server::SeatInterface *seat, quint32 serial); /** * The surface requested a window resize. * * @param seat The SeatInterface on which the surface requested the resize * @param serial The serial of the implicit mouse grab which triggered the resize * @param edges A hint which edges are involved in the resize **/ void resizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges); void windowMenuRequested(KWayland::Server::SeatInterface *seat, quint32 serial, const QPoint &surfacePos); /** * The surface requested a change of maximized state. * @param maximized Whether the window wants to be maximized **/ void maximizedChanged(bool maximized); /** * The surface requested a change of fullscreen state * @param fullscreen Whether the window wants to be fullscreen * @param output An optional output hint on which the window wants to be fullscreen **/ void fullscreenChanged(bool fullscreen, KWayland::Server::OutputInterface *output); /** * The surface requested to be minimized. **/ void minimizeRequested(); /** * A configure event with @p serial got acknowledged. * @see configure **/ void configureAcknowledged(quint32 serial); /** * Emitted whenever the parent surface changes. * @see isTransient * @see transientFor **/ void transientForChanged(); /** * Emitted whenever the maximum size hint changes * @since 5.39 */ void maxSizeChanged(const QSize &size); /** * Emitted whenever the minimum size hint changes * @since 5.39 */ void minSizeChanged(const QSize &size); /** * @brief windowGeometryChanged * @param windowGeometry the newly changed windowGeometry * @since 5.59 */ void windowGeometryChanged(const QRect &windowGeometry); protected: class Private; explicit XdgShellSurfaceInterface(Private *p); private: Private *d_func() const; friend class GenericShellSurface; }; /** * * @since 5.25 **/ class KWAYLANDSERVER_EXPORT XdgShellPopupInterface : public Resource { Q_OBJECT public: virtual ~XdgShellPopupInterface(); /** * @return The SurfaceInterface this XdgShellPopupInterface got created for. **/ SurfaceInterface *surface() const; /* * Ask the popup surface to configure itself for the given configuration. * * @arg rect. The position of the surface relative to the transient parent * @since 5.39 */ quint32 configure(const QRect &rect); /** * @returns the parent surface. * @see transientOffset **/ QPointer transientFor() const; /** * The offset of the Surface in the coordinate system of the SurfaceInterface this surface is a transient for. * * For XDG V6 this returns the point on the anchorRect defined by the anchor edge. * * @returns offset in parent coordinate system. * @see transientFor **/ QPoint transientOffset() const; /** * The size of the surface that is to be positioned. * * @since 5.39 */ QSize initialSize() const; /** * The area this popup should be positioned around * @since 5.39 */ QRect anchorRect() const; /** * Which edge of the anchor should the popup be positioned around * @since 5.39 */ Qt::Edges anchorEdge() const; /** * An additional offset that should be applied to the popup from the anchor rect * * @since 5.39 */ QPoint anchorOffset() const; /** * Specifies in what direction the popup should be positioned around the anchor * i.e if the gravity is "bottom", then then the top of top of the popup will be at the anchor edge * if the gravity is top, then the bottom of the popup will be at the anchor edge * * @since 5.39 */ //DAVE left + right is illegal, so this is possible a useless return value? Maybe an enum with 9 entries left, topleft, top, .. Qt::Edges gravity() const; /** * Specifies how the compositor should position the popup if it does not fit in the requested position * @since 5.39 */ PositionerConstraints constraintAdjustments() const; /** * Dismiss this popup. This indicates to the client that it should destroy this popup. * The Compositor can invoke this method when e.g. the user clicked outside the popup * to dismiss it. **/ void popupDone(); /** * @brief windowGeometryChanged * @param windowGeometry the newly changed geometry of the window contents within the buffer * @since 5.59 */ QRect windowGeometry()const; Q_SIGNALS: /** * A configure event with @p serial got acknowledged. * Note: XdgV6 only * @see configure * @since 5.39 **/ void configureAcknowledged(quint32 serial); /** * The client requested that this popup takes an explicit grab * * @param seat The seat on which an action triggered the popup * @param serial The serial of the action on the seat * @since 5.39 */ void grabRequested(KWayland::Server::SeatInterface *seat, quint32 serial); /** * @brief windowGeometryChanged * @param windowGeometry the newly changed windowGeometry * @since 5.59 */ void windowGeometryChanged(const QRect &windowGeometry); protected: class Private; explicit XdgShellPopupInterface(Private *p); private: friend class GenericShellSurface; Private *d_func() const; }; } } Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface *) Q_DECLARE_METATYPE(KWayland::Server::XdgShellPopupInterface *) Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::State) Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::States) Q_DECLARE_OPERATORS_FOR_FLAGS(KWayland::Server::PositionerConstraints) #endif diff --git a/src/server/xdgshell_interface_p.h b/src/server/xdgshell_interface_p.h index 9bd22be..f7aeaab 100644 --- a/src/server/xdgshell_interface_p.h +++ b/src/server/xdgshell_interface_p.h @@ -1,111 +1,97 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_P_H #define KWAYLAND_SERVER_XDGSHELL_INTERFACE_P_H #include "xdgshell_interface.h" #include "global_p.h" #include "generic_shell_surface_p.h" #include "resource_p.h" #include namespace KWayland { namespace Server { class XdgShellInterface::Private : public Global::Private { public: XdgShellInterfaceVersion interfaceVersion; virtual quint32 ping(XdgShellSurfaceInterface * surface) = 0; void setupTimer(quint32 serial); //pingserial/timer correspondence QHash pingTimers; protected: Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version); XdgShellInterface *q; }; class XdgShellSurfaceInterface::Private : public Resource::Private, public GenericShellSurface { public: virtual ~Private(); virtual void close() = 0; virtual quint32 configure(States states, const QSize &size) = 0; virtual QRect windowGeometry() const = 0; virtual QSize minimumSize() const = 0; virtual QSize maximumSize() const = 0; XdgShellSurfaceInterface *q_func() { return reinterpret_cast(q); } QVector configureSerials; QPointer parent; XdgShellInterfaceVersion interfaceVersion; protected: Private(XdgShellInterfaceVersion interfaceVersion, XdgShellSurfaceInterface *q, Global *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation); }; class XdgShellPopupInterface::Private : public Resource::Private, public GenericShellSurface { public: virtual ~Private(); virtual void popupDone() = 0; virtual QRect windowGeometry() const = 0; XdgShellPopupInterface *q_func() { return reinterpret_cast(q); } virtual quint32 configure(const QRect &rect) { Q_UNUSED(rect) return 0; }; QVector configureSerials; QPointer parent; QSize initialSize; /* * */ QRect anchorRect; Qt::Edges anchorEdge; Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; XdgShellInterfaceVersion interfaceVersion; protected: Private(XdgShellInterfaceVersion interfaceVersion, XdgShellPopupInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation); }; } } #endif diff --git a/src/server/xdgshell_stable_interface.cpp b/src/server/xdgshell_stable_interface.cpp index 5c569a0..ce7b056 100644 --- a/src/server/xdgshell_stable_interface.cpp +++ b/src/server/xdgshell_stable_interface.cpp @@ -1,1121 +1,1107 @@ -/**************************************************************************** -Copyright 2017 David Edmundson -Copyright 2019 Vlad Zahorodnii - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + SPDX-FileCopyrightText: 2019 Vlad Zahorodnii + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_stable_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "global.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class XdgShellStableInterface::Private : public XdgShellInterface::Private { public: Private(XdgShellStableInterface *q, Display *d); QVector surfaces; QVector positioners; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return static_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellStableInterface *q; static const struct xdg_wm_base_interface s_interface; static const quint32 s_version; QHash resources; }; class XdgPopupStableInterface::Private : public XdgShellPopupInterface::Private { public: Private(XdgPopupStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private() override; QRect windowGeometry() const override; void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void popupDone() override; quint32 configure(const QRect &rect) override; XdgPopupStableInterface *q_func() { return static_cast(q); } private: void setWindowGeometryCallback(const QRect &rect); static void grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); static const struct xdg_popup_interface s_interface; struct ShellSurfaceState { QRect windowGeometry; bool windowGeometryIsSet = false; }; ShellSurfaceState m_currentState; ShellSurfaceState m_pendingState; friend class XdgSurfaceStableInterface; }; class XdgSurfaceStableInterface::Private : public KWayland::Server::Resource::Private { public: Private(XdgSurfaceStableInterface* q, XdgShellStableInterface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private() override; XdgSurfaceStableInterface *q_func() { return static_cast(q); } void createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentWindow, wl_resource *positioner); XdgShellStableInterface *m_shell; SurfaceInterface *m_surface; //effectively a union, only one of these should be populated. //a surface cannot have two roles QPointer m_topLevel; QPointer m_popup; private: static void destroyCallback(wl_client *client, wl_resource *resource); static void getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct xdg_surface_interface s_interface; }; class XdgTopLevelStableInterface::Private : public XdgShellSurfaceInterface::Private { public: Private(XdgTopLevelStableInterface* q, XdgShellStableInterface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private() override; QRect windowGeometry() const override; QSize minimumSize() const override; QSize maximumSize() const override; void close() override; void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } quint32 configure(States states, const QSize &size) override { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array configureStates; wl_array_init(&configureStates); if (states.testFlag(State::Maximized)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_ACTIVATED; } configureSerials << serial; xdg_toplevel_send_configure(resource, size.width(), size.height(), &configureStates); xdg_surface_send_configure(parentResource, serial); client->flush(); wl_array_release(&configureStates); return serial; }; XdgTopLevelStableInterface *q_func() { return static_cast(q); } private: void setWindowGeometryCallback(const QRect &rect); static void destroyCallback(wl_client *client, wl_resource *resource); static void setParentCallback(struct wl_client *client, struct wl_resource *resource, wl_resource *parent); static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y); static void setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct xdg_toplevel_interface s_interface; struct ShellSurfaceState { QRect windowGeometry; QSize minimumSize = QSize(0, 0); QSize maximiumSize = QSize(INT32_MAX, INT32_MAX); bool windowGeometryIsSet = false; bool minimumSizeIsSet = false; bool maximumSizeIsSet = false; }; ShellSurfaceState m_currentState; ShellSurfaceState m_pendingState; friend class XdgSurfaceStableInterface; }; const quint32 XdgShellStableInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct xdg_wm_base_interface XdgShellStableInterface::Private::s_interface = { destroyCallback, createPositionerCallback, getXdgSurfaceCallback, pongCallback }; #endif void XdgShellStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) auto s = cast(resource); if (!s->surfaces.isEmpty()) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "WMBase destroyed before surfaces"); } wl_resource_destroy(resource); } void XdgShellStableInterface::Private::createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createPositioner(client, wl_resource_get_version(resource), id, resource); } void XdgShellStableInterface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellStableInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceStableInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), XDG_WM_BASE_ERROR_ROLE, "XDG Surface already created"); return; } XdgSurfaceStableInterface *shellSurface = new XdgSurfaceStableInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceStableInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); } void XdgShellStableInterface::Private::createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { Q_UNUSED(client) XdgPositionerStableInterface *positioner = new XdgPositionerStableInterface(q, parentResource); positioners << positioner; QObject::connect(positioner, &Resource::destroyed, q, [this, positioner] { positioners.removeAll(positioner); } ); positioner->d->create(display->getConnection(client), version, id); } void XdgShellStableInterface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellStableInterface::Private::Private(XdgShellStableInterface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::Stable, q, d, &xdg_wm_base_interface, 1) , q(q) { } void XdgShellStableInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&xdg_wm_base_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellStableInterface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgTopLevelStableInterface *XdgShellStableInterface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { auto topLevel = (*it)->topLevel(); if (topLevel && topLevel->resource() == resource) { return topLevel; } } return nullptr; } XdgSurfaceStableInterface *XdgShellStableInterface::realGetSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { if ((*it)->resource() == resource) { return (*it); } } return nullptr; } XdgPositionerStableInterface *XdgShellStableInterface::getPositioner(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->positioners.constBegin(); it != d->positioners.constEnd() ; it++) { if ((*it)->resource() == resource) { return *it; } } return nullptr; } quint32 XdgShellStableInterface::Private::ping(XdgShellSurfaceInterface *surface) { auto client = surface->client()->client(); //from here we can get the resource bound to our global. auto clientXdgShellResource = resources.value(client); if (!clientXdgShellResource) { return 0; } const quint32 pingSerial = display->nextSerial(); xdg_wm_base_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellStableInterface::Private *XdgShellStableInterface::d_func() const { return static_cast(d.data()); } namespace { template <> Qt::Edges edgesToQtEdges(xdg_toplevel_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case XDG_TOPLEVEL_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef K_DOXYGEN const struct xdg_surface_interface XdgSurfaceStableInterface::Private::s_interface = { destroyCallback, getTopLevelCallback, getPopupCallback, setWindowGeometryCallback, ackConfigureCallback }; #endif void XdgSurfaceStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgSurfaceStableInterface::Private::getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createTopLevel(client, wl_resource_get_version(resource), id, resource); } void XdgSurfaceStableInterface::Private::createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { // FIXME: That's incorrect! The client may have asked us to create an xdg-toplevel // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Toplevel already created on this surface"); return; } if (m_popup) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Popup already created on this surface"); return; } m_topLevel = new XdgTopLevelStableInterface (m_shell, m_surface, parentResource); m_topLevel->d->create(m_shell->display()->getConnection(client), version, id); emit m_shell->surfaceCreated(m_topLevel); } void XdgSurfaceStableInterface::Private::getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, resource, parent, positioner); } void XdgSurfaceStableInterface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentSurface, wl_resource *positioner) { // FIXME: That's incorrect! The client may have asked us to create an xdg-popup // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Toplevel already created on this surface"); return; } if (m_popup) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Popup already created on this surface"); return; } auto xdgPositioner = m_shell->getPositioner(positioner); if (!xdgPositioner) { wl_resource_post_error(parentResource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "Invalid positioner"); return; } m_popup = new XdgPopupStableInterface(m_shell, m_surface, parentResource); auto pd = m_popup->d_func(); pd->create(m_shell->display()->getConnection(client), version, id); auto parentXdgSurface = m_shell->realGetSurface(parentSurface); if (!parentXdgSurface) { wl_resource_post_error(parentResource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent"); return; } pd->parent = parentXdgSurface->surface(); pd->initialSize = xdgPositioner->initialSize(); pd->anchorRect = xdgPositioner->anchorRect(); pd->anchorEdge = xdgPositioner->anchorEdge(); pd->gravity = xdgPositioner->gravity(); pd->constraintAdjustments = xdgPositioner->constraintAdjustments(); pd->anchorOffset = xdgPositioner->anchorOffset(); emit m_shell->xdgPopupCreated(m_popup.data()); } void XdgSurfaceStableInterface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_topLevel) { s->m_topLevel->d_func()->ackConfigure(serial); } else if (s->m_popup) { s->m_popup->d_func()->ackConfigure(serial); } } void XdgSurfaceStableInterface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE , "Tried to set invalid xdg-surface geometry"); return; } if (s->m_topLevel) { s->m_topLevel->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } else if (s->m_popup) { s->m_popup->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } } XdgSurfaceStableInterface::Private::Private(XdgSurfaceStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &xdg_surface_interface, &s_interface), m_shell(c), m_surface(surface) { } XdgSurfaceStableInterface::Private::~Private() = default; class XdgPositionerStableInterface::Private : public KWayland::Server::Resource::Private { public: Private(XdgPositionerStableInterface *q, XdgShellStableInterface *c, wl_resource* parentResource); QSize initialSize; QRect anchorRect; Qt::Edges anchorEdge; Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; private: static void setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor); static void setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity); static void setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment); static void setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static const struct xdg_positioner_interface s_interface; }; XdgPositionerStableInterface::Private::Private(XdgPositionerStableInterface *q, XdgShellStableInterface *c, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &xdg_positioner_interface, &s_interface) { } #ifndef K_DOXYGEN const struct xdg_positioner_interface XdgPositionerStableInterface::Private::s_interface = { resourceDestroyedCallback, setSizeCallback, setAnchorRectCallback, setAnchorCallback, setGravityCallback, setConstraintAdjustmentCallback, setOffsetCallback }; #endif void XdgPositionerStableInterface::Private::setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->initialSize = QSize(width, height); } void XdgPositionerStableInterface::Private::setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->anchorRect = QRect(x, y, width, height); } void XdgPositionerStableInterface::Private::setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor) { Q_UNUSED(client) auto s = cast(resource); Qt::Edges qtEdges; switch (anchor) { case XDG_POSITIONER_ANCHOR_TOP: qtEdges = Qt::TopEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_POSITIONER_ANCHOR_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_NONE: break; default: Q_UNREACHABLE(); break; } s->anchorEdge = qtEdges; } void XdgPositionerStableInterface::Private::setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity) { Q_UNUSED(client) auto s = cast(resource); Qt::Edges qtEdges; switch (gravity) { case XDG_POSITIONER_GRAVITY_TOP: qtEdges = Qt::TopEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_POSITIONER_GRAVITY_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_NONE: break; default: Q_UNREACHABLE(); break; } s->gravity = qtEdges; } void XdgPositionerStableInterface::Private::setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment) { Q_UNUSED(client) auto s = cast(resource); PositionerConstraints constraints; if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) { constraints |= PositionerConstraint::SlideX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { constraints |= PositionerConstraint::SlideY; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) { constraints |= PositionerConstraint::FlipX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) { constraints |= PositionerConstraint::FlipY; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) { constraints |= PositionerConstraint::ResizeX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { constraints |= PositionerConstraint::ResizeY; } s->constraintAdjustments = constraints; } void XdgPositionerStableInterface::Private::setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { Q_UNUSED(client) auto s = cast(resource); s->anchorOffset = QPoint(x,y); } QRect XdgTopLevelStableInterface::Private::windowGeometry() const { return m_currentState.windowGeometry; } QSize XdgTopLevelStableInterface::Private::minimumSize() const { return m_currentState.minimumSize; } QSize XdgTopLevelStableInterface::Private::maximumSize() const { return m_currentState.maximiumSize; } void XdgTopLevelStableInterface::Private::close() { xdg_toplevel_send_close(resource); client->flush(); } void XdgTopLevelStableInterface::Private::commit() { const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; const bool minimumSizeChanged = m_pendingState.minimumSizeIsSet; const bool maximumSizeChanged = m_pendingState.maximumSizeIsSet; if (windowGeometryChanged) { m_currentState.windowGeometry = m_pendingState.windowGeometry; } if (minimumSizeChanged) { m_currentState.minimumSize = m_pendingState.minimumSize; } if (maximumSizeChanged) { m_currentState.maximiumSize = m_pendingState.maximiumSize; } m_pendingState = ShellSurfaceState{}; if (windowGeometryChanged) { emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); } if (minimumSizeChanged) { emit q_func()->minSizeChanged(m_currentState.minimumSize); } if (maximumSizeChanged) { emit q_func()->maxSizeChanged(m_currentState.maximiumSize); } } void XdgTopLevelStableInterface::Private::setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel maximum size"); return; } if (width == 0) { width = INT32_MAX; } if (height == 0) { height = INT32_MAX; } auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_pendingState.maximiumSize = QSize(width, height); s->m_pendingState.maximumSizeIsSet = true; } void XdgTopLevelStableInterface::Private::setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel minimum size"); return; } auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_pendingState.minimumSize = QSize(width, height); s->m_pendingState.minimumSizeIsSet = true; } void XdgTopLevelStableInterface::Private::setWindowGeometryCallback(const QRect &rect) { m_pendingState.windowGeometry = rect; m_pendingState.windowGeometryIsSet = true; } const struct xdg_toplevel_interface XdgTopLevelStableInterface::Private::s_interface = { destroyCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, setMaxSizeCallback, setMinSizeCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; void XdgTopLevelStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgTopLevelStableInterface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!parent) { //setting null is valid API. Clear s->parent = nullptr; emit s->q_func()->transientForChanged(); } else { auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } } void XdgTopLevelStableInterface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } XdgTopLevelStableInterface::Private::Private(XdgTopLevelStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::Stable, q, c, surface, parentResource, &xdg_toplevel_interface, &s_interface) { } void XdgTopLevelStableInterface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgTopLevelStableInterface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgTopLevelStableInterface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgTopLevelStableInterface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgTopLevelStableInterface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgTopLevelStableInterface::Private::~Private() = default; #ifndef K_DOXYGEN const struct xdg_popup_interface XdgPopupStableInterface::Private::s_interface = { resourceDestroyedCallback, grabCallback }; #endif XdgPopupStableInterface::Private::Private(XdgPopupStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::Stable, q, c, surface, parentResource, &xdg_popup_interface, &s_interface) { } QRect XdgPopupStableInterface::Private::windowGeometry() const { return m_currentState.windowGeometry; } void XdgPopupStableInterface::Private::commit() { const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; if (windowGeometryChanged) { m_currentState.windowGeometry = m_pendingState.windowGeometry; } m_pendingState = ShellSurfaceState{}; if (windowGeometryChanged) { emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); } } void XdgPopupStableInterface::Private::setWindowGeometryCallback(const QRect &rect) { m_pendingState.windowGeometry = rect; m_pendingState.windowGeometryIsSet = true; } void XdgPopupStableInterface::Private::grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto seatInterface = SeatInterface::get(seat); s->q_func()->grabRequested(seatInterface, serial); } XdgPopupStableInterface::Private::~Private() = default; quint32 XdgPopupStableInterface::Private::configure(const QRect &rect) { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); configureSerials << serial; xdg_popup_send_configure(resource, rect.x(), rect.y(), rect.width(), rect.height()); xdg_surface_send_configure(parentResource, serial); client->flush(); return serial; } void XdgPopupStableInterface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups xdg_popup_send_popup_done(resource); client->flush(); } XdgShellStableInterface::XdgShellStableInterface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } Display* XdgShellStableInterface::display() const { return d->display; } XdgShellStableInterface::~XdgShellStableInterface() = default; XdgSurfaceStableInterface::XdgSurfaceStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, surface, parentResource)) { } XdgSurfaceStableInterface::~XdgSurfaceStableInterface() = default; SurfaceInterface* XdgSurfaceStableInterface::surface() const { Q_D(); return d->m_surface; } XdgPositionerStableInterface::XdgPositionerStableInterface(XdgShellStableInterface *parent, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, parentResource)) { } QSize XdgPositionerStableInterface::initialSize() const { Q_D(); return d->initialSize; } QRect XdgPositionerStableInterface::anchorRect() const { Q_D(); return d->anchorRect; } Qt::Edges XdgPositionerStableInterface::anchorEdge() const { Q_D(); return d->anchorEdge; } Qt::Edges XdgPositionerStableInterface::gravity() const { Q_D(); return d->gravity; } PositionerConstraints XdgPositionerStableInterface::constraintAdjustments() const { Q_D(); return d->constraintAdjustments; } QPoint XdgPositionerStableInterface::anchorOffset() const { Q_D(); return d->anchorOffset; } XdgPositionerStableInterface::Private *XdgPositionerStableInterface::d_func() const { return static_cast(d.data()); } XdgTopLevelStableInterface* XdgSurfaceStableInterface::topLevel() const { Q_D(); return d->m_topLevel.data(); } XdgPopupStableInterface* XdgSurfaceStableInterface::popup() const { Q_D(); return d->m_popup.data(); } XdgSurfaceStableInterface::Private *XdgSurfaceStableInterface::d_func() const { return static_cast(d.data()); } XdgTopLevelStableInterface::XdgTopLevelStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgTopLevelStableInterface::~XdgTopLevelStableInterface() = default; XdgTopLevelStableInterface::Private *XdgTopLevelStableInterface::d_func() const { return static_cast(d.data()); } XdgPopupStableInterface::XdgPopupStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupStableInterface::~XdgPopupStableInterface() = default; XdgPopupStableInterface::Private *XdgPopupStableInterface::d_func() const { return static_cast(d.data()); } } } diff --git a/src/server/xdgshell_stable_interface_p.h b/src/server/xdgshell_stable_interface_p.h index 51c0ea7..e6160ac 100644 --- a/src/server/xdgshell_stable_interface_p.h +++ b/src/server/xdgshell_stable_interface_p.h @@ -1,148 +1,134 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGSHELL_STABLE_INTERFACE_P_H #define KWAYLAND_SERVER_XDGSHELL_STABLE_INTERFACE_P_H #include "global.h" #include "resource.h" #include "xdgshell_interface.h" #include #include namespace KWayland { namespace Server { class Display; class OutputInterface; class SeatInterface; class SurfaceInterface; class XdgTopLevelStableInterface; class XdgPopupStableInterface; class XdgPositionerStableInterface; class XdgSurfaceStableInterface; template class GenericShellSurface; class XdgShellStableInterface : public XdgShellInterface { Q_OBJECT public: virtual ~XdgShellStableInterface(); /** * @returns The XdgTopLevelV6Interface for the @p native resource. **/ XdgTopLevelStableInterface *getSurface(wl_resource *native); //DAVE we want to rename this, as it's bloody confusing. But XdgShellInterface::getSurface exists and expects that //also use a less terrible argument name than native. It's obvious it's native from the type XdgPositionerStableInterface *getPositioner(wl_resource *native); XdgSurfaceStableInterface *realGetSurface(wl_resource *native); Display *display() const; void ping(XdgShellSurfaceInterface * surface); private: explicit XdgShellStableInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; class XdgSurfaceStableInterface : public KWayland::Server::Resource { Q_OBJECT public: virtual ~XdgSurfaceStableInterface(); SurfaceInterface* surface() const; XdgTopLevelStableInterface* topLevel() const; XdgPopupStableInterface *popup() const; private: explicit XdgSurfaceStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellStableInterface; class Private; Private *d_func() const; }; class XdgTopLevelStableInterface : public XdgShellSurfaceInterface { Q_OBJECT public: virtual ~XdgTopLevelStableInterface(); private: explicit XdgTopLevelStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellStableInterface; friend class XdgSurfaceStableInterface; class Private; Private *d_func() const; }; class XdgPopupStableInterface : public XdgShellPopupInterface { Q_OBJECT public: virtual ~XdgPopupStableInterface(); private: explicit XdgPopupStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellStableInterface; friend class XdgSurfaceStableInterface; friend class GenericShellSurface; class Private; Private *d_func() const; }; /* * This is a private internal class that keeps track of sent data * At the time of PopupCreation these values are copied to the popup */ class XdgPositionerStableInterface: public KWayland::Server::Resource { public: QSize initialSize() const; QRect anchorRect() const; Qt::Edges anchorEdge() const; Qt::Edges gravity() const; PositionerConstraints constraintAdjustments() const; QPoint anchorOffset() const; private: explicit XdgPositionerStableInterface(XdgShellStableInterface *parent, wl_resource *parentResource); friend class XdgShellStableInterface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/xdgshell_v5_interface.cpp b/src/server/xdgshell_v5_interface.cpp index 54eaf2b..e8be36e 100644 --- a/src/server/xdgshell_v5_interface.cpp +++ b/src/server/xdgshell_v5_interface.cpp @@ -1,564 +1,550 @@ -/**************************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2019 Vlad Zahorodnii - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2019 Vlad Zahorodnii + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_v5_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include "../compat/wayland-xdg-shell-v5-server-protocol.h" namespace KWayland { namespace Server { class XdgShellV5Interface::Private : public XdgShellInterface::Private { public: Private(XdgShellV5Interface *q, Display *d); QVector surfaces; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } QHash resources; static void destroyCallback(wl_client *client, wl_resource *resource); static void useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellV5Interface *q; static const struct zxdg_shell_v5_interface s_interface; static const quint32 s_version; }; class XdgPopupV5Interface::Private : public XdgShellPopupInterface::Private { public: Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); QRect windowGeometry() const override; void commit() override; void popupDone() override; XdgPopupV5Interface *q_func() { return reinterpret_cast(q); } private: static const struct zxdg_popup_v5_interface s_interface; }; const quint32 XdgShellV5Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zxdg_shell_v5_interface XdgShellV5Interface::Private::s_interface = { destroyCallback, useUnstableVersionCallback, getXdgSurfaceCallback, getXdgPopupCallback, pongCallback }; #endif void XdgShellV5Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) // TODO: send protocol error if there are still surfaces mapped wl_resource_destroy(resource); } void XdgShellV5Interface::Private::useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(version) // TODO: implement } void XdgShellV5Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellV5Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceV5Interface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), ZXDG_SHELL_V5_ERROR_ROLE, "ShellSurface already created"); return; } XdgSurfaceV5Interface *shellSurface = new XdgSurfaceV5Interface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceV5Interface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } void XdgShellV5Interface::Private::getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), SurfaceInterface::get(parent), SeatInterface::get(seat), serial, QPoint(x, y), resource); } void XdgShellV5Interface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource) { XdgPopupV5Interface *popupSurface = new XdgPopupV5Interface(q, surface, parentResource); auto d = popupSurface->d_func(); d->parent = QPointer(parent); d->anchorRect = QRect(pos, QSize(0,0)); //default open like a normal popup d->anchorEdge = Qt::BottomEdge; d->gravity = Qt::TopEdge; d->create(display->getConnection(client), version, id); //compat emit q->popupCreated(popupSurface, seat, serial); //new system emit q->xdgPopupCreated(popupSurface); emit popupSurface->grabRequested(seat, serial); } void XdgShellV5Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellV5Interface::Private::Private(XdgShellV5Interface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, d, &zxdg_shell_v5_interface, s_version) , q(q) { } void XdgShellV5Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&zxdg_shell_v5_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellV5Interface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgSurfaceV5Interface *XdgShellV5Interface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); auto it = std::find_if(d->surfaces.constBegin(), d->surfaces.constEnd(), [resource] (XdgSurfaceV5Interface *surface) { return surface->resource() == resource; } ); if (it != d->surfaces.constEnd()) { return *it; } return nullptr; } quint32 XdgShellV5Interface::Private::ping(XdgShellSurfaceInterface * surface) { auto client = surface->client()->client(); //from here we can get the resource bound to our global. auto clientXdgShellResource = resources.value(client); if (!clientXdgShellResource) { return 0; } const quint32 pingSerial = display->nextSerial(); zxdg_shell_v5_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellV5Interface::Private *XdgShellV5Interface::d_func() const { return reinterpret_cast(d.data()); } class XdgSurfaceV5Interface::Private : public XdgShellSurfaceInterface::Private { public: Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); QRect windowGeometry() const override; QSize minimumSize() const override; QSize maximumSize() const override; void close() override; void commit() override; quint32 configure(States states, const QSize &size) override; XdgSurfaceV5Interface *q_func() { return reinterpret_cast(q); } private: static void setParentCallback(wl_client *client, wl_resource *resource, wl_resource * parent); static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, int32_t x, int32_t y); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource * output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct zxdg_surface_v5_interface s_interface; struct ShellSurfaceState { QRect windowGeometry; bool windowGeometryIsSet = false; }; ShellSurfaceState m_currentState; ShellSurfaceState m_pendingState; }; namespace { template <> Qt::Edges edgesToQtEdges(zxdg_surface_v5_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef K_DOXYGEN const struct zxdg_surface_v5_interface XdgSurfaceV5Interface::Private::s_interface = { resourceDestroyedCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, ackConfigureCallback, setWindowGeometryCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; #endif void XdgSurfaceV5Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } void XdgSurfaceV5Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } void XdgSurfaceV5Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!s->configureSerials.contains(serial)) { // TODO: send error? return; } while (!s->configureSerials.isEmpty()) { quint32 i = s->configureSerials.takeFirst(); emit s->q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void XdgSurfaceV5Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { if (width < 0 || height < 0) { wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); return; } auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_pendingState.windowGeometry = QRect(x, y, width, height); s->m_pendingState.windowGeometryIsSet = true; } void XdgSurfaceV5Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgSurfaceV5Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgSurfaceV5Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgSurfaceV5Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgSurfaceV5Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgSurfaceV5Interface::Private::Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_surface_v5_interface, &s_interface) { } XdgSurfaceV5Interface::Private::~Private() = default; QRect XdgSurfaceV5Interface::Private::windowGeometry() const { return m_currentState.windowGeometry; } QSize XdgSurfaceV5Interface::Private::minimumSize() const { return QSize(0, 0); } QSize XdgSurfaceV5Interface::Private::maximumSize() const { return QSize(INT_MAX, INT_MAX); } void XdgSurfaceV5Interface::Private::close() { zxdg_surface_v5_send_close(resource); client->flush(); } void XdgSurfaceV5Interface::Private::commit() { const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; if (windowGeometryChanged) { m_currentState.windowGeometry = m_pendingState.windowGeometry; } m_pendingState = ShellSurfaceState{}; if (windowGeometryChanged) { emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); } } quint32 XdgSurfaceV5Interface::Private::configure(States states, const QSize &size) { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array state; wl_array_init(&state); if (states.testFlag(State::Maximized)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_ACTIVATED; } configureSerials << serial; zxdg_surface_v5_send_configure(resource, size.width(), size.height(), &state, serial); client->flush(); wl_array_release(&state); return serial; } #ifndef K_DOXYGEN const struct zxdg_popup_v5_interface XdgPopupV5Interface::Private::s_interface = { resourceDestroyedCallback }; #endif XdgPopupV5Interface::Private::Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_popup_v5_interface, &s_interface) { } XdgPopupV5Interface::Private::~Private() = default; QRect XdgPopupV5Interface::Private::windowGeometry() const { return QRect(); } void XdgPopupV5Interface::Private::commit() { } void XdgPopupV5Interface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups zxdg_popup_v5_send_popup_done(resource); client->flush(); } XdgShellV5Interface::XdgShellV5Interface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } XdgShellV5Interface::~XdgShellV5Interface() = default; XdgSurfaceV5Interface::XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgSurfaceV5Interface::~XdgSurfaceV5Interface() = default; XdgPopupV5Interface::XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupV5Interface::~XdgPopupV5Interface() = default; XdgPopupV5Interface::Private *XdgPopupV5Interface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/xdgshell_v5_interface_p.h b/src/server/xdgshell_v5_interface_p.h index 55454da..16f8fce 100644 --- a/src/server/xdgshell_v5_interface_p.h +++ b/src/server/xdgshell_v5_interface_p.h @@ -1,96 +1,82 @@ -/**************************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGSHELL_V5_INTERFACE_P_H #define KWAYLAND_SERVER_XDGSHELL_V5_INTERFACE_P_H #include "global.h" #include "resource.h" #include "xdgshell_interface.h" #include #include namespace KWayland { namespace Server { class Display; class OutputInterface; class SeatInterface; class SurfaceInterface; class XdgPopupV5Interface; class XdgSurfaceV5Interface; template class GenericShellSurface; class XdgShellV5Interface : public XdgShellInterface { Q_OBJECT public: virtual ~XdgShellV5Interface(); /** * @returns The XdgSurfaceV5Interface for the @p native resource. **/ XdgSurfaceV5Interface *getSurface(wl_resource *native); void ping(); private: explicit XdgShellV5Interface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; class XdgSurfaceV5Interface : public XdgShellSurfaceInterface { Q_OBJECT public: virtual ~XdgSurfaceV5Interface(); private: explicit XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellV5Interface; class Private; }; class XdgPopupV5Interface : public XdgShellPopupInterface { Q_OBJECT public: virtual ~XdgPopupV5Interface(); private: explicit XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellV5Interface; friend class GenericShellSurface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/server/xdgshell_v6_interface.cpp b/src/server/xdgshell_v6_interface.cpp index 310360d..bf5c255 100644 --- a/src/server/xdgshell_v6_interface.cpp +++ b/src/server/xdgshell_v6_interface.cpp @@ -1,1106 +1,1092 @@ -/**************************************************************************** -Copyright 2017 David Edmundson -Copyright 2019 Vlad Zahorodnii - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + SPDX-FileCopyrightText: 2019 Vlad Zahorodnii + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "xdgshell_v6_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "global.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class XdgShellV6Interface::Private : public XdgShellInterface::Private { public: Private(XdgShellV6Interface *q, Display *d); QVector surfaces; QVector positioners; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellV6Interface *q; static const struct zxdg_shell_v6_interface s_interface; static const quint32 s_version; QHash resources; }; class XdgPopupV6Interface::Private : public XdgShellPopupInterface::Private { public: Private(XdgPopupV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); QRect windowGeometry() const override; void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { // TODO: send error? return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void popupDone() override; quint32 configure(const QRect &rect) override; XdgPopupV6Interface *q_func() { return reinterpret_cast(q); } private: void setWindowGeometryCallback(const QRect &rect); static void grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); static const struct zxdg_popup_v6_interface s_interface; struct ShellSurfaceState { QRect windowGeometry; bool windowGeometryIsSet = false; }; ShellSurfaceState m_currentState; ShellSurfaceState m_pendingState; friend class XdgSurfaceV6Interface; }; class XdgSurfaceV6Interface::Private : public KWayland::Server::Resource::Private { public: Private(XdgSurfaceV6Interface* q, XdgShellV6Interface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private(); XdgSurfaceV6Interface *q_func() { return reinterpret_cast(q); } void createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentWindow, wl_resource *positioner); XdgShellV6Interface *m_shell; SurfaceInterface *m_surface; //effectively a union, only one of these should be populated. //a surface cannot have two roles QPointer m_topLevel; QPointer m_popup; private: static void destroyCallback(wl_client *client, wl_resource *resource); static void getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct zxdg_surface_v6_interface s_interface; }; class XdgTopLevelV6Interface::Private : public XdgShellSurfaceInterface::Private { public: Private(XdgTopLevelV6Interface* q, XdgShellV6Interface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private(); QRect windowGeometry() const override; QSize minimumSize() const override; QSize maximumSize() const override; void close() override; void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { // TODO: send error? return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } quint32 configure(States states, const QSize &size) override { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array state; wl_array_init(&state); if (states.testFlag(State::Maximized)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; } configureSerials << serial; zxdg_toplevel_v6_send_configure(resource, size.width(), size.height(), &state); zxdg_surface_v6_send_configure(parentResource, serial); client->flush(); wl_array_release(&state); return serial; }; XdgTopLevelV6Interface *q_func() { return reinterpret_cast(q); } private: void setWindowGeometryCallback(const QRect &rect); static void destroyCallback(wl_client *client, wl_resource *resource); static void setParentCallback(struct wl_client *client, struct wl_resource *resource, wl_resource *parent); static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y); static void setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct zxdg_toplevel_v6_interface s_interface; struct ShellSurfaceState { QRect windowGeometry; QSize minimumSize = QSize(0, 0); QSize maximiumSize = QSize(INT32_MAX, INT32_MAX); bool windowGeometryIsSet = false; bool minimumSizeIsSet = false; bool maximumSizeIsSet = false; }; ShellSurfaceState m_currentState; ShellSurfaceState m_pendingState; friend class XdgSurfaceV6Interface; }; const quint32 XdgShellV6Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct zxdg_shell_v6_interface XdgShellV6Interface::Private::s_interface = { destroyCallback, createPositionerCallback, getXdgSurfaceCallback, pongCallback }; #endif void XdgShellV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) // TODO: send protocol error if there are still surfaces mapped wl_resource_destroy(resource); } void XdgShellV6Interface::Private::createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createPositioner(client, wl_resource_get_version(resource), id, resource); } void XdgShellV6Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellV6Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceV6Interface *s) { return false; return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), ZXDG_SHELL_V6_ERROR_ROLE, "ShellSurface already created"); return; } XdgSurfaceV6Interface *shellSurface = new XdgSurfaceV6Interface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceV6Interface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); } void XdgShellV6Interface::Private::createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { Q_UNUSED(client) XdgPositionerV6Interface *positioner = new XdgPositionerV6Interface(q, parentResource); positioners << positioner; QObject::connect(positioner, &Resource::destroyed, q, [this, positioner] { positioners.removeAll(positioner); } ); positioner->d->create(display->getConnection(client), version, id); } void XdgShellV6Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellV6Interface::Private::Private(XdgShellV6Interface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, d, &zxdg_shell_v6_interface, 1) , q(q) { } void XdgShellV6Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&zxdg_shell_v6_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellV6Interface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgTopLevelV6Interface *XdgShellV6Interface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { auto topLevel = (*it)->topLevel(); if (topLevel && topLevel->resource() == resource) { return topLevel; } } return nullptr; } XdgSurfaceV6Interface *XdgShellV6Interface::realGetSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { if ((*it)->resource() == resource) { return (*it); } } return nullptr; } XdgPositionerV6Interface *XdgShellV6Interface::getPositioner(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->positioners.constBegin(); it != d->positioners.constEnd() ; it++) { if ((*it)->resource() == resource) { return *it; } } return nullptr; } quint32 XdgShellV6Interface::Private::ping(XdgShellSurfaceInterface *surface) { auto client = surface->client()->client(); //from here we can get the resource bound to our global. auto clientXdgShellResource = resources.value(client); if (!clientXdgShellResource) { return 0; } const quint32 pingSerial = display->nextSerial(); zxdg_shell_v6_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellV6Interface::Private *XdgShellV6Interface::d_func() const { return reinterpret_cast(d.data()); } namespace { template <> Qt::Edges edgesToQtEdges(zxdg_toplevel_v6_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef K_DOXYGEN const struct zxdg_surface_v6_interface XdgSurfaceV6Interface::Private::s_interface = { destroyCallback, getTopLevelCallback, getPopupCallback, setWindowGeometryCallback, ackConfigureCallback }; #endif void XdgSurfaceV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) //FIXME check if we have attached toplevels first and throw an error wl_resource_destroy(resource); } void XdgSurfaceV6Interface::Private::getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createTopLevel(client, wl_resource_get_version(resource), id, resource); } void XdgSurfaceV6Interface::Private::createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { // FIXME: That's incorrect! The client may have asked us to create an xdg-toplevel // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Toplevel already created on this surface"); return; } if (m_popup) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Popup already created on this surface"); return; } m_topLevel = new XdgTopLevelV6Interface (m_shell, m_surface, parentResource); m_topLevel->d->create(m_shell->display()->getConnection(client), version, id); emit m_shell->surfaceCreated(m_topLevel); } void XdgSurfaceV6Interface::Private::getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, resource, parent, positioner); } void XdgSurfaceV6Interface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentSurface, wl_resource *positioner) { // FIXME: That's incorrect! The client may have asked us to create an xdg-popup // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Toplevel already created on this surface"); return; } if (m_popup) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Popup already created on this surface"); return; } auto xdgPositioner = m_shell->getPositioner(positioner); if (!xdgPositioner) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, "Invalid positioner"); return; } m_popup = new XdgPopupV6Interface(m_shell, m_surface, parentResource); auto pd = m_popup->d_func(); pd->create(m_shell->display()->getConnection(client), version, id); auto parentXdgSurface = m_shell->realGetSurface(parentSurface); if (parentXdgSurface) { pd->parent = parentXdgSurface->surface(); } else { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent"); return; } pd->initialSize = xdgPositioner->initialSize(); pd->anchorRect = xdgPositioner->anchorRect(); pd->anchorEdge = xdgPositioner->anchorEdge(); pd->gravity = xdgPositioner->gravity(); pd->constraintAdjustments = xdgPositioner->constraintAdjustments(); pd->anchorOffset = xdgPositioner->anchorOffset(); emit m_shell->xdgPopupCreated(m_popup.data()); } void XdgSurfaceV6Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_topLevel) { s->m_topLevel->d_func()->ackConfigure(serial); } else if (s->m_popup) { s->m_popup->d_func()->ackConfigure(serial); } } void XdgSurfaceV6Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (width < 0 || height < 0) { wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-surface geometry"); return; } if (s->m_topLevel) { s->m_topLevel->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } else if (s->m_popup) { s->m_popup->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } } XdgSurfaceV6Interface::Private::Private(XdgSurfaceV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &zxdg_surface_v6_interface, &s_interface), m_shell(c), m_surface(surface) { } XdgSurfaceV6Interface::Private::~Private() = default; class XdgPositionerV6Interface::Private : public KWayland::Server::Resource::Private { public: Private(XdgPositionerV6Interface *q, XdgShellV6Interface *c, wl_resource* parentResource); QSize initialSize; QRect anchorRect; Qt::Edges anchorEdge; Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; private: static void setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor); static void setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity); static void setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment); static void setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static const struct zxdg_positioner_v6_interface s_interface; }; XdgPositionerV6Interface::Private::Private(XdgPositionerV6Interface *q, XdgShellV6Interface *c, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &zxdg_positioner_v6_interface, &s_interface) { } #ifndef K_DOXYGEN const struct zxdg_positioner_v6_interface XdgPositionerV6Interface::Private::s_interface = { resourceDestroyedCallback, setSizeCallback, setAnchorRectCallback, setAnchorCallback, setGravityCallback, setConstraintAdjustmentCallback, setOffsetCallback }; #endif void XdgPositionerV6Interface::Private::setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->initialSize = QSize(width, height); } void XdgPositionerV6Interface::Private::setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->anchorRect = QRect(x, y, width, height); } void XdgPositionerV6Interface::Private::setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor) { Q_UNUSED(client) auto s = cast(resource); //Note - see David E's email to wayland-devel about this being bad API if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) && (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } Qt::Edges edges; if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { edges |= Qt::LeftEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { edges |= Qt::TopEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { edges |= Qt::RightEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { edges |= Qt::BottomEdge; } s->anchorEdge = edges; } void XdgPositionerV6Interface::Private::setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity) { Q_UNUSED(client) auto s = cast(resource); if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } Qt::Edges edges; if (gravity & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { edges |= Qt::LeftEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_TOP) { edges |= Qt::TopEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { edges |= Qt::RightEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { edges |= Qt::BottomEdge; } s->gravity = edges; } void XdgPositionerV6Interface::Private::setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment) { Q_UNUSED(client) auto s = cast(resource); PositionerConstraints constraints; if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X) { constraints |= PositionerConstraint::SlideX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { constraints |= PositionerConstraint::SlideY; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) { constraints |= PositionerConstraint::FlipX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) { constraints |= PositionerConstraint::FlipY; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X) { constraints |= PositionerConstraint::ResizeX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { constraints |= PositionerConstraint::ResizeY; } s->constraintAdjustments = constraints; } void XdgPositionerV6Interface::Private::setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { Q_UNUSED(client) auto s = cast(resource); s->anchorOffset = QPoint(x,y); } QRect XdgTopLevelV6Interface::Private::windowGeometry() const { return m_currentState.windowGeometry; } QSize XdgTopLevelV6Interface::Private::minimumSize() const { return m_currentState.minimumSize; } QSize XdgTopLevelV6Interface::Private::maximumSize() const { return m_currentState.maximiumSize; } void XdgTopLevelV6Interface::Private::close() { zxdg_toplevel_v6_send_close(resource); client->flush(); } void XdgTopLevelV6Interface::Private::setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { if (width < 0 || height < 0) { wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel maximum size"); return; } if (width == 0) { width = INT32_MAX; } if (height == 0) { height = INT32_MAX; } auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_pendingState.maximiumSize = QSize(width, height); s->m_pendingState.maximumSizeIsSet = true; } void XdgTopLevelV6Interface::Private::setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { if (width < 0 || height < 0) { wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel minimum size"); return; } auto s = cast(resource); Q_ASSERT(client == *s->client); s->m_pendingState.minimumSize = QSize(width, height); s->m_pendingState.minimumSizeIsSet = true; } void XdgTopLevelV6Interface::Private::setWindowGeometryCallback(const QRect &rect) { m_pendingState.windowGeometry = rect; m_pendingState.windowGeometryIsSet = true; } const struct zxdg_toplevel_v6_interface XdgTopLevelV6Interface::Private::s_interface = { destroyCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, setMaxSizeCallback, setMinSizeCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; void XdgTopLevelV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgTopLevelV6Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!parent) { //setting null is valid API. Clear s->parent = nullptr; emit s->q_func()->transientForChanged(); } else { auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } } void XdgTopLevelV6Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } XdgTopLevelV6Interface::Private::Private(XdgTopLevelV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, c, surface, parentResource, &zxdg_toplevel_v6_interface, &s_interface) { } void XdgTopLevelV6Interface::Private::commit() { const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; const bool minimumSizeChanged = m_pendingState.minimumSizeIsSet; const bool maximumSizeChanged = m_pendingState.maximumSizeIsSet; if (windowGeometryChanged) { m_currentState.windowGeometry = m_pendingState.windowGeometry; } if (minimumSizeChanged) { m_currentState.minimumSize = m_pendingState.minimumSize; } if (maximumSizeChanged) { m_currentState.maximiumSize = m_pendingState.maximiumSize; } m_pendingState = ShellSurfaceState{}; if (windowGeometryChanged) { emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); } if (minimumSizeChanged) { emit q_func()->minSizeChanged(m_currentState.minimumSize); } if (maximumSizeChanged) { emit q_func()->maxSizeChanged(m_currentState.maximiumSize); } } void XdgTopLevelV6Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgTopLevelV6Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgTopLevelV6Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgTopLevelV6Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgTopLevelV6Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgTopLevelV6Interface::Private::~Private() = default; #ifndef K_DOXYGEN const struct zxdg_popup_v6_interface XdgPopupV6Interface::Private::s_interface = { resourceDestroyedCallback, grabCallback }; #endif XdgPopupV6Interface::Private::Private(XdgPopupV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, c, surface, parentResource, &zxdg_popup_v6_interface, &s_interface) { } void XdgPopupV6Interface::Private::grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto seatInterface = SeatInterface::get(seat); s->q_func()->grabRequested(seatInterface, serial); } XdgPopupV6Interface::Private::~Private() = default; QRect XdgPopupV6Interface::Private::windowGeometry() const { return m_currentState.windowGeometry; } void XdgPopupV6Interface::Private::commit() { const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; if (windowGeometryChanged) { m_currentState.windowGeometry = m_pendingState.windowGeometry; } m_pendingState = ShellSurfaceState{}; if (windowGeometryChanged) { emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); } } void XdgPopupV6Interface::Private::setWindowGeometryCallback(const QRect &rect) { m_pendingState.windowGeometry = rect; m_pendingState.windowGeometryIsSet = true; } quint32 XdgPopupV6Interface::Private::configure(const QRect &rect) { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); configureSerials << serial; zxdg_popup_v6_send_configure(resource, rect.x(), rect.y(), rect.width(), rect.height()); zxdg_surface_v6_send_configure(parentResource, serial); client->flush(); return serial; } void XdgPopupV6Interface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups zxdg_popup_v6_send_popup_done(resource); client->flush(); } XdgShellV6Interface::XdgShellV6Interface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } Display* XdgShellV6Interface::display() const { return d->display; } XdgShellV6Interface::~XdgShellV6Interface() = default; XdgSurfaceV6Interface::XdgSurfaceV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, surface, parentResource)) { } XdgSurfaceV6Interface::~XdgSurfaceV6Interface() = default; SurfaceInterface* XdgSurfaceV6Interface::surface() const { Q_D(); return d->m_surface; } XdgPositionerV6Interface::XdgPositionerV6Interface(XdgShellV6Interface *parent, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, parentResource)) { } QSize XdgPositionerV6Interface::initialSize() const { Q_D(); return d->initialSize; } QRect XdgPositionerV6Interface::anchorRect() const { Q_D(); return d->anchorRect; } Qt::Edges XdgPositionerV6Interface::anchorEdge() const { Q_D(); return d->anchorEdge; } Qt::Edges XdgPositionerV6Interface::gravity() const { Q_D(); return d->gravity; } PositionerConstraints XdgPositionerV6Interface::constraintAdjustments() const { Q_D(); return d->constraintAdjustments; } QPoint XdgPositionerV6Interface::anchorOffset() const { Q_D(); return d->anchorOffset; } XdgPositionerV6Interface::Private *XdgPositionerV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgTopLevelV6Interface* XdgSurfaceV6Interface::topLevel() const { Q_D(); return d->m_topLevel.data(); } XdgPopupV6Interface* XdgSurfaceV6Interface::popup() const { Q_D(); return d->m_popup.data(); } XdgSurfaceV6Interface::Private *XdgSurfaceV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgTopLevelV6Interface::XdgTopLevelV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgTopLevelV6Interface::~XdgTopLevelV6Interface() = default; XdgTopLevelV6Interface::Private *XdgTopLevelV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgPopupV6Interface::XdgPopupV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupV6Interface::~XdgPopupV6Interface() = default; XdgPopupV6Interface::Private *XdgPopupV6Interface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/xdgshell_v6_interface_p.h b/src/server/xdgshell_v6_interface_p.h index 0d818c3..86a3126 100644 --- a/src/server/xdgshell_v6_interface_p.h +++ b/src/server/xdgshell_v6_interface_p.h @@ -1,148 +1,134 @@ -/**************************************************************************** -Copyright 2017 David Edmundson - -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) 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 6 of version 3 of the license. - -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, see . -****************************************************************************/ +/* + SPDX-FileCopyrightText: 2017 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_SERVER_XDGSHELL_V6_INTERFACE_P_H #define KWAYLAND_SERVER_XDGSHELL_V6_INTERFACE_P_H #include "global.h" #include "resource.h" #include "xdgshell_interface.h" #include #include namespace KWayland { namespace Server { class Display; class OutputInterface; class SeatInterface; class SurfaceInterface; class XdgTopLevelV6Interface; class XdgPopupV6Interface; class XdgPositionerV6Interface; class XdgSurfaceV6Interface; template class GenericShellSurface; class XdgShellV6Interface : public XdgShellInterface { Q_OBJECT public: virtual ~XdgShellV6Interface(); /** * @returns The XdgTopLevelV6Interface for the @p native resource. **/ XdgTopLevelV6Interface *getSurface(wl_resource *native); //DAVE we want to rename this, as it's bloody confusing. But XdgShellInterface::getSurface exists and expects that //also use a less terrible argument name than native. It's obvious it's native from the type XdgPositionerV6Interface *getPositioner(wl_resource *native); XdgSurfaceV6Interface *realGetSurface(wl_resource *native); Display *display() const; void ping(XdgShellSurfaceInterface * surface); private: explicit XdgShellV6Interface(Display *display, QObject *parent = nullptr); friend class Display; class Private; Private *d_func() const; }; class XdgSurfaceV6Interface : public KWayland::Server::Resource { Q_OBJECT public: virtual ~XdgSurfaceV6Interface(); SurfaceInterface* surface() const; XdgTopLevelV6Interface* topLevel() const; XdgPopupV6Interface *popup() const; private: explicit XdgSurfaceV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellV6Interface; class Private; Private *d_func() const; }; class XdgTopLevelV6Interface : public XdgShellSurfaceInterface { Q_OBJECT public: virtual ~XdgTopLevelV6Interface(); private: explicit XdgTopLevelV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellV6Interface; friend class XdgSurfaceV6Interface; class Private; Private *d_func() const; }; class XdgPopupV6Interface : public XdgShellPopupInterface { Q_OBJECT public: virtual ~XdgPopupV6Interface(); private: explicit XdgPopupV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource); friend class XdgShellV6Interface; friend class XdgSurfaceV6Interface; friend class GenericShellSurface; class Private; Private *d_func() const; }; /* * This is a private internal class that keeps track of sent data * At the time of PopupCreation these values are copied to the popup */ class XdgPositionerV6Interface: public KWayland::Server::Resource { public: QSize initialSize() const; QRect anchorRect() const; Qt::Edges anchorEdge() const; Qt::Edges gravity() const; PositionerConstraints constraintAdjustments() const; QPoint anchorOffset() const; private: explicit XdgPositionerV6Interface(XdgShellV6Interface *parent, wl_resource *parentResource); friend class XdgShellV6Interface; class Private; Private *d_func() const; }; } } #endif diff --git a/src/tools/generator.cpp b/src/tools/generator.cpp index 764096f..f20491f 100644 --- a/src/tools/generator.cpp +++ b/src/tools/generator.cpp @@ -1,1587 +1,1573 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "generator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWayland { namespace Tools { static QMap s_clientClassNameMapping; static QString toQtInterfaceName(const QString &wlInterface) { auto it = s_clientClassNameMapping.constFind(wlInterface); if (it != s_clientClassNameMapping.constEnd()) { return it.value(); } else { qWarning() << "Cannot find mapping for " << wlInterface; } return wlInterface; } static QString toCamelCase(const QString &underscoreName) { const QStringList parts = underscoreName.split(QStringLiteral("_")); if (parts.count() < 2) { return underscoreName; } auto it = parts.constBegin(); QString camelCase = (*it); it++; for (; it != parts.constEnd(); ++it) { camelCase.append((*it).left(1).toUpper()); camelCase.append((*it).mid(1)); } return camelCase; } Argument::Argument() { } Argument::Argument(const QXmlStreamAttributes &attributes) : m_name(attributes.value(QStringLiteral("name")).toString()) , m_type(parseType(attributes.value(QStringLiteral("type")))) , m_allowNull(attributes.hasAttribute(QStringLiteral("allow-null"))) , m_inteface(attributes.value(QStringLiteral("interface")).toString()) { } Argument::~Argument() = default; Argument::Type Argument::parseType(const QStringRef &type) { if (type.compare(QLatin1String("new_id")) == 0) { return Type::NewId; } if (type.compare(QLatin1String("destructor")) == 0) { return Type::Destructor; } if (type.compare(QLatin1String("object")) == 0) { return Type::Object; } if (type.compare(QLatin1String("fd")) == 0) { return Type::FileDescriptor; } if (type.compare(QLatin1String("fixed")) == 0) { return Type::Fixed; } if (type.compare(QLatin1String("uint")) == 0) { return Type::Uint; } if (type.compare(QLatin1String("int")) == 0) { return Type::Int; } if (type.compare(QLatin1String("string")) == 0) { return Type::String; } return Type::Unknown; } QString Argument::typeAsQt() const { switch (m_type) { case Type::Destructor: return QString(); case Type::FileDescriptor: return QStringLiteral("int"); case Type::Fixed: return QStringLiteral("qreal"); case Type::Int: return QStringLiteral("qint32"); case Type::NewId: case Type::Object: return toQtInterfaceName(m_inteface); case Type::String: return QStringLiteral("const QString &"); case Type::Uint: return QStringLiteral("quint32"); case Type::Unknown: return QString(); default: Q_UNREACHABLE(); } } QString Argument::typeAsServerWl() const { switch (m_type) { case Type::Destructor: return QString(); case Type::FileDescriptor: return QStringLiteral("int32_t"); case Type::Fixed: return QStringLiteral("wl_fixed"); case Type::Int: return QStringLiteral("int32_t"); case Type::Object: return QStringLiteral("wl_resource *"); case Type::String: return QStringLiteral("const char *"); case Type::Uint: case Type::NewId: return QStringLiteral("uint32_t"); case Type::Unknown: return QString(); default: Q_UNREACHABLE(); } } Request::Request() { } Request::Request(const QString &name) : m_name(name) { } Request::~Request() = default; bool Request::isFactory() const { for (const auto a: m_arguments) { if (a.type() == Argument::Type::NewId) { return true; } } return false; } Event::Event() { } Event::Event(const QString &name) : m_name(name) { } Event::~Event() = default; Interface::Interface() = default; Interface::Interface(const QXmlStreamAttributes &attributes) : m_name(attributes.value(QStringLiteral("name")).toString()) , m_version(attributes.value(QStringLiteral("version")).toUInt()) , m_factory(nullptr) { auto it = s_clientClassNameMapping.constFind(m_name); if (it != s_clientClassNameMapping.constEnd()) { m_clientName = it.value(); } else { qWarning() << "Failed to map " << m_name << " to a KWayland name"; } } Interface::~Interface() = default; Generator::Generator(QObject *parent) : QObject(parent) { } Generator::~Generator() = default; void Generator::start() { startAuthorNameProcess(); startAuthorEmailProcess(); startParseXml(); startGenerateHeaderFile(); startGenerateCppFile(); startGenerateServerHeaderFile(); startGenerateServerCppFile(); } void Generator::startParseXml() { if (m_xmlFileName.isEmpty()) { return; } QFile xmlFile(m_xmlFileName); xmlFile.open(QIODevice::ReadOnly); m_xmlReader.setDevice(&xmlFile); while (!m_xmlReader.atEnd()) { if (!m_xmlReader.readNextStartElement()) { continue; } if (m_xmlReader.qualifiedName().compare(QLatin1String("protocol")) == 0) { parseProtocol(); } } auto findFactory = [this] (const QString interfaceName) -> Interface* { for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) { if ((*it).name().compare(interfaceName) == 0) { continue; } for (auto r: (*it).requests()) { for (auto a: r.arguments()) { if (a.type() == Argument::Type::NewId && a.interface().compare(interfaceName) == 0) { return &(*it); } } } } return nullptr; }; for (auto it = m_interfaces.begin(); it != m_interfaces.end(); ++it) { Interface *factory = findFactory((*it).name()); if (factory) { qDebug() << (*it).name() << "gets factored by" << factory->kwaylandClientName(); (*it).setFactory(factory); } else { qDebug() << (*it).name() << "considered as a global"; (*it).markAsGlobal(); } } } void Generator::parseProtocol() { const auto attributes = m_xmlReader.attributes(); const QString protocolName = attributes.value(QStringLiteral("name")).toString(); if (m_baseFileName.isEmpty()) { m_baseFileName = protocolName.toLower(); } while (!m_xmlReader.atEnd()) { if (!m_xmlReader.readNextStartElement()) { if (m_xmlReader.qualifiedName().compare(QLatin1String("protocol")) == 0) { return; } continue; } if (m_xmlReader.qualifiedName().compare(QLatin1String("interface")) == 0) { m_interfaces << parseInterface(); } } } Interface Generator::parseInterface() { Interface interface(m_xmlReader.attributes()); while (!m_xmlReader.atEnd()) { if (!m_xmlReader.readNextStartElement()) { if (m_xmlReader.qualifiedName().compare(QLatin1String("interface")) == 0) { break; } continue; } if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) { interface.addRequest(parseRequest()); } if (m_xmlReader.qualifiedName().compare(QLatin1String("event")) == 0) { interface.addEvent(parseEvent()); } } return interface; } Request Generator::parseRequest() { const auto attributes = m_xmlReader.attributes(); Request request(attributes.value(QStringLiteral("name")).toString()); if (attributes.value(QStringLiteral("type")).toString().compare(QLatin1String("destructor")) == 0) { request.markAsDestructor(); } while (!m_xmlReader.atEnd()) { if (!m_xmlReader.readNextStartElement()) { if (m_xmlReader.qualifiedName().compare(QLatin1String("request")) == 0) { break; } continue; } if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) { request.addArgument(Argument(m_xmlReader.attributes())); } } return request; } Event Generator::parseEvent() { const auto attributes = m_xmlReader.attributes(); Event event(attributes.value(QStringLiteral("name")).toString()); while (!m_xmlReader.atEnd()) { if (!m_xmlReader.readNextStartElement()) { if (m_xmlReader.qualifiedName().compare(QLatin1String("event")) == 0) { break; } continue; } if (m_xmlReader.qualifiedName().compare(QLatin1String("arg")) == 0) { event.addArgument(Argument(m_xmlReader.attributes())); } } return event; } void Generator::startGenerateHeaderFile() { QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcher::finished, this, &Generator::checkEnd); m_finishedCounter++; watcher->setFuture(QtConcurrent::run([this] { QFile file(QStringLiteral("%1.h").arg(m_baseFileName)); file.open(QIODevice::WriteOnly); m_stream.setLocalData(new QTextStream(&file)); m_project.setLocalData(Project::Client); generateCopyrightHeader(); generateStartIncludeGuard(); generateHeaderIncludes(); generateWaylandForwardDeclarations(); generateStartNamespace(); generateNamespaceForwardDeclarations(); for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { generateClass(*it); } generateEndNamespace(); generateEndIncludeGuard(); m_stream.setLocalData(nullptr); file.close(); })); } void Generator::startGenerateCppFile() { QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcher::finished, this, &Generator::checkEnd); m_finishedCounter++; watcher->setFuture(QtConcurrent::run([this] { QFile file(QStringLiteral("%1.cpp").arg(m_baseFileName)); file.open(QIODevice::WriteOnly); m_stream.setLocalData(new QTextStream(&file)); m_project.setLocalData(Project::Client); generateCopyrightHeader(); generateCppIncludes(); generateStartNamespace(); for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { generatePrivateClass(*it); generateClientCpp(*it); generateClientCppRequests(*it); } generateEndNamespace(); m_stream.setLocalData(nullptr); file.close(); })); } void Generator::startGenerateServerHeaderFile() { QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcher::finished, this, &Generator::checkEnd); m_finishedCounter++; watcher->setFuture(QtConcurrent::run([this] { QFile file(QStringLiteral("%1_interface.h").arg(m_baseFileName)); file.open(QIODevice::WriteOnly); m_stream.setLocalData(new QTextStream(&file)); m_project.setLocalData(Project::Server); generateCopyrightHeader(); generateStartIncludeGuard(); generateHeaderIncludes(); generateStartNamespace(); generateNamespaceForwardDeclarations(); if (std::any_of(m_interfaces.constBegin(), m_interfaces.constEnd(), [] (const Interface &i) { return i.isUnstableInterface(); })) { // generate the unstable semantic version auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [] (const Interface &i) { return i.isGlobal(); }); if (it != m_interfaces.constEnd()) { const QString templateString = QStringLiteral( "/**\n" " * Enum describing the interface versions the %1 can support.\n" " *\n" " * @since 5.XX\n" " **/\n" "enum class %1Version {\n" " /**\n" " * %2\n" " **/\n" " UnstableV%3\n" "};\n\n"); *m_stream.localData() << templateString.arg((*it).kwaylandServerName()) .arg((*it).name()) .arg((*it).name().mid((*it).name().lastIndexOf(QStringLiteral("_v")) + 2)); } } for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { generateClass(*it); } generateEndNamespace(); generateEndIncludeGuard(); m_stream.setLocalData(nullptr); file.close(); })); } void Generator::startGenerateServerCppFile() { QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcher::finished, this, &Generator::checkEnd); m_finishedCounter++; watcher->setFuture(QtConcurrent::run([this] { QFile file(QStringLiteral("%1_interface.cpp").arg(m_baseFileName)); file.open(QIODevice::WriteOnly); m_stream.setLocalData(new QTextStream(&file)); m_project.setLocalData(Project::Server); generateCopyrightHeader(); generateCppIncludes(); generateStartNamespace(); for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { generatePrivateClass(*it); // generateClientCpp(*it); // generateClientCppRequests(*it); } generateEndNamespace(); m_stream.setLocalData(nullptr); file.close(); })); } void Generator::checkEnd() { m_finishedCounter--; if (m_finishedCounter == 0) { QCoreApplication::quit(); } } void Generator::startAuthorNameProcess() { QProcess *git = new QProcess(this); git->setArguments(QStringList{ QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.name") }); git->setProgram(QStringLiteral("git")); connect(git, static_cast(&QProcess::finished), this, [this, git] { QMutexLocker locker(&m_mutex); m_authorName = QString::fromLocal8Bit(git->readAllStandardOutput()).trimmed(); git->deleteLater(); m_waitCondition.wakeAll(); } ); git->start(); } void Generator::startAuthorEmailProcess() { QProcess *git = new QProcess(this); git->setArguments(QStringList{ QStringLiteral("config"), QStringLiteral("--get"), QStringLiteral("user.email") }); git->setProgram(QStringLiteral("git")); connect(git, static_cast(&QProcess::finished), this, [this, git] { QMutexLocker locker(&m_mutex); m_authorEmail = QString::fromLocal8Bit(git->readAllStandardOutput()).trimmed(); git->deleteLater(); m_waitCondition.wakeAll(); } ); git->start(); } void Generator::generateCopyrightHeader() { m_mutex.lock(); while (m_authorEmail.isEmpty() || m_authorName.isEmpty()) { m_waitCondition.wait(&m_mutex); } m_mutex.unlock(); const QString templateString = QStringLiteral( "/****************************************************************************\n" "Copyright %1 %2 <%3>\n" "\n" "This library is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU Lesser General Public\n" "License as published by the Free Software Foundation; either\n" "version 2.1 of the License, or (at your option) version 3, or any\n" "later version accepted by the membership of KDE e.V. (or its\n" "successor approved by the membership of KDE e.V.), which shall\n" "act as a proxy defined in Section 6 of version 3 of the license.\n" "\n" "This library is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" "Lesser General Public License for more details.\n" "\n" "You should have received a copy of the GNU Lesser General Public\n" "License along with this library. If not, see .\n" "****************************************************************************/\n" ); QDate date = QDate::currentDate(); *m_stream.localData() << templateString.arg(date.year()).arg(m_authorName).arg(m_authorEmail); } void Generator::generateEndIncludeGuard() { *m_stream.localData() << QStringLiteral("#endif\n"); } void Generator::generateStartIncludeGuard() { const QString templateString = QStringLiteral( "#ifndef KWAYLAND_%1_%2_H\n" "#define KWAYLAND_%1_%2_H\n\n"); *m_stream.localData() << templateString.arg(projectToName().toUpper()).arg(m_baseFileName.toUpper()); } void Generator::generateStartNamespace() { const QString templateString = QStringLiteral( "namespace KWayland\n" "{\n" "namespace %1\n" "{\n\n"); *m_stream.localData() << templateString.arg(projectToName()); } void Generator::generateEndNamespace() { *m_stream.localData() << QStringLiteral("\n}\n}\n\n"); } void Generator::generateHeaderIncludes() { switch (m_project.localData()) { case Project::Client: *m_stream.localData() << QStringLiteral("#include \n\n"); break; case Project::Server: *m_stream.localData() << QStringLiteral( "#include \"global.h\"\n" "#include \"resource.h\"\n\n"); break; default: Q_UNREACHABLE(); } *m_stream.localData() << QStringLiteral("#include \n\n").arg(projectToName()).arg(projectToName().toLower()); } void Generator::generateCppIncludes() { switch (m_project.localData()) { case Project::Client: *m_stream.localData() << QStringLiteral("#include \"%1.h\"\n").arg(m_baseFileName.toLower()); *m_stream.localData() << QStringLiteral("#include \"event_queue.h\"\n"); *m_stream.localData() << QStringLiteral("#include \"wayland_pointer_p.h\"\n\n"); break; case Project::Server: *m_stream.localData() << QStringLiteral("#include \"%1_interface.h\"\n").arg(m_baseFileName.toLower()); *m_stream.localData() << QStringLiteral( "#include \"display.h\"\n" "#include \"global_p.h\"\n" "#include \"resource_p.h\"\n\n"); break; default: Q_UNREACHABLE(); } } void Generator::generateClass(const Interface &interface) { switch (m_project.localData()) { case Project::Client: if (interface.isGlobal()) { generateClientGlobalClass(interface); } else { generateClientResourceClass(interface); } break; case Project::Server: if (interface.isGlobal()) { generateServerGlobalClass(interface); } else { generateServerResourceClass(interface); } break; default: Q_UNREACHABLE(); } } void Generator::generateClientGlobalClass(const Interface &interface) { generateClientGlobalClassDoxy(interface); generateClientClassQObjectDerived(interface); generateClientGlobalClassCtor(interface); generateClientClassDtor(interface); generateClientGlobalClassSetup(interface); generateClientClassReleaseDestroy(interface); generateClientClassStart(interface); generateClientClassRequests(interface); generateClientClassCasts(interface); generateClientClassSignals(interface); generateClientGlobalClassEnd(interface); } void Generator::generateClientResourceClass(const Interface &interface) { generateClientClassQObjectDerived(interface); generateClientClassDtor(interface); generateClientResourceClassSetup(interface); generateClientClassReleaseDestroy(interface); generateClientClassRequests(interface); generateClientClassCasts(interface); generateClientResourceClassEnd(interface); } void Generator::generateServerGlobalClass(const Interface &interface) { if (interface.isUnstableInterface()) { generateServerGlobalClassUnstable(interface); return; } const QString templateString = QStringLiteral( "class KWAYLANDSERVER_EXPORT %1 : public Global\n" "{\n" " Q_OBJECT\n" "public:\n" " virtual ~%1();\n" "\n" "private:\n" " explicit %1(Display *display, QObject *parent = nullptr);\n" " friend class Display;\n" " class Private;\n" "};\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()); } void Generator::generateServerGlobalClassUnstable(const Interface &interface) { const QString templateString = QStringLiteral( "class KWAYLANDSERVER_EXPORT %1 : public Global\n" "{\n" " Q_OBJECT\n" "public:\n" " virtual ~%1();\n" "\n" " /**\n" " * @returns The interface version used by this %1\n" " **/\n" " %1Version interfaceVersion() const;\n" "\n" "protected:\n" " class Private;\n" " explicit %1(Private *d, QObject *parent = nullptr);\n" "\n" "private:\n" " Private *d_func() const;\n" "};\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()); } void Generator::generateServerResourceClass(const Interface &interface) { if (interface.factory()->isUnstableInterface()) { generateServerResourceClassUnstable(interface); return; } const QString templateString = QStringLiteral( "class KWAYLANDSERVER_EXPORT %1 : public Resource\n" "{\n" " Q_OBJECT\n" "public:\n" " virtual ~%1();\n" "\n" "private:\n" " explicit %1(%2 *parent, wl_resource *parentResource);\n" " friend class %2;\n" "\n" " class Private;\n" " Private *d_func() const;\n" "};\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()); } void Generator::generateServerResourceClassUnstable(const Interface &interface) { const QString templateString = QStringLiteral( "class KWAYLANDSERVER_EXPORT %1 : public Resource\n" "{\n" " Q_OBJECT\n" "public:\n" "\n" " virtual ~%1();\n" "\n" " /**\n" " * @returns The interface version used by this %1\n" " **/\n" " %2Version interfaceVersion() const;\n" "\n" "protected:\n" " class Private;\n" " explicit %1(Private *p, QObject *parent = nullptr);\n" "\n" "private:\n" " Private *d_func() const;\n" "};\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()); } void Generator::generatePrivateClass(const Interface &interface) { switch (m_project.localData()) { case Project::Client: generateClientPrivateClass(interface); break; case Project::Server: if (interface.isGlobal()) { generateServerPrivateGlobalClass(interface); } else { generateServerPrivateResourceClass(interface); } break; default: Q_UNREACHABLE(); } } void Generator::generateServerPrivateGlobalClass(const Interface &interface) { QString templateString = QStringLiteral( "class %1::Private : public Global::Private\n" "{\n" "public:\n" " Private(%1 *q, Display *d);\n" "\n" "private:\n" " void bind(wl_client *client, uint32_t version, uint32_t id) override;\n" "\n" " static void unbind(wl_resource *resource);\n" " static Private *cast(wl_resource *r) {\n" " return reinterpret_cast(wl_resource_get_user_data(r));\n" " }\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()); generateServerPrivateCallbackDefinitions(interface); templateString = QStringLiteral( " %1 *q;\n" " static const struct %2_interface s_interface;\n" " static const quint32 s_version;\n" "};\n" "\n" "const quint32 %1::Private::s_version = %3;\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name()).arg(interface.version()); generateServerPrivateInterfaceClass(interface); generateServerPrivateCallbackImpl(interface); generateServerPrivateGlobalCtorBindClass(interface); } void Generator::generateServerPrivateCallbackDefinitions(const Interface &interface) { for (const auto &r: interface.requests()) { if (r.isDestructor() && !interface.isGlobal()) { continue; } *m_stream.localData() << QStringLiteral(" static void %1Callback(wl_client *client, wl_resource *resource").arg(toCamelCase(r.name())); for (const auto &a: r.arguments()) { *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name()); } *m_stream.localData() << QStringLiteral(");\n"); } *m_stream.localData() << QStringLiteral("\n"); } void Generator::generateServerPrivateCallbackImpl(const Interface &interface) { for (const auto &r: interface.requests()) { if (r.isDestructor() && !interface.isGlobal()) { continue; } *m_stream.localData() << QStringLiteral("void %2::Private::%1Callback(wl_client *client, wl_resource *resource").arg(toCamelCase(r.name())).arg(interface.kwaylandServerName()); for (const auto &a: r.arguments()) { *m_stream.localData() << QStringLiteral(", %1 %2").arg(a.typeAsServerWl()).arg(a.name()); } *m_stream.localData() << QStringLiteral( ")\n" "{\n"); if (r.isDestructor()) { *m_stream.localData() << QStringLiteral( " Q_UNUSED(client)\n" " wl_resource_destroy(resource);\n"); } else { *m_stream.localData() << QStringLiteral(" // TODO: implement\n"); } *m_stream.localData() << QStringLiteral( "}\n" "\n"); } } void Generator::generateServerPrivateGlobalCtorBindClass(const Interface &interface) { QString templateString = QStringLiteral( "%1::Private::Private(%1 *q, Display *d)\n" " : Global::Private(d, &%2_interface, s_version)\n" " , q(q)\n" "{\n" "}\n" "\n" "void %1::Private::bind(wl_client *client, uint32_t version, uint32_t id)\n" "{\n" " auto c = display->getConnection(client);\n" " wl_resource *resource = c->createResource(&%2_interface, qMin(version, s_version), id);\n" " if (!resource) {\n" " wl_client_post_no_memory(client);\n" " return;\n" " }\n" " wl_resource_set_implementation(resource, &s_interface, this, unbind);\n" " // TODO: should we track?\n" "}\n" "\n" "void %1::Private::unbind(wl_resource *resource)\n" "{\n" " Q_UNUSED(resource)\n" " // TODO: implement?\n" "}\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name()); } void Generator::generateServerPrivateResourceClass(const Interface &interface) { QString templateString = QStringLiteral( "class %1::Private : public Resource::Private\n" "{\n" "public:\n" " Private(%1 *q, %2 *c, wl_resource *parentResource);\n" " ~Private();\n" "\n" "private:\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()); generateServerPrivateCallbackDefinitions(interface); templateString = QStringLiteral( " %1 *q_func() {\n" " return reinterpret_cast<%1 *>(q);\n" " }\n" "\n" " static const struct %2_interface s_interface;\n" "};\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.name()); generateServerPrivateInterfaceClass(interface); generateServerPrivateCallbackImpl(interface); generateServerPrivateResourceCtorDtorClass(interface); } void Generator::generateServerPrivateInterfaceClass(const Interface &interface) { *m_stream.localData() << QStringLiteral("#ifndef K_DOXYGEN\n"); *m_stream.localData() << QStringLiteral("const struct %2_interface %1::Private::s_interface = {\n").arg(interface.kwaylandServerName()).arg(interface.name()); bool first = true; for (auto r: interface.requests()) { if (!first) { *m_stream.localData() << QStringLiteral(",\n"); } else { first = false; } if (r.isDestructor() && !interface.isGlobal()) { *m_stream.localData() << QStringLiteral(" resourceDestroyedCallback"); } else { *m_stream.localData() << QStringLiteral(" %1Callback").arg(toCamelCase(r.name())); } } *m_stream.localData() << QStringLiteral("\n};\n#endif\n\n"); } void Generator::generateServerPrivateResourceCtorDtorClass(const Interface &interface) { QString templateString = QStringLiteral( "%1::Private::Private(%1 *q, %2 *c, wl_resource *parentResource)\n" " : Resource::Private(q, c, parentResource, &%3_interface, &s_interface)\n" "{\n" "}\n" "\n" "%1::Private::~Private()\n" "{\n" " if (resource) {\n" " wl_resource_destroy(resource);\n" " resource = nullptr;\n" " }\n" "}\n"); *m_stream.localData() << templateString.arg(interface.kwaylandServerName()).arg(interface.factory()->kwaylandServerName()).arg(interface.name()); } void Generator::generateClientPrivateClass(const Interface &interface) { if (interface.isGlobal()) { generateClientPrivateGlobalClass(interface); } else { generateClientPrivateResourceClass(interface); } const auto events = interface.events(); if (!events.isEmpty()) { *m_stream.localData() << QStringLiteral("\nprivate:\n"); // generate the callbacks for (auto event : events) { const QString templateString = QStringLiteral(" static void %1Callback(void *data, %2 *%2"); *m_stream.localData() << templateString.arg(event.name()).arg(interface.name()); const auto arguments = event.arguments(); for (auto argument : arguments) { if (argument.interface().isNull()) { *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name()); } else { *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name()); } } *m_stream.localData() << ");\n"; } *m_stream.localData() << QStringLiteral("\n static const %1_listener s_listener;\n").arg(interface.name()); } *m_stream.localData() << QStringLiteral("};\n\n"); } void Generator::generateClientPrivateResourceClass(const Interface &interface) { const QString templateString = QStringLiteral( "class %1::Private\n" "{\n" "public:\n" " Private(%1 *q);\n" "\n" " void setup(%2 *arg);\n" "\n" " WaylandPointer<%2, %2_destroy> %3;\n" "\n" "private:\n" " %1 *q;\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower()); } void Generator::generateClientPrivateGlobalClass(const Interface &interface) { const QString templateString = QStringLiteral( "class %1::Private\n" "{\n" "public:\n" " Private() = default;\n" "\n" " void setup(%2 *arg);\n" "\n" " WaylandPointer<%2, %2_destroy> %3;\n" " EventQueue *queue = nullptr;\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower()); } void Generator::generateClientCpp(const Interface &interface) { // TODO: generate listener and callbacks const auto events = interface.events(); if (!events.isEmpty()) { // listener *m_stream.localData() << QStringLiteral("const %1_listener %2::Private::s_listener = {\n").arg(interface.name()).arg(interface.kwaylandClientName()); bool first = true; for (auto event : events) { if (!first) { *m_stream.localData() << QStringLiteral(",\n"); } *m_stream.localData() << QStringLiteral(" %1Callback").arg(event.name()); first = false; } *m_stream.localData() << QStringLiteral("\n};\n\n"); // callbacks for (auto event : events) { *m_stream.localData() << QStringLiteral("void %1::Private::%2Callback(void *data, %3 *%3").arg(interface.kwaylandClientName()).arg(event.name()).arg(interface.name()); const auto arguments = event.arguments(); for (auto argument : arguments) { if (argument.interface().isNull()) { *m_stream.localData() << QStringLiteral(", %1 %2").arg(argument.typeAsServerWl()).arg(argument.name()); } else { *m_stream.localData() << QStringLiteral(", %1 *%2").arg(argument.interface()).arg(argument.name()); } } *m_stream.localData() << QStringLiteral( ")\n" "{\n" " auto p = reinterpret_cast<%1::Private*>(data);\n" " Q_ASSERT(p->%2 == %3);\n").arg(interface.kwaylandClientName()) .arg(interface.kwaylandClientName().toLower()) .arg(interface.name()); for (auto argument : arguments) { *m_stream.localData() << QStringLiteral(" Q_UNUSED(%1)\n").arg(argument.name()); } *m_stream.localData() << QStringLiteral(" // TODO: implement\n}\n\n"); } } if (interface.isGlobal()) { // generate ctor without this pointer to Private const QString templateString = QStringLiteral( "%1::%1(QObject *parent)\n" " : QObject(parent)\n" " , d(new Private)\n" "{\n" "}\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } else { // Private ctor const QString templateString = QStringLiteral( "%1::Private::Private(%1 *q)\n" " : q(q)\n" "{\n" "}\n" "\n" "%1::%1(QObject *parent)\n" " : QObject(parent)\n" " , d(new Private(this))\n" "{\n" "}\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } // setup call with optional add_listener const QString setupTemplate = QStringLiteral( "\n" "void %1::Private::setup(%3 *arg)\n" "{\n" " Q_ASSERT(arg);\n" " Q_ASSERT(!%2);\n" " %2.setup(arg);\n"); *m_stream.localData() << setupTemplate.arg(interface.kwaylandClientName()) .arg(interface.kwaylandClientName().toLower()) .arg(interface.name()); if (!interface.events().isEmpty()) { *m_stream.localData() << QStringLiteral(" %1_add_listener(%2, &s_listener, this);\n").arg(interface.name()).arg(interface.kwaylandClientName().toLower()); } *m_stream.localData() << QStringLiteral("}\n"); const QString templateString = QStringLiteral( "\n" "%1::~%1()\n" "{\n" " release();\n" "}\n" "\n" "void %1::setup(%3 *%2)\n" "{\n" " d->setup(%2);\n" "}\n" "\n" "void %1::release()\n" "{\n" " d->%2.release();\n" "}\n" "\n" "void %1::destroy()\n" "{\n" " d->%2.destroy();\n" "}\n" "\n" "%1::operator %3*() {\n" " return d->%2;\n" "}\n" "\n" "%1::operator %3*() const {\n" " return d->%2;\n" "}\n" "\n" "bool %1::isValid() const\n" "{\n" " return d->%2.isValid();\n" "}\n\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()) .arg(interface.kwaylandClientName().toLower()) .arg(interface.name()); if (interface.isGlobal()) { const QString templateStringGlobal = QStringLiteral( "void %1::setEventQueue(EventQueue *queue)\n" "{\n" " d->queue = queue;\n" "}\n" "\n" "EventQueue *%1::eventQueue()\n" "{\n" " return d->queue;\n" "}\n\n" ); *m_stream.localData() << templateStringGlobal.arg(interface.kwaylandClientName()); } } void Generator::generateClientGlobalClassDoxy(const Interface &interface) { const QString templateString = QStringLiteral( "/**\n" " * @short Wrapper for the %2 interface.\n" " *\n" " * This class provides a convenient wrapper for the %2 interface.\n" " *\n" " * To use this class one needs to interact with the Registry. There are two\n" " * possible ways to create the %1 interface:\n" " * @code\n" " * %1 *c = registry->create%1(name, version);\n" " * @endcode\n" " *\n" " * This creates the %1 and sets it up directly. As an alternative this\n" " * can also be done in a more low level way:\n" " * @code\n" " * %1 *c = new %1;\n" " * c->setup(registry->bind%1(name, version));\n" " * @endcode\n" " *\n" " * The %1 can be used as a drop-in replacement for any %2\n" " * pointer as it provides matching cast operators.\n" " *\n" " * @see Registry\n" " **/\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()); } void Generator::generateClientClassQObjectDerived(const Interface &interface) { const QString templateString = QStringLiteral( "class KWAYLANDCLIENT_EXPORT %1 : public QObject\n" "{\n" " Q_OBJECT\n" "public:\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } void Generator::generateClientGlobalClassCtor(const Interface &interface) { const QString templateString = QStringLiteral( " /**\n" " * Creates a new %1.\n" " * Note: after constructing the %1 it is not yet valid and one needs\n" " * to call setup. In order to get a ready to use %1 prefer using\n" " * Registry::create%1.\n" " **/\n" " explicit %1(QObject *parent = nullptr);\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } void Generator::generateClientClassDtor(const Interface &interface) { *m_stream.localData() << QStringLiteral(" virtual ~%1();\n\n").arg(interface.kwaylandClientName()); } void Generator::generateClientClassReleaseDestroy(const Interface &interface) { const QString templateString = QStringLiteral( " /**\n" " * @returns @c true if managing a %2.\n" " **/\n" " bool isValid() const;\n" " /**\n" " * Releases the %2 interface.\n" " * After the interface has been released the %1 instance is no\n" " * longer valid and can be setup with another %2 interface.\n" " **/\n" " void release();\n" " /**\n" " * Destroys the data held by this %1.\n" " * This method is supposed to be used when the connection to the Wayland\n" " * server goes away. If the connection is not valid anymore, it's not\n" " * possible to call release anymore as that calls into the Wayland\n" " * connection and the call would fail. This method cleans up the data, so\n" " * that the instance can be deleted or set up to a new %2 interface\n" " * once there is a new connection available.\n" " *\n" " * It is suggested to connect this method to ConnectionThread::connectionDied:\n" " * @code\n" " * connect(connection, &ConnectionThread::connectionDied, %3, &%1::destroy);\n" " * @endcode\n" " *\n" " * @see release\n" " **/\n" " void destroy();\n" "\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower()); } void Generator::generateClientGlobalClassSetup(const Interface &interface) { const QString templateString = QStringLiteral( " /**\n" " * Setup this %1 to manage the @p %3.\n" " * When using Registry::create%1 there is no need to call this\n" " * method.\n" " **/\n" " void setup(%2 *%3);\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()).arg(interface.name()).arg(interface.kwaylandClientName().toLower()); } void Generator::generateClientResourceClassSetup(const Interface &interface) { const QString templateString = QStringLiteral( " /**\n" " * Setup this %1 to manage the @p %3.\n" " * When using %4::create%1 there is no need to call this\n" " * method.\n" " **/\n" " void setup(%2 *%3);\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()) .arg(interface.name()) .arg(interface.kwaylandClientName().toLower()) .arg(interface.factory()->kwaylandClientName()); } void Generator::generateClientClassStart(const Interface &interface) { const QString templateString = QStringLiteral( " /**\n" " * Sets the @p queue to use for creating objects with this %1.\n" " **/\n" " void setEventQueue(EventQueue *queue);\n" " /**\n" " * @returns The event queue to use for creating objects with this %1.\n" " **/\n" " EventQueue *eventQueue();\n\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } void Generator::generateClientClassRequests(const Interface &interface) { const auto requests = interface.requests(); const QString templateString = QStringLiteral(" void %1(%2);\n\n"); const QString factoryTemplateString = QStringLiteral(" %1 *%2(%3);\n\n"); for (const auto &r: requests) { if (r.isDestructor()) { continue; } QString arguments; bool first = true; QString factored; for (const auto &a: r.arguments()) { if (a.type() == Argument::Type::NewId) { factored = a.interface(); continue; } if (!first) { arguments.append(QStringLiteral(", ")); } else { first = false; } if (a.type() == Argument::Type::Object) { arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name()))); } else { arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name()))); } } if (factored.isEmpty()) { *m_stream.localData() << templateString.arg(toCamelCase((r.name()))).arg(arguments); } else { if (!first) { arguments.append(QStringLiteral(", ")); } arguments.append(QStringLiteral("QObject *parent = nullptr")); *m_stream.localData() << factoryTemplateString.arg(toQtInterfaceName(factored)).arg(toCamelCase(r.name())).arg(arguments); } } } void Generator::generateClientCppRequests(const Interface &interface) { const auto requests = interface.requests(); const QString templateString = QStringLiteral( "void %1::%2(%3)\n" "{\n" " Q_ASSERT(isValid());\n" " %4_%5(d->%6%7);\n" "}\n\n"); const QString factoryTemplateString = QStringLiteral( "%2 *%1::%3(%4)\n" "{\n" " Q_ASSERT(isValid());\n" " auto p = new %2(parent);\n" " auto w = %5_%6(d->%7%8);\n" " if (d->queue) {\n" " d->queue->addProxy(w);\n" " }\n" " p->setup(w);\n" " return p;\n" "}\n\n" ); for (const auto &r: requests) { if (r.isDestructor()) { continue; } QString arguments; QString requestArguments; bool first = true; QString factored; for (const auto &a: r.arguments()) { if (a.type() == Argument::Type::NewId) { factored = a.interface(); continue; } if (!first) { arguments.append(QStringLiteral(", ")); } else { first = false; } if (a.type() == Argument::Type::Object) { arguments.append(QStringLiteral("%1 *%2").arg(a.typeAsQt()).arg(toCamelCase(a.name()))); requestArguments.append(QStringLiteral(", *%1").arg(toCamelCase(a.name()))); } else { arguments.append(QStringLiteral("%1 %2").arg(a.typeAsQt()).arg(toCamelCase(a.name()))); QString arg = toCamelCase(a.name()); if (a.type() == Argument::Type::Fixed) { arg = QStringLiteral("wl_fixed_from_double(%1)").arg(arg); } requestArguments.append(QStringLiteral(", %1").arg(arg)); } } if (factored.isEmpty()) { *m_stream.localData() << templateString.arg(interface.kwaylandClientName()) .arg(toCamelCase(r.name())) .arg(arguments) .arg(interface.name()) .arg(r.name()) .arg(interface.kwaylandClientName().toLower()) .arg(requestArguments); } else { if (!first) { arguments.append(QStringLiteral(", ")); } arguments.append(QStringLiteral("QObject *parent")); *m_stream.localData() << factoryTemplateString.arg(interface.kwaylandClientName()) .arg(toQtInterfaceName(factored)) .arg(toCamelCase(r.name())) .arg(arguments) .arg(interface.name()) .arg(r.name()) .arg(interface.kwaylandClientName().toLower()) .arg(requestArguments); } } } void Generator::generateClientClassCasts(const Interface &interface) { const QString templateString = QStringLiteral( " operator %1*();\n" " operator %1*() const;\n\n"); *m_stream.localData() << templateString.arg(interface.name()); } void Generator::generateClientGlobalClassEnd(const Interface &interface) { Q_UNUSED(interface) *m_stream.localData() << QStringLiteral("private:\n"); generateClientClassDptr(interface); *m_stream.localData() << QStringLiteral("};\n\n"); } void Generator::generateClientClassDptr(const Interface &interface) { Q_UNUSED(interface) *m_stream.localData() << QStringLiteral( " class Private;\n" " QScopedPointer d;\n"); } void Generator::generateClientResourceClassEnd(const Interface &interface) { *m_stream.localData() << QStringLiteral( "private:\n" " friend class %2;\n" " explicit %1(QObject *parent = nullptr);\n" ).arg(interface.kwaylandClientName()).arg(interface.factory()->kwaylandClientName()); generateClientClassDptr(interface); *m_stream.localData() << QStringLiteral("};\n\n"); } void Generator::generateWaylandForwardDeclarations() { for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { *m_stream.localData() << QStringLiteral("struct %1;\n").arg((*it).name()); } *m_stream.localData() << "\n"; } void Generator::generateNamespaceForwardDeclarations() { QSet referencedObjects; for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { const auto events = (*it).events(); const auto requests = (*it).requests(); for (const auto &e: events) { const auto args = e.arguments(); for (const auto &a: args) { if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) { continue; } referencedObjects << a.interface(); } } for (const auto &r: requests) { const auto args = r.arguments(); for (const auto &a: args) { if (a.type() != Argument::Type::Object && a.type() != Argument::Type::NewId) { continue; } referencedObjects << a.interface(); } } } switch (m_project.localData()) { case Project::Client: *m_stream.localData() << QStringLiteral("class EventQueue;\n"); for (const auto &o : referencedObjects) { auto it = s_clientClassNameMapping.constFind(o); if (it != s_clientClassNameMapping.constEnd()) { *m_stream.localData() << QStringLiteral("class %1;\n").arg(it.value()); } else { qWarning() << "Cannot forward declare KWayland class for interface " << o; } } *m_stream.localData() << QStringLiteral("\n"); break; case Project::Server: *m_stream.localData() << QStringLiteral("class Display;\n\n"); break; default: Q_UNREACHABLE(); } } void Generator::generateClientClassSignals(const Interface &interface) { const QString templateString = QStringLiteral( "Q_SIGNALS:\n" " /**\n" " * The corresponding global for this interface on the Registry got removed.\n" " *\n" " * This signal gets only emitted if the %1 got created by\n" " * Registry::create%1\n" " **/\n" " void removed();\n\n"); *m_stream.localData() << templateString.arg(interface.kwaylandClientName()); } QString Generator::projectToName() const { switch (m_project.localData()) { case Project::Client: return QStringLiteral("Client"); case Project::Server: return QStringLiteral("Server"); default: Q_UNREACHABLE(); } } static void parseMapping() { QFile mappingFile(QStringLiteral(MAPPING_FILE)); mappingFile.open(QIODevice::ReadOnly); QTextStream stream(&mappingFile); while (!stream.atEnd()) { QString line = stream.readLine(); if (line.startsWith(QLatin1String("#")) || line.isEmpty()) { continue; } const QStringList parts = line.split(QStringLiteral(";")); if (parts.count() < 2) { continue; } s_clientClassNameMapping.insert(parts.first(), parts.at(1)); } } } } int main(int argc, char **argv) { using namespace KWayland::Tools; parseMapping(); QCoreApplication app(argc, argv); QCommandLineParser parser; QCommandLineOption xmlFile(QStringList{QStringLiteral("x"), QStringLiteral("xml")}, QStringLiteral("The wayland protocol to parse."), QStringLiteral("FileName")); QCommandLineOption fileName(QStringList{QStringLiteral("f"), QStringLiteral("file")}, QStringLiteral("The base name of files to be generated. E.g. for \"foo\" the files \"foo.h\" and \"foo.cpp\" are generated." "If not provided the base name gets derived from the xml protocol name"), QStringLiteral("FileName")); parser.addHelpOption(); parser.addOption(xmlFile); parser.addOption(fileName); parser.process(app); Generator generator(&app); generator.setXmlFileName(parser.value(xmlFile)); generator.setBaseFileName(parser.value(fileName)); generator.start(); return app.exec(); } diff --git a/src/tools/generator.h b/src/tools/generator.h index 764ff17..a34f83c 100644 --- a/src/tools/generator.h +++ b/src/tools/generator.h @@ -1,298 +1,284 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef KWAYLAND_TOOLS_GENERATOR_H #define KWAYLAND_TOOLS_GENERATOR_H #include #include #include #include #include #include class QTextStream; namespace KWayland { namespace Tools { class Argument { public: explicit Argument(); explicit Argument(const QXmlStreamAttributes &attributes); ~Argument(); enum class Type { Unknown, NewId, Destructor, Object, FileDescriptor, Fixed, Uint, Int, String }; QString name() const { return m_name; } Type type() const { return m_type; } bool isNullAllowed() const { return m_allowNull; } QString interface() const { return m_inteface; } QString typeAsQt() const; QString typeAsServerWl() const; private: Type parseType(const QStringRef &type); QString m_name; Type m_type = Type::Unknown; bool m_allowNull = false; QString m_inteface; }; class Request { public: explicit Request(); explicit Request(const QString &name); ~Request(); void addArgument(const Argument &arg) { m_arguments << arg; } QString name() const { return m_name; } QVector arguments() const { return m_arguments; } bool isDestructor() const { return m_destructor; } bool isFactory() const; void markAsDestructor() { m_destructor = true; } private: QString m_name; QVector m_arguments; bool m_destructor = false; }; class Event { public: explicit Event(); explicit Event(const QString &name); ~Event(); void addArgument(const Argument &arg) { m_arguments << arg; } QString name() const { return m_name; } QVector arguments() const { return m_arguments; } private: QString m_name; QVector m_arguments; }; class Interface { public: explicit Interface(); explicit Interface(const QXmlStreamAttributes &attributes); virtual ~Interface(); void addRequest(const Request &request) { m_requests << request; } void addEvent(const Event &event) { m_events << event; } QString name() const { return m_name; } quint32 version() const { return m_version; } QString kwaylandClientName() const { return m_clientName; } QString kwaylandServerName() const { return m_clientName + QStringLiteral("Interface"); } QVector requests() const { return m_requests; } QVector events() const { return m_events; } void markAsGlobal() { m_global = true; } bool isGlobal() const { return m_global; } void setFactory(Interface *factory) { m_factory = factory; } Interface *factory() const { return m_factory; } bool isUnstableInterface() const { return m_name.startsWith(QLatin1String("zwp")); } private: QString m_name; QString m_clientName; quint32 m_version; QVector m_requests; QVector m_events; bool m_global = false; Interface *m_factory; }; class Generator : public QObject { Q_OBJECT public: explicit Generator(QObject *parent = nullptr); virtual ~Generator(); void setXmlFileName(const QString &name) { m_xmlFileName = name; } void setBaseFileName(const QString &name) { m_baseFileName = name; } void start(); private: void generateCopyrightHeader(); void generateStartIncludeGuard(); void generateEndIncludeGuard(); void generateStartNamespace(); void generateEndNamespace(); void generateHeaderIncludes(); void generateCppIncludes(); void generatePrivateClass(const Interface &interface); void generateClientPrivateClass(const Interface &interface); void generateClientPrivateResourceClass(const Interface &interface); void generateClientPrivateGlobalClass(const Interface &interface); void generateServerPrivateGlobalClass(const Interface &interface); void generateServerPrivateResourceClass(const Interface &interface); void generateServerPrivateInterfaceClass(const Interface &interface); void generateServerPrivateGlobalCtorBindClass(const Interface &interface); void generateServerPrivateResourceCtorDtorClass(const Interface &interface); void generateServerPrivateCallbackDefinitions(const Interface &interface); void generateServerPrivateCallbackImpl(const Interface &interface); void generateClientCpp(const Interface &interface); void generateClass(const Interface &interface); void generateClientGlobalClass(const Interface &interface); void generateClientResourceClass(const Interface &interface); void generateServerGlobalClass(const Interface &interface); void generateServerGlobalClassUnstable(const Interface &interface); void generateServerResourceClass(const Interface &interface); void generateServerResourceClassUnstable(const Interface &interface); void generateClientClassQObjectDerived(const Interface &interface); void generateClientGlobalClassDoxy(const Interface &interface); void generateClientGlobalClassCtor(const Interface &interface); void generateClientGlobalClassSetup(const Interface &interface); void generateClientResourceClassSetup(const Interface &interface); void generateClientClassDtor(const Interface &interface); void generateClientClassReleaseDestroy(const Interface &interface); void generateClientClassStart(const Interface &interface); void generateClientClassCasts(const Interface &interface); void generateClientClassSignals(const Interface &interface); void generateClientClassDptr(const Interface &interface); void generateClientGlobalClassEnd(const Interface &interface); void generateClientResourceClassEnd(const Interface &interface); void generateClientClassRequests(const Interface &interface); void generateClientCppRequests(const Interface &interface); void generateWaylandForwardDeclarations(); void generateNamespaceForwardDeclarations(); void startParseXml(); void startAuthorNameProcess(); void startAuthorEmailProcess(); void startGenerateHeaderFile(); void startGenerateCppFile(); void startGenerateServerHeaderFile(); void startGenerateServerCppFile(); void checkEnd(); void parseProtocol(); Interface parseInterface(); Request parseRequest(); Event parseEvent(); QString projectToName() const; QThreadStorage m_stream; QString m_xmlFileName; enum class Project { Client, Server }; QThreadStorage m_project; QString m_authorName; QString m_authorEmail; QString m_baseFileName; QMutex m_mutex; QWaitCondition m_waitCondition; QXmlStreamReader m_xmlReader; QVector m_interfaces; int m_finishedCounter = 0; }; } } #endif diff --git a/src/tools/testserver/main.cpp b/src/tools/testserver/main.cpp index cff2eff..dbb4d99 100644 --- a/src/tools/testserver/main.cpp +++ b/src/tools/testserver/main.cpp @@ -1,39 +1,25 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "testserver.h" #include int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); auto arguments = QCoreApplication::arguments(); // get rid of our own application path arguments.removeFirst(); if (arguments.size() < 1) { return 1; } TestServer *server = new TestServer(&a); server->init(); server->startTestApp(arguments.takeFirst(), arguments); return a.exec(); } diff --git a/src/tools/testserver/testserver.cpp b/src/tools/testserver/testserver.cpp index c80bed3..1fa5881 100644 --- a/src/tools/testserver/testserver.cpp +++ b/src/tools/testserver/testserver.cpp @@ -1,199 +1,185 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "testserver.h" #include "../../server/display.h" #include "../../server/compositor_interface.h" #include "../../server/datadevicemanager_interface.h" #include "../../server/idle_interface.h" #include "../../server/fakeinput_interface.h" #include "../../server/seat_interface.h" #include "../../server/shell_interface.h" #include "../../server/surface_interface.h" #include "../../server/subcompositor_interface.h" #include #include #include #include // system #include #include #include using namespace KWayland::Server; TestServer::TestServer(QObject *parent) : QObject(parent) , m_repaintTimer(new QTimer(this)) , m_timeSinceStart(new QElapsedTimer) , m_cursorPos(QPointF(0, 0)) { } TestServer::~TestServer() = default; void TestServer::init() { Q_ASSERT(!m_display); m_display = new Display(this); m_display->start(Display::StartMode::ConnectClientsOnly); m_display->createShm(); m_display->createCompositor()->create(); m_shell = m_display->createShell(m_display); connect(m_shell, &ShellInterface::surfaceCreated, this, [this] (ShellSurfaceInterface *surface) { m_shellSurfaces << surface; // TODO: pass keyboard/pointer/touch focus on mapped connect(surface, &QObject::destroyed, this, [this, surface] { m_shellSurfaces.removeOne(surface); } ); } ); m_shell->create(); m_seat = m_display->createSeat(m_display); m_seat->setHasKeyboard(true); m_seat->setHasPointer(true); m_seat->setHasTouch(true); m_seat->create(); m_display->createDataDeviceManager(m_display)->create(); m_display->createIdle(m_display)->create(); m_display->createSubCompositor(m_display)->create(); // output auto output = m_display->createOutput(m_display); const QSize size(1280, 1024); output->setGlobalPosition(QPoint(0, 0)); output->setPhysicalSize(size / 3.8); output->addMode(size); output->create(); auto fakeInput = m_display->createFakeInput(m_display); fakeInput->create(); connect(fakeInput, &FakeInputInterface::deviceCreated, this, [this] (FakeInputDevice *device) { device->setAuthentication(true); connect(device, &FakeInputDevice::pointerMotionRequested, this, [this] (const QSizeF &delta) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_cursorPos = m_cursorPos + QPointF(delta.width(), delta.height()); m_seat->setPointerPos(m_cursorPos); } ); connect(device, &FakeInputDevice::pointerButtonPressRequested, this, [this] (quint32 button) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_seat->pointerButtonPressed(button); } ); connect(device, &FakeInputDevice::pointerButtonReleaseRequested, this, [this] (quint32 button) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_seat->pointerButtonReleased(button); } ); connect(device, &FakeInputDevice::pointerAxisRequested, this, [this] (Qt::Orientation orientation, qreal delta) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_seat->pointerAxis(orientation, delta); } ); connect(device, &FakeInputDevice::touchDownRequested, this, [this] (quint32 id, const QPointF &pos) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_touchIdMapper.insert(id, m_seat->touchDown(pos)); } ); connect(device, &FakeInputDevice::touchMotionRequested, this, [this] (quint32 id, const QPointF &pos) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); const auto it = m_touchIdMapper.constFind(id); if (it != m_touchIdMapper.constEnd()) { m_seat->touchMove(it.value(), pos); } } ); connect(device, &FakeInputDevice::touchUpRequested, this, [this] (quint32 id) { m_seat->setTimestamp(m_timeSinceStart->elapsed()); const auto it = m_touchIdMapper.find(id); if (it != m_touchIdMapper.end()) { m_seat->touchUp(it.value()); m_touchIdMapper.erase(it); } } ); connect(device, &FakeInputDevice::touchCancelRequested, this, [this] { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_seat->cancelTouchSequence(); } ); connect(device, &FakeInputDevice::touchFrameRequested, this, [this] { m_seat->setTimestamp(m_timeSinceStart->elapsed()); m_seat->touchFrame(); } ); } ); m_repaintTimer->setInterval(1000 / 60); connect(m_repaintTimer, &QTimer::timeout, this, &TestServer::repaint); m_repaintTimer->start(); m_timeSinceStart->start(); } void TestServer::startTestApp(const QString &app, const QStringList &arguments) { int sx[2]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) { QCoreApplication::instance()->exit(1); return; } m_display->createClient(sx[0]); int socket = dup(sx[1]); if (socket == -1) { QCoreApplication::instance()->exit(1); return; } QProcess *p = new QProcess(this); p->setProcessChannelMode(QProcess::ForwardedChannels); QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland")); environment.insert(QStringLiteral("WAYLAND_SOCKET"), QString::fromUtf8(QByteArray::number(socket))); p->setProcessEnvironment(environment); auto finishedSignal = static_cast(&QProcess::finished); connect(p, finishedSignal, QCoreApplication::instance(), &QCoreApplication::exit); connect(p, &QProcess::errorOccurred, this, [] { QCoreApplication::instance()->exit(1); } ); p->start(app, arguments); } void TestServer::repaint() { for (auto it = m_shellSurfaces.constBegin(), end = m_shellSurfaces.constEnd(); it != end; ++it) { (*it)->surface()->frameRendered(m_timeSinceStart->elapsed()); } } diff --git a/src/tools/testserver/testserver.h b/src/tools/testserver/testserver.h index 1cee08f..e7db87a 100644 --- a/src/tools/testserver/testserver.h +++ b/src/tools/testserver/testserver.h @@ -1,65 +1,51 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef TESTSERVER_H #define TESTSERVER_H #include #include #include #include class QElapsedTimer; class QTimer; namespace KWayland { namespace Server { class Display; class SeatInterface; class ShellInterface; class ShellSurfaceInterface; } } class TestServer : public QObject { Q_OBJECT public: explicit TestServer(QObject *parent); virtual ~TestServer(); void init(); void startTestApp(const QString &app, const QStringList &arguments); private: void repaint(); KWayland::Server::Display *m_display = nullptr; KWayland::Server::ShellInterface *m_shell = nullptr; KWayland::Server::SeatInterface *m_seat = nullptr; QVector m_shellSurfaces; QTimer *m_repaintTimer; QScopedPointer m_timeSinceStart; QPointF m_cursorPos; QHash m_touchIdMapper; }; #endif diff --git a/tests/copyclient.cpp b/tests/copyclient.cpp index 81aa9b3..f220a47 100644 --- a/tests/copyclient.cpp +++ b/tests/copyclient.cpp @@ -1,199 +1,185 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/datadevice.h" #include "../src/client/datadevicemanager.h" #include "../src/client/datasource.h" #include "../src/client/event_queue.h" #include "../src/client/keyboard.h" #include "../src/client/pointer.h" #include "../src/client/registry.h" #include "../src/client/seat.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" // Qt #include #include #include #include #include using namespace KWayland::Client; class CopyClient : public QObject { Q_OBJECT public: explicit CopyClient(QObject *parent = nullptr); virtual ~CopyClient(); void init(); private: void setupRegistry(Registry *registry); void render(); void copy(const QString &mimeType, qint32 fd); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; DataDeviceManager *m_dataDeviceManager = nullptr; DataDevice *m_dataDevice = nullptr; DataSource *m_copySource = nullptr; Seat *m_seat = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; }; CopyClient::CopyClient(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } CopyClient::~CopyClient() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void CopyClient::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void CopyClient::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { m_seat = registry->createSeat(name, version, this); connect(m_seat, &Seat::hasPointerChanged, this, [this] { auto p = m_seat->createPointer(this); connect(p, &Pointer::entered, this, [this] (quint32 serial) { if (m_copySource) { m_dataDevice->setSelection(serial, m_copySource); } } ); } ); } ); connect(registry, &Registry::dataDeviceManagerAnnounced, this, [this, registry](quint32 name, quint32 version) { m_dataDeviceManager = registry->createDataDeviceManager(name, version, this); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_dataDeviceManager); Q_ASSERT(m_seat); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setFullscreen(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &CopyClient::render); m_dataDevice = m_dataDeviceManager->getDataDevice(m_seat, this); m_copySource = m_dataDeviceManager->createDataSource(this); m_copySource->offer(QStringLiteral("text/plain")); connect(m_copySource, &DataSource::sendDataRequested, this, &CopyClient::copy); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void CopyClient::render() { const QSize &size = m_shellSurface->size(); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::green); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } void CopyClient::copy(const QString &mimeType, qint32 fd) { qDebug() << "Requested to copy for mimeType" << mimeType; QFile c; if (c.open(fd, QFile::WriteOnly, QFile::AutoCloseHandle)) { c.write(QByteArrayLiteral("foo")); c.close(); qDebug() << "Copied foo"; } } int main(int argc, char **argv) { QCoreApplication app(argc, argv); CopyClient client; client.init(); return app.exec(); } #include "copyclient.moc" diff --git a/tests/dpmstest.cpp b/tests/dpmstest.cpp index 8071c79..90641a6 100644 --- a/tests/dpmstest.cpp +++ b/tests/dpmstest.cpp @@ -1,163 +1,149 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include #include #include #include #include #include #include #include "../src/client/connection_thread.h" #include "../src/client/dpms.h" #include "../src/client/registry.h" #include "../src/client/output.h" using namespace KWayland::Client; static QString modeToString(Dpms::Mode mode) { switch (mode) { case Dpms::Mode::On: return QStringLiteral("On"); case Dpms::Mode::Standby: return QStringLiteral("Standby"); case Dpms::Mode::Suspend: return QStringLiteral("Suspend"); case Dpms::Mode::Off: return QStringLiteral("Off"); default: Q_UNREACHABLE(); } } QString supportedToString(bool supported) { return supported ? QStringLiteral("Yes") : QStringLiteral("No"); } static QLayout *setupOutput(Registry::AnnouncedInterface outputInterface, Registry *registry, DpmsManager *manager) { Output *output = registry->createOutput(outputInterface.name, outputInterface.version, registry); QLabel *label = new QLabel(output->model()); QObject::connect(output, &Output::changed, label, [label, output] { label->setText(output->model()); }, Qt::QueuedConnection ); Dpms *dpms = nullptr; if (manager) { dpms = manager->getDpms(output, output); } QFormLayout *dpmsForm = new QFormLayout; bool supported = dpms ? dpms->isSupported() : false; QLabel *supportedLabel = new QLabel(supportedToString(supported)); dpmsForm->addRow(QStringLiteral("Supported:"), supportedLabel); QLabel *modeLabel = new QLabel(modeToString(dpms ? dpms->mode() : Dpms::Mode::On)); dpmsForm->addRow(QStringLiteral("Mode:"), modeLabel); QPushButton *standbyButton = new QPushButton(QStringLiteral("Standby")); QPushButton *suspendButton = new QPushButton(QStringLiteral("Suspend")); QPushButton *offButton = new QPushButton(QStringLiteral("Off")); standbyButton->setEnabled(supported); suspendButton->setEnabled(supported); offButton->setEnabled(supported); QDialogButtonBox *bg = new QDialogButtonBox; bg->addButton(standbyButton, QDialogButtonBox::ActionRole); bg->addButton(suspendButton, QDialogButtonBox::ActionRole); bg->addButton(offButton, QDialogButtonBox::ActionRole); if (dpms) { QObject::connect(dpms, &Dpms::supportedChanged, supportedLabel, [supportedLabel, dpms, standbyButton, suspendButton, offButton] { const bool supported = dpms->isSupported(); supportedLabel->setText(supportedToString(supported)); standbyButton->setEnabled(supported); suspendButton->setEnabled(supported); offButton->setEnabled(supported); }, Qt::QueuedConnection ); QObject::connect(dpms, &Dpms::modeChanged, modeLabel, [modeLabel, dpms] { modeLabel->setText(modeToString(dpms->mode())); }, Qt::QueuedConnection ); QObject::connect(standbyButton, &QPushButton::clicked, dpms, [dpms] { dpms->requestMode(Dpms::Mode::Standby);}); QObject::connect(suspendButton, &QPushButton::clicked, dpms, [dpms] { dpms->requestMode(Dpms::Mode::Suspend);}); QObject::connect(offButton, &QPushButton::clicked, dpms, [dpms] { dpms->requestMode(Dpms::Mode::Off);}); } QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addLayout(dpmsForm); layout->addWidget(bg); return layout; } int main(int argc, char **argv) { qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland")); QApplication app(argc, argv); QWidget window; ConnectionThread *connection = ConnectionThread::fromApplication(); Registry registry; registry.create(connection); QObject::connect(®istry, &Registry::interfacesAnnounced, &app, [®istry, &window] { const bool hasDpms = registry.hasInterface(Registry::Interface::Dpms); QLabel *hasDpmsLabel = new QLabel(&window); if (hasDpms) { hasDpmsLabel->setText(QStringLiteral("Compositor provides a DpmsManager")); } else { hasDpmsLabel->setText(QStringLiteral("Compositor does not provide a DpmsManager")); } QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(hasDpmsLabel); QFrame *hline = new QFrame; hline->setFrameShape(QFrame::HLine); layout->addWidget(hline); DpmsManager *dpmsManager = nullptr; if (hasDpms) { const auto dpmsData = registry.interface(Registry::Interface::Dpms); dpmsManager = registry.createDpmsManager(dpmsData.name, dpmsData.version); } // get all Outputs const auto outputs = registry.interfaces(Registry::Interface::Output); for (auto o : outputs) { layout->addLayout(setupOutput(o, ®istry, dpmsManager)); QFrame *hline = new QFrame; hline->setFrameShape(QFrame::HLine); layout->addWidget(hline); } window.setLayout(layout); window.show(); }, Qt::QueuedConnection ); registry.setup(); return app.exec(); } diff --git a/tests/paneltest.cpp b/tests/paneltest.cpp index ea0eb40..3c09efb 100644 --- a/tests/paneltest.cpp +++ b/tests/paneltest.cpp @@ -1,363 +1,349 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/datadevice.h" #include "../src/client/datadevicemanager.h" #include "../src/client/dataoffer.h" #include "../src/client/event_queue.h" #include "../src/client/keyboard.h" #include "../src/client/plasmashell.h" #include "../src/client/plasmawindowmanagement.h" #include "../src/client/pointer.h" #include "../src/client/registry.h" #include "../src/client/seat.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" // Qt #include #include #include #include #include #include // system #include #include using namespace KWayland::Client; class PanelTest : public QObject { Q_OBJECT public: explicit PanelTest(QObject *parent = nullptr); virtual ~PanelTest(); void init(); private: void setupRegistry(Registry *registry); void render(); void showTooltip(const QPointF &pos); void hideTooltip(); void moveTooltip(const QPointF &pos); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; Seat *m_seat = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; PlasmaShell *m_plasmaShell = nullptr; PlasmaShellSurface *m_plasmaShellSurface = nullptr; PlasmaWindowManagement *m_windowManagement = nullptr; struct { Surface *surface = nullptr; ShellSurface *shellSurface = nullptr; PlasmaShellSurface *plasmaSurface = nullptr; bool visible = false; } m_tooltip; }; PanelTest::PanelTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } PanelTest::~PanelTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void PanelTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void PanelTest::showTooltip(const QPointF &pos) { if (!m_tooltip.surface) { m_tooltip.surface = m_compositor->createSurface(this); m_tooltip.shellSurface = m_shell->createSurface(m_tooltip.surface, this); if (m_plasmaShell) { m_tooltip.plasmaSurface = m_plasmaShell->createSurface(m_tooltip.surface, this); } } m_tooltip.shellSurface->setTransient(m_surface, pos.toPoint()); if (!m_tooltip.visible) { const QSize size(100, 50); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); m_tooltip.surface->attachBuffer(*buffer); m_tooltip.surface->damage(QRect(QPoint(0, 0), size)); m_tooltip.surface->commit(Surface::CommitFlag::None); m_tooltip.visible = true; } } void PanelTest::hideTooltip() { if (!m_tooltip.visible) { return; } m_tooltip.surface->attachBuffer(Buffer::Ptr()); m_tooltip.surface->commit(Surface::CommitFlag::None); m_tooltip.visible = false; } void PanelTest::moveTooltip(const QPointF &pos) { if (m_tooltip.plasmaSurface) { m_tooltip.plasmaSurface->setPosition(QPoint(10, 0) + pos.toPoint()); } } void PanelTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { m_seat = registry->createSeat(name, version, this); connect(m_seat, &Seat::hasPointerChanged, this, [this] (bool has) { if (!has) { return; } auto p = m_seat->createPointer(this); connect(p, &Pointer::buttonStateChanged, this, [this] (quint32 serial, quint32 time, quint32 button, KWayland::Client::Pointer::ButtonState state) { Q_UNUSED(time) Q_UNUSED(serial) if (!m_windowManagement) { return; } if (state == Pointer::ButtonState::Released) { return; } if (button == BTN_LEFT) { m_windowManagement->showDesktop(); } else if (button == BTN_RIGHT) { m_windowManagement->hideDesktop(); } } ); connect(p, &Pointer::entered, this, [this, p] (quint32 serial, const QPointF &relativeToSurface) { Q_UNUSED(serial) if (p->enteredSurface() == m_surface) { showTooltip(relativeToSurface); } } ); connect(p, &Pointer::motion, this, [this, p] (const QPointF &relativeToSurface) { if (p->enteredSurface() == m_surface) { moveTooltip(relativeToSurface); } } ); connect(p, &Pointer::left, this, [this] { hideTooltip(); } ); } ); } ); connect(registry, &Registry::plasmaShellAnnounced, this, [this, registry] (quint32 name, quint32 version) { m_plasmaShell = registry->createPlasmaShell(name, version, this); } ); connect(registry, &Registry::plasmaWindowManagementAnnounced, this, [this, registry] (quint32 name, quint32 version) { m_windowManagement = registry->createPlasmaWindowManagement(name, version, this); connect(m_windowManagement, &PlasmaWindowManagement::showingDesktopChanged, this, [] (bool set) { qDebug() << "Showing desktop changed, new state: " << set; } ); connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, [this] (PlasmaWindow *w) { connect(w, &PlasmaWindow::titleChanged, this, [w] { qDebug() << "Window title changed to: " << w->title(); } ); connect(w, &PlasmaWindow::activeChanged, this, [w] { qDebug() << "Window active changed: " << w->isActive(); } ); connect(w, &PlasmaWindow::maximizedChanged, this, [w] { qDebug() << "Window maximized changed: " << w->isMaximized(); } ); connect(w, &PlasmaWindow::maximizedChanged, this, [w] { qDebug() << "Window minimized changed: " << w->isMinimized(); } ); connect(w, &PlasmaWindow::keepAboveChanged, this, [w] { qDebug() << "Window keep above changed: " << w->isKeepAbove(); } ); connect(w, &PlasmaWindow::keepBelowChanged, this, [w] { qDebug() << "Window keep below changed: " << w->isKeepBelow(); } ); connect(w, &PlasmaWindow::onAllDesktopsChanged, this, [w] { qDebug() << "Window on all desktops changed: " << w->isOnAllDesktops(); } ); connect(w, &PlasmaWindow::fullscreenChanged, this, [w] { qDebug() << "Window full screen changed: " << w->isFullscreen(); } ); connect(w, &PlasmaWindow::demandsAttentionChanged, this, [w] { qDebug() << "Window demands attention changed: " << w->isDemandingAttention(); } ); connect(w, &PlasmaWindow::closeableChanged, this, [w] { qDebug() << "Window is closeable changed: " << w->isCloseable(); } ); connect(w, &PlasmaWindow::minimizeableChanged, this, [w] { qDebug() << "Window is minimizeable changed: " << w->isMinimizeable(); } ); connect(w, &PlasmaWindow::maximizeableChanged, this, [w] { qDebug() << "Window is maximizeable changed: " << w->isMaximizeable(); } ); connect(w, &PlasmaWindow::fullscreenableChanged, this, [w] { qDebug() << "Window is fullscreenable changed: " << w->isFullscreenable(); } ); connect(w, &PlasmaWindow::iconChanged, this, [w] { qDebug() << "Window icon changed: " << w->icon().name(); } ); } ); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_seat); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setToplevel(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &PanelTest::render); if (m_plasmaShell) { m_plasmaShellSurface = m_plasmaShell->createSurface(m_surface, this); m_plasmaShellSurface->setPosition(QPoint(10, 0)); m_plasmaShellSurface->setRole(PlasmaShellSurface::Role::Panel); } render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void PanelTest::render() { const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(300, 20); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::blue); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QGuiApplication app(argc, argv); PanelTest client; client.init(); return app.exec(); } #include "paneltest.moc" diff --git a/tests/pasteclient.cpp b/tests/pasteclient.cpp index 11c64cc..9a0f96e 100644 --- a/tests/pasteclient.cpp +++ b/tests/pasteclient.cpp @@ -1,207 +1,193 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/datadevice.h" #include "../src/client/datadevicemanager.h" #include "../src/client/dataoffer.h" #include "../src/client/event_queue.h" #include "../src/client/keyboard.h" #include "../src/client/pointer.h" #include "../src/client/registry.h" #include "../src/client/seat.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" // Qt #include #include #include #include #include #include #include // system #include using namespace KWayland::Client; class PasteClient : public QObject { Q_OBJECT public: explicit PasteClient(QObject *parent = nullptr); virtual ~PasteClient(); void init(); private: void setupRegistry(Registry *registry); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; DataDeviceManager *m_dataDeviceManager = nullptr; DataDevice *m_dataDevice = nullptr; Seat *m_seat = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; }; PasteClient::PasteClient(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } PasteClient::~PasteClient() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void PasteClient::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void PasteClient::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { m_seat = registry->createSeat(name, version, this); } ); connect(registry, &Registry::dataDeviceManagerAnnounced, this, [this, registry](quint32 name, quint32 version) { m_dataDeviceManager = registry->createDataDeviceManager(name, version, this); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_dataDeviceManager); Q_ASSERT(m_seat); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setFullscreen(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &PasteClient::render); m_dataDevice = m_dataDeviceManager->getDataDevice(m_seat, this); connect(m_dataDevice, &DataDevice::selectionOffered, this, [this] { auto dataOffer = m_dataDevice->offeredSelection(); if (!dataOffer) { return; } const auto &mimeTypes = dataOffer->offeredMimeTypes(); auto it = std::find_if(mimeTypes.constBegin(), mimeTypes.constEnd(), [](const QMimeType &type) { return type.inherits(QStringLiteral("text/plain")); }); if (it == mimeTypes.constEnd()) { return; } int pipeFds[2]; if (pipe(pipeFds) != 0){ return; } dataOffer->receive((*it).name(), pipeFds[1]); close(pipeFds[1]); QtConcurrent::run( [pipeFds] { QFile readPipe; if (readPipe.open(pipeFds[0], QIODevice::ReadOnly)) { qDebug() << "Pasted: " << readPipe.readLine(); } close(pipeFds[0]); QCoreApplication::quit(); } ); } ); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void PasteClient::render() { const QSize &size = m_shellSurface->size(); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::blue); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); PasteClient client; client.init(); return app.exec(); } #include "pasteclient.moc" diff --git a/tests/plasmasurfacetest.cpp b/tests/plasmasurfacetest.cpp index e10ffba..97ac9da 100644 --- a/tests/plasmasurfacetest.cpp +++ b/tests/plasmasurfacetest.cpp @@ -1,215 +1,201 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" #include "../src/client/plasmashell.h" // Qt #include #include #include #include using namespace KWayland::Client; class PlasmaSurfaceTest : public QObject { Q_OBJECT public: explicit PlasmaSurfaceTest(QObject *parent = nullptr); virtual ~PlasmaSurfaceTest(); void init(); void setRole(PlasmaShellSurface::Role role) { m_role = role; } void setSkipTaskbar(bool set) { m_skipTaskbar = set; } void setSkipSwitcher(bool set) { m_skipSwitcher = set; } private: void setupRegistry(Registry *registry); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; PlasmaShell *m_plasmaShell = nullptr; PlasmaShellSurface *m_plasmaShellSurface = nullptr; PlasmaShellSurface::Role m_role = PlasmaShellSurface::Role::Normal; bool m_skipTaskbar = false; bool m_skipSwitcher = false; }; PlasmaSurfaceTest::PlasmaSurfaceTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } PlasmaSurfaceTest::~PlasmaSurfaceTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void PlasmaSurfaceTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void PlasmaSurfaceTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::plasmaShellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_plasmaShell = registry->createPlasmaShell(name, version, this); m_plasmaShell->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_shell); Q_ASSERT(m_shm); Q_ASSERT(m_plasmaShell); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setToplevel(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &PlasmaSurfaceTest::render); m_plasmaShellSurface = m_plasmaShell->createSurface(m_surface, this); Q_ASSERT(m_plasmaShellSurface); m_plasmaShellSurface->setSkipTaskbar(m_skipTaskbar); m_plasmaShellSurface->setSkipSwitcher(m_skipSwitcher); m_plasmaShellSurface->setRole(m_role); render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void PlasmaSurfaceTest::render() { const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(300, 200); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 128)); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); QCommandLineParser parser; parser.addHelpOption(); QCommandLineOption notificationOption(QStringLiteral("notification")); parser.addOption(notificationOption); QCommandLineOption criticalNotificationOption(QStringLiteral("criticalNotification")); parser.addOption(criticalNotificationOption); QCommandLineOption panelOption(QStringLiteral("panel")); parser.addOption(panelOption); QCommandLineOption desktopOption(QStringLiteral("desktop")); parser.addOption(desktopOption); QCommandLineOption osdOption(QStringLiteral("osd")); parser.addOption(osdOption); QCommandLineOption tooltipOption(QStringLiteral("tooltip")); parser.addOption(tooltipOption); QCommandLineOption skipTaskbarOption(QStringLiteral("skipTaskbar")); parser.addOption(skipTaskbarOption); parser.process(app); QCommandLineOption skipSwitcherOption(QStringLiteral("skipSwitcher")); parser.addOption(skipSwitcherOption); parser.process(app); PlasmaSurfaceTest client; if (parser.isSet(notificationOption)) { client.setRole(PlasmaShellSurface::Role::Notification); } else if (parser.isSet(criticalNotificationOption)) { client.setRole(PlasmaShellSurface::Role::CriticalNotification); } else if (parser.isSet(panelOption)) { client.setRole(PlasmaShellSurface::Role::Panel); } else if (parser.isSet(desktopOption)) { client.setRole(PlasmaShellSurface::Role::Desktop); } else if (parser.isSet(osdOption)) { client.setRole(PlasmaShellSurface::Role::OnScreenDisplay); } else if (parser.isSet(tooltipOption)) { client.setRole(PlasmaShellSurface::Role::ToolTip); } client.setSkipTaskbar(parser.isSet(skipTaskbarOption)); client.setSkipSwitcher(parser.isSet(skipSwitcherOption)); client.init(); return app.exec(); } #include "plasmasurfacetest.moc" diff --git a/tests/qtwaylandintegrationtest.cpp b/tests/qtwaylandintegrationtest.cpp index 77209da..7088f25 100644 --- a/tests/qtwaylandintegrationtest.cpp +++ b/tests/qtwaylandintegrationtest.cpp @@ -1,129 +1,115 @@ -/******************************************************************** -Copyright 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "qtwaylandintegrationtest.h" // KWin::Wayland #include <../src/client/buffer.h> #include <../src/client/compositor.h> #include <../src/client/connection_thread.h> #include <../src/client/pointer.h> #include <../src/client/registry.h> #include <../src/client/shell.h> #include <../src/client/shm_pool.h> #include <../src/client/surface.h> // Qt #include #include #include #include #include #include using namespace KWayland::Client; static Qt::GlobalColor s_colors[] = { Qt::white, Qt::red, Qt::green, Qt::blue, Qt::black }; static int s_colorIndex = 0; WaylandClientTest::WaylandClientTest(QObject *parent) : QObject(parent) , m_connectionThreadObject(ConnectionThread::fromApplication(this)) , m_compositor(Compositor::fromApplication(this)) , m_surface(nullptr) , m_shm(nullptr) , m_shellSurface(nullptr) , m_timer(new QTimer(this)) { init(); } WaylandClientTest::~WaylandClientTest() = default; void WaylandClientTest::init() { connect(m_timer, &QTimer::timeout, this, [this]() { s_colorIndex = (s_colorIndex + 1) % 5; render(); } ); m_timer->setInterval(1000); m_timer->start(); m_surface = m_compositor->createSurface(this); Registry *registry = new Registry(this); setupRegistry(registry); } void WaylandClientTest::setupRegistry(Registry *registry) { connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name) { Shell *shell = registry->createShell(name, 1, this); m_shellSurface = shell->createSurface(m_surface, m_surface); connect(m_shellSurface, &ShellSurface::sizeChanged, this, static_cast(&WaylandClientTest::render)); render(QSize(200, 200)); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name) { m_shm = registry->createShmPool(name, 1, this); } ); registry->create(m_connectionThreadObject->display()); registry->setup(); } void WaylandClientTest::render(const QSize &size) { m_currentSize = size; render(); } void WaylandClientTest::render() { if (!m_shm || !m_surface || !m_surface->isValid() || !m_currentSize.isValid()) { return; } auto buffer = m_shm->getBuffer(m_currentSize, m_currentSize.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), m_currentSize.width(), m_currentSize.height(), QImage::Format_ARGB32_Premultiplied); image.fill(s_colors[s_colorIndex]); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), m_currentSize)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { qputenv("QT_QPA_PLATFORM", "wayland"); QGuiApplication app(argc, argv); new WaylandClientTest(&app); return app.exec(); } diff --git a/tests/qtwaylandintegrationtest.h b/tests/qtwaylandintegrationtest.h index b972edc..fe19a04 100644 --- a/tests/qtwaylandintegrationtest.h +++ b/tests/qtwaylandintegrationtest.h @@ -1,63 +1,49 @@ -/******************************************************************** -Copyright 2014 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef QTWAYLANDINTEGRATIONTEST_H #define QTWAYLANDINTEGRATIONTEST_H #include #include namespace KWayland { namespace Client { class Compositor; class ConnectionThread; class Registry; class ShellSurface; class ShmPool; class Surface; } } class QTimer; class WaylandClientTest : public QObject { Q_OBJECT public: explicit WaylandClientTest(QObject *parent = nullptr); virtual ~WaylandClientTest(); private: void init(); void render(const QSize &size); void render(); void setupRegistry(KWayland::Client::Registry *registry); void toggleTimer(); KWayland::Client::ConnectionThread *m_connectionThreadObject; KWayland::Client::Compositor *m_compositor; KWayland::Client::Surface *m_surface; KWayland::Client::ShmPool *m_shm; KWayland::Client::ShellSurface *m_shellSurface; QSize m_currentSize; QTimer *m_timer; }; #endif diff --git a/tests/renderingservertest.cpp b/tests/renderingservertest.cpp index 5ae0edb..32c7879 100644 --- a/tests/renderingservertest.cpp +++ b/tests/renderingservertest.cpp @@ -1,306 +1,292 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/server/buffer_interface.h" #include "../src/server/compositor_interface.h" #include "../src/server/datadevicemanager_interface.h" #include "../src/server/display.h" #include "../src/server/keyboard_interface.h" #include "../src/server/output_interface.h" #include "../src/server/pointer_interface.h" #include "../src/server/seat_interface.h" #include "../src/server/shell_interface.h" #include #include #include #include #include #include #include #include #include #include static int startXServer() { const QByteArray process = QByteArrayLiteral("Xwayland"); int pipeFds[2]; if (pipe(pipeFds) != 0) { std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl; exit(1); } pid_t pid = fork(); if (pid == 0) { // child process - should be turned into Xwayland // writes to pipe, closes read side close(pipeFds[0]); char fdbuf[16]; sprintf(fdbuf, "%d", pipeFds[1]); execlp(process.constData(), process.constData(), "-displayfd", fdbuf, "-rootless", (char *)nullptr); close(pipeFds[1]); exit(20); } // parent process - this is the wayland server // reads from pipe, closes write side close(pipeFds[1]); return pipeFds[0]; } static void readDisplayFromPipe(int pipe) { QFile readPipe; if (!readPipe.open(pipe, QIODevice::ReadOnly)) { std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl; exit(1); } QByteArray displayNumber = readPipe.readLine(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; setenv("DISPLAY", displayNumber.constData(), true); // close our pipe close(pipe); } class CompositorWindow : public QWidget { Q_OBJECT public: explicit CompositorWindow(QWidget *parent = nullptr); virtual ~CompositorWindow(); void surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface); void setSeat(const QPointer &seat); protected: void paintEvent(QPaintEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: void updateFocus(); QList m_stackingOrder; QPointer m_seat; }; CompositorWindow::CompositorWindow(QWidget *parent) : QWidget(parent) { setMouseTracking(true); } CompositorWindow::~CompositorWindow() = default; void CompositorWindow::surfaceCreated(KWayland::Server::ShellSurfaceInterface *surface) { using namespace KWayland::Server; m_stackingOrder << surface; connect(surface, &ShellSurfaceInterface::fullscreenChanged, this, [surface, this](bool fullscreen) { if (fullscreen) { surface->requestSize(size()); } } ); connect(surface, &ShellSurfaceInterface::maximizedChanged, this, [surface, this](bool maximized) { if (maximized) { surface->requestSize(size()); } } ); connect(surface->surface(), &SurfaceInterface::damaged, this, static_cast(&CompositorWindow::update)); connect(surface, &ShellSurfaceInterface::destroyed, this, [surface, this] { m_stackingOrder.removeAll(surface); updateFocus(); update(); } ); updateFocus(); } void CompositorWindow::updateFocus() { using namespace KWayland::Server; if (!m_seat || m_stackingOrder.isEmpty()) { return; } auto it = std::find_if(m_stackingOrder.constBegin(), m_stackingOrder.constEnd(), [](ShellSurfaceInterface *s) { return s->surface()->buffer() != nullptr; } ); if (it == m_stackingOrder.constEnd()) { return; } m_seat->setFocusedPointerSurface((*it)->surface()); m_seat->setFocusedKeyboardSurface((*it)->surface()); } void CompositorWindow::setSeat(const QPointer< KWayland::Server::SeatInterface > &seat) { m_seat = seat; } void CompositorWindow::paintEvent(QPaintEvent *event) { QWidget::paintEvent(event); QPainter p(this); for (auto s : m_stackingOrder) { if (auto *b = s->surface()->buffer()) { p.drawImage(QPoint(0, 0), b->data()); s->surface()->frameRendered(QDateTime::currentMSecsSinceEpoch()); } } } void CompositorWindow::keyPressEvent(QKeyEvent *event) { QWidget::keyPressEvent(event); if (!m_seat) { return; } if (!m_seat->focusedKeyboardSurface()) { updateFocus(); } m_seat->setTimestamp(event->timestamp()); m_seat->keyPressed(event->nativeScanCode() - 8); } void CompositorWindow::keyReleaseEvent(QKeyEvent *event) { QWidget::keyReleaseEvent(event); if (!m_seat) { return; } m_seat->setTimestamp(event->timestamp()); m_seat->keyReleased(event->nativeScanCode() - 8); } void CompositorWindow::mouseMoveEvent(QMouseEvent *event) { QWidget::mouseMoveEvent(event); if (!m_seat->focusedPointerSurface()) { updateFocus(); } m_seat->setTimestamp(event->timestamp()); m_seat->setPointerPos(event->localPos().toPoint()); } void CompositorWindow::mousePressEvent(QMouseEvent *event) { QWidget::mousePressEvent(event); if (!m_seat->focusedPointerSurface()) { if (!m_stackingOrder.isEmpty()) { m_seat->setFocusedPointerSurface(m_stackingOrder.last()->surface()); } } m_seat->setTimestamp(event->timestamp()); m_seat->pointerButtonPressed(event->button()); } void CompositorWindow::mouseReleaseEvent(QMouseEvent *event) { QWidget::mouseReleaseEvent(event); m_seat->setTimestamp(event->timestamp()); m_seat->pointerButtonReleased(event->button()); } void CompositorWindow::wheelEvent(QWheelEvent *event) { QWidget::wheelEvent(event); m_seat->setTimestamp(event->timestamp()); const QPoint &angle = event->angleDelta() / (8 * 15); if (angle.x() != 0) { m_seat->pointerAxis(Qt::Horizontal, angle.x()); } if (angle.y() != 0) { m_seat->pointerAxis(Qt::Vertical, angle.y()); } } int main(int argc, char **argv) { using namespace KWayland::Server; QApplication app(argc, argv); QCommandLineParser parser; parser.addHelpOption(); QCommandLineOption xwaylandOption(QStringList{QStringLiteral("x"), QStringLiteral("xwayland")}, QStringLiteral("Start a rootless Xwayland server")); parser.addOption(xwaylandOption); parser.process(app); Display display; display.start(); DataDeviceManagerInterface *ddm = display.createDataDeviceManager(); ddm->create(); CompositorInterface *compositor = display.createCompositor(&display); compositor->create(); ShellInterface *shell = display.createShell(); shell->create(); display.createShm(); OutputInterface *output = display.createOutput(&display); output->setPhysicalSize(QSize(269, 202)); const QSize windowSize(1024, 768); output->addMode(windowSize); output->create(); SeatInterface *seat = display.createSeat(); seat->setHasKeyboard(true); seat->setHasPointer(true); seat->setName(QStringLiteral("testSeat0")); seat->create(); CompositorWindow compositorWindow; compositorWindow.setSeat(seat); compositorWindow.setMinimumSize(windowSize); compositorWindow.setMaximumSize(windowSize); compositorWindow.setGeometry(QRect(QPoint(0, 0), windowSize)); compositorWindow.show(); QObject::connect(shell, &ShellInterface::surfaceCreated, &compositorWindow, &CompositorWindow::surfaceCreated); // start XWayland if (parser.isSet(xwaylandOption)) { // starts XWayland by forking and opening a pipe const int pipe = startXServer(); if (pipe == -1) { exit(1); } QtConcurrent::run([pipe] { readDisplayFromPipe(pipe); }); } return app.exec(); } #include "renderingservertest.moc" diff --git a/tests/shadowtest.cpp b/tests/shadowtest.cpp index 69cfe8a..54f6873 100644 --- a/tests/shadowtest.cpp +++ b/tests/shadowtest.cpp @@ -1,183 +1,169 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shadow.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" // Qt #include #include #include using namespace KWayland::Client; class ShadowTest : public QObject { Q_OBJECT public: explicit ShadowTest(QObject *parent = nullptr); virtual ~ShadowTest(); void init(); private: void setupRegistry(Registry *registry); void setupShadow(); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; ShadowManager *m_shadowManager = nullptr; }; ShadowTest::ShadowTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } ShadowTest::~ShadowTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void ShadowTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void ShadowTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::shadowAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shadowManager = registry->createShadowManager(name, version, this); m_shadowManager->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); setupShadow(); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setToplevel(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &ShadowTest::render); render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void ShadowTest::setupShadow() { Q_ASSERT(m_shadowManager); Shadow *shadow = m_shadowManager->createShadow(m_surface, this); Q_ASSERT(shadow); auto addElement = [shadow, this](const QColor color) { const QSize size = QSize(10, 10); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(color); return buffer; }; shadow->attachTopLeft(addElement(Qt::red)); shadow->attachTop(addElement(Qt::darkRed)); shadow->attachTopRight(addElement(Qt::green)); shadow->attachRight(addElement(Qt::darkGreen)); shadow->attachBottomRight(addElement(Qt::darkBlue)); shadow->attachBottom(addElement(Qt::cyan)); shadow->attachBottomLeft(addElement(Qt::darkCyan)); shadow->attachLeft(addElement(Qt::magenta)); shadow->setOffsets(QMarginsF(5, 5, 5, 5)); shadow->commit(); } void ShadowTest::render() { const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(300, 200); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 128)); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); ShadowTest client; client.init(); return app.exec(); } #include "shadowtest.moc" diff --git a/tests/subsurfacetest.cpp b/tests/subsurfacetest.cpp index dca9081..20b18ce 100644 --- a/tests/subsurfacetest.cpp +++ b/tests/subsurfacetest.cpp @@ -1,209 +1,195 @@ -/******************************************************************** -Copyright 2016 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/datadevice.h" #include "../src/client/datadevicemanager.h" #include "../src/client/dataoffer.h" #include "../src/client/event_queue.h" #include "../src/client/keyboard.h" #include "../src/client/registry.h" #include "../src/client/seat.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/subcompositor.h" #include "../src/client/surface.h" // Qt #include #include #include #include #include #include #include // system #include #include using namespace KWayland::Client; class SubSurfaceTest : public QObject { Q_OBJECT public: explicit SubSurfaceTest(QObject *parent = nullptr); virtual ~SubSurfaceTest(); void init(); private: void setupRegistry(Registry *registry); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; Seat *m_seat = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; SubCompositor *m_subCompositor = nullptr; }; SubSurfaceTest::SubSurfaceTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } SubSurfaceTest::~SubSurfaceTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void SubSurfaceTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void SubSurfaceTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { m_seat = registry->createSeat(name, version, this); } ); connect(registry, &Registry::interfacesAnnounced, this, [this, registry] { Q_ASSERT(m_compositor); Q_ASSERT(m_seat); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setToplevel(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &SubSurfaceTest::render); auto subInterface = registry->interface(Registry::Interface::SubCompositor); if (subInterface.name != 0) { m_subCompositor = registry->createSubCompositor(subInterface.name, subInterface.version, this); Q_ASSERT(m_subCompositor); // create the sub surface auto surface = m_compositor->createSurface(this); Q_ASSERT(surface); auto subsurface = m_subCompositor->createSubSurface(surface, m_surface, this); Q_ASSERT(subsurface); QImage image(QSize(100, 100), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::red); surface->attachBuffer(m_shm->createBuffer(image)); surface->damage(QRect(0, 0, 100, 100)); surface->commit(Surface::CommitFlag::None); // and another sub-surface to the sub-surface auto surface2 = m_compositor->createSurface(this); Q_ASSERT(surface2); auto subsurface2 = m_subCompositor->createSubSurface(surface2, surface, this); Q_ASSERT(subsurface2); QImage green(QSize(50, 50), QImage::Format_ARGB32_Premultiplied); green.fill(Qt::green); surface2->attachBuffer(m_shm->createBuffer(green)); surface2->damage(QRect(0, 0, 50, 50)); surface2->commit(Surface::CommitFlag::None); QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, surface2, [surface2, this] { QImage yellow(QSize(50, 50), QImage::Format_ARGB32_Premultiplied); yellow.fill(Qt::yellow); surface2->attachBuffer(m_shm->createBuffer(yellow)); surface2->damage(QRect(0, 0, 50, 50)); surface2->commit(Surface::CommitFlag::None); m_surface->commit(Surface::CommitFlag::None); } ); timer->setSingleShot(true); timer->start(5000); } render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void SubSurfaceTest::render() { const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(200, 200); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(Qt::blue); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); SubSurfaceTest client; client.init(); return app.exec(); } #include "subsurfacetest.moc" diff --git a/tests/touchclienttest.cpp b/tests/touchclienttest.cpp index fb9e4a0..df4fe2e 100644 --- a/tests/touchclienttest.cpp +++ b/tests/touchclienttest.cpp @@ -1,258 +1,244 @@ -/******************************************************************** -Copyright 2014, 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014, 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "touchclienttest.h" // KWin::Wayland #include <../src/client/buffer.h> #include <../src/client/compositor.h> #include <../src/client/connection_thread.h> #include <../src/client/event_queue.h> #include <../src/client/keyboard.h> #include <../src/client/output.h> #include <../src/client/pointer.h> #include <../src/client/registry.h> #include <../src/client/seat.h> #include <../src/client/shell.h> #include <../src/client/shm_pool.h> #include <../src/client/surface.h> #include <../src/client/touch.h> // Qt #include #include #include #include #include #include #include #include using namespace KWayland::Client; static Qt::GlobalColor s_colors[] = { Qt::white, Qt::red, Qt::green, Qt::blue, Qt::black }; static int s_colorIndex = 0; WaylandClientTest::WaylandClientTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread(nullptr)) , m_eventQueue(nullptr) , m_compositor(nullptr) , m_output(nullptr) , m_surface(nullptr) , m_shm(nullptr) , m_timer(new QTimer(this)) { init(); } WaylandClientTest::~WaylandClientTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void WaylandClientTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this]() { // create the event queue for the main gui thread m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); // setup registry Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); connect(m_timer, &QTimer::timeout, this, [this]() { s_colorIndex = (s_colorIndex + 1) % 5; render(); } ); m_timer->setInterval(1000); m_timer->start(); } void WaylandClientTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name) { m_compositor = registry->createCompositor(name, 1, this); m_surface = m_compositor->createSurface(this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name) { Shell *shell = registry->createShell(name, 1, this); ShellSurface *shellSurface = shell->createSurface(m_surface, m_surface); shellSurface->setToplevel(); render(QSize(400, 200)); } ); connect(registry, &Registry::outputAnnounced, this, [this, registry](quint32 name) { if (m_output) { return; } m_output = registry->createOutput(name, 2, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name) { m_shm = registry->createShmPool(name, 1, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name) { Seat *s = registry->createSeat(name, 2, this); connect(s, &Seat::hasKeyboardChanged, this, [this, s](bool has) { if (!has) { return; } Keyboard *k = s->createKeyboard(this); connect(k, &Keyboard::keyChanged, this, [this](quint32 key, Keyboard::KeyState state) { if (key == KEY_Q && state == Keyboard::KeyState::Released) { QCoreApplication::instance()->quit(); } } ); } ); connect(s, &Seat::hasPointerChanged, this, [this, s](bool has) { if (!has) { return; } Pointer *p = s->createPointer(this); connect(p, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { Q_UNUSED(serial) Q_UNUSED(time) if (state == Pointer::ButtonState::Released) { if (button == BTN_LEFT) { if (m_timer->isActive()) { m_timer->stop(); } else { m_timer->start(); } } if (button == BTN_RIGHT) { QCoreApplication::instance()->quit(); } } } ); } ); connect(s, &Seat::hasTouchChanged, this, [this, s](bool has) { if (!has) { return; } Touch *t = s->createTouch(this); connect(t, &Touch::sequenceStarted, this, [] (KWayland::Client::TouchPoint *startPoint) { qDebug() << "Touch sequence started at" << startPoint->position() << "with id" << startPoint->id(); } ); connect(t, &Touch::sequenceCanceled, this, [] () { qDebug() << "Touch sequence canceled"; } ); connect(t, &Touch::sequenceEnded, this, [] () { qDebug() << "Touch sequence finished"; } ); connect(t, &Touch::frameEnded, this, [] () { qDebug() << "End of touch contact point list"; } ); connect(t, &Touch::pointAdded, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point added at" << point->position() << "with id" << point->id(); } ); connect(t, &Touch::pointRemoved, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point " << point->id() << " removed at" << point->position(); } ); connect(t, &Touch::pointMoved, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point " << point->id() << " moved to" << point->position(); } ); } ); } ); registry->create(m_connectionThreadObject->display()); registry->setEventQueue(m_eventQueue); registry->setup(); } void WaylandClientTest::render(const QSize &size) { m_currentSize = size; render(); } void WaylandClientTest::render() { if (!m_shm || !m_surface || !m_surface->isValid() || !m_currentSize.isValid()) { return; } auto buffer = m_shm->getBuffer(m_currentSize, m_currentSize.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), m_currentSize.width(), m_currentSize.height(), QImage::Format_ARGB32_Premultiplied); image.fill(s_colors[s_colorIndex]); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), m_currentSize)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); new WaylandClientTest(&app); return app.exec(); } diff --git a/tests/touchclienttest.h b/tests/touchclienttest.h index 48f424b..35f4bcc 100644 --- a/tests/touchclienttest.h +++ b/tests/touchclienttest.h @@ -1,67 +1,53 @@ -/******************************************************************** -Copyright 2014, 2015 Martin Gräßlin +/* + SPDX-FileCopyrightText: 2014, 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #ifndef TOUCHCLIENTTEST_H #define TOUCHCLIENTTEST_H #include #include namespace KWayland { namespace Client { class Compositor; class ConnectionThread; class EventQueue; class Output; class Registry; class ShmPool; class Surface; } } class QThread; class QTimer; class WaylandClientTest : public QObject { Q_OBJECT public: explicit WaylandClientTest(QObject *parent = nullptr); virtual ~WaylandClientTest(); private: void init(); void render(const QSize &size); void render(); void setupRegistry(KWayland::Client::Registry *registry); void toggleTimer(); QThread *m_connectionThread; KWayland::Client::ConnectionThread *m_connectionThreadObject; KWayland::Client::EventQueue *m_eventQueue; KWayland::Client::Compositor *m_compositor; KWayland::Client::Output *m_output; KWayland::Client::Surface *m_surface; KWayland::Client::ShmPool *m_shm; QSize m_currentSize; QTimer *m_timer; }; #endif diff --git a/tests/waylandservertest.cpp b/tests/waylandservertest.cpp index 9a669aa..ba8e18a 100644 --- a/tests/waylandservertest.cpp +++ b/tests/waylandservertest.cpp @@ -1,127 +1,113 @@ -/******************************************************************** -Copyright 2014 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2014 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/server/compositor_interface.h" #include "../src/server/display.h" #include "../src/server/output_interface.h" #include "../src/server/seat_interface.h" #include "../src/server/shell_interface.h" #include #include #include #include #include static int startXServer() { const QByteArray process = QByteArrayLiteral("Xwayland"); int pipeFds[2]; if (pipe(pipeFds) != 0) { std::cerr << "FATAL ERROR failed to create pipe to start X Server " << process.constData() << std::endl; exit(1); } pid_t pid = fork(); if (pid == 0) { // child process - should be turned into Xwayland // writes to pipe, closes read side close(pipeFds[0]); char fdbuf[16]; sprintf(fdbuf, "%d", pipeFds[1]); execlp(process.constData(), process.constData(), "-displayfd", fdbuf, (char *)nullptr); close(pipeFds[1]); exit(20); } // parent process - this is the wayland server // reads from pipe, closes write side close(pipeFds[1]); return pipeFds[0]; } static void readDisplayFromPipe(int pipe) { QFile readPipe; if (!readPipe.open(pipe, QIODevice::ReadOnly)) { std::cerr << "FATAL ERROR failed to open pipe to start X Server XWayland" << std::endl; exit(1); } QByteArray displayNumber = readPipe.readLine(); displayNumber.prepend(QByteArray(":")); displayNumber.remove(displayNumber.size() -1, 1); std::cout << "X-Server started on display " << displayNumber.constData() << std::endl; setenv("DISPLAY", displayNumber.constData(), true); // close our pipe close(pipe); } int main(int argc, char **argv) { using namespace KWayland::Server; // set our own event dispatcher to be able to dispatch events before the event loop is started QAbstractEventDispatcher *eventDispatcher = new QEventDispatcherGlib(); QCoreApplication::setEventDispatcher(eventDispatcher); // first create the Server and setup with minimum to get an XWayland connected Display display; display.start(); display.createShm(); CompositorInterface *compositor = display.createCompositor(&display); compositor->create(); ShellInterface *shell = display.createShell(); shell->create(); OutputInterface *output = display.createOutput(&display); output->setPhysicalSize(QSize(10, 10)); output->addMode(QSize(1024, 768)); output->create(); // starts XWayland by forking and opening a pipe const int pipe = startXServer(); if (pipe == -1) { exit(1); } fd_set rfds; struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; do { eventDispatcher->processEvents(QEventLoop::WaitForMoreEvents); FD_ZERO(&rfds); FD_SET(pipe, &rfds); } while (select(pipe + 1, &rfds, nullptr, nullptr, &tv) == 0); // now Xwayland is ready and we can read the pipe to get the display readDisplayFromPipe(pipe); QGuiApplication app(argc, argv); SeatInterface *seat = display.createSeat(); seat->setName(QStringLiteral("testSeat0")); seat->create(); return app.exec(); } diff --git a/tests/xdgforeigntest.cpp b/tests/xdgforeigntest.cpp index 6101408..22a4309 100644 --- a/tests/xdgforeigntest.cpp +++ b/tests/xdgforeigntest.cpp @@ -1,208 +1,194 @@ -/******************************************************************** -Copyright 2016 Martin Gräßlin -Copyright 2017 Marco Martin - -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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2016 Martin Gräßlin + SPDX-FileCopyrightText: 2017 Marco Martin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/server_decoration.h" #include "../src/client/xdgshell.h" #include "../src/client/xdgforeign.h" // Qt #include #include #include #include #include using namespace KWayland::Client; class XdgForeignTest : public QObject { Q_OBJECT public: explicit XdgForeignTest(QObject *parent = nullptr); virtual ~XdgForeignTest(); void init(); private: void setupRegistry(Registry *registry); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; XdgShell *m_shell = nullptr; XdgShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; XdgShellSurface *m_childShellSurface = nullptr; Surface *m_childSurface = nullptr; KWayland::Client::XdgExporter *m_exporter = nullptr; KWayland::Client::XdgImporter *m_importer = nullptr; KWayland::Client::XdgExported *m_exported = nullptr; KWayland::Client::XdgImported *m_imported = nullptr; KWayland::Client::ServerSideDecorationManager *m_decoration = nullptr; }; XdgForeignTest::XdgForeignTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } XdgForeignTest::~XdgForeignTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void XdgForeignTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void XdgForeignTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::xdgShellUnstableV5Announced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createXdgShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::exporterUnstableV2Announced, this, [this, registry](quint32 name, quint32 version) { m_exporter = registry->createXdgExporter(name, version, this); m_exporter->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::importerUnstableV2Announced, this, [this, registry](quint32 name, quint32 version) { m_importer = registry->createXdgImporter(name, version, this); m_importer->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::serverSideDecorationManagerAnnounced, this, [this, registry](quint32 name, quint32 version) { m_decoration = registry->createServerSideDecorationManager(name, version, this); m_decoration->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_shell); Q_ASSERT(m_shm); Q_ASSERT(m_exporter); Q_ASSERT(m_importer); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); auto parentDeco = m_decoration->create(m_surface, this); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); connect(m_shellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); m_childSurface = m_compositor->createSurface(this); Q_ASSERT(m_childSurface); auto childDeco = m_decoration->create(m_childSurface, this); m_childShellSurface = m_shell->createSurface(m_childSurface, this); Q_ASSERT(m_childShellSurface); connect(m_childShellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); m_exported = m_exporter->exportTopLevel(m_surface, this); Q_ASSERT(m_decoration); connect(m_exported, &XdgExported::done, this, [this]() { m_imported = m_importer->importTopLevel(m_exported->handle(), this); m_imported->setParentOf(m_childSurface); }); render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void XdgForeignTest::render() { QSize size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(500, 500); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 255)); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); - + size = m_childShellSurface->size().isValid() ? m_childShellSurface->size() : QSize(200, 200); buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); image = QImage(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 0, 0, 255)); m_childSurface->attachBuffer(*buffer); m_childSurface->damage(QRect(QPoint(0, 0), size)); m_childSurface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); XdgForeignTest client; client.init(); return app.exec(); } #include "xdgforeigntest.moc" diff --git a/tests/xdgtest.cpp b/tests/xdgtest.cpp index 3fa0f87..c85ef4d 100644 --- a/tests/xdgtest.cpp +++ b/tests/xdgtest.cpp @@ -1,227 +1,213 @@ -/******************************************************************** -Copyright 2015 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) 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 6 of version 3 of the license. - -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, see . -*********************************************************************/ +/* + SPDX-FileCopyrightText: 2015 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shadow.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" #include "../src/client/xdgshell.h" #include "../src/client/pointer.h" #include "../src/client/seat.h" // Qt #include #include #include #include using namespace KWayland::Client; class XdgTest : public QObject { Q_OBJECT public: explicit XdgTest(QObject *parent = nullptr); virtual ~XdgTest(); void init(); private: void setupRegistry(Registry *registry); void createPopup(); void render(); void renderPopup(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; XdgShell *m_xdgShell = nullptr; XdgShellSurface *m_xdgShellSurface = nullptr; Surface *m_popupSurface = nullptr; XdgShellPopup *m_xdgShellPopup = nullptr; }; XdgTest::XdgTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } XdgTest::~XdgTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void XdgTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void XdgTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::xdgShellUnstableV6Announced, this, [this, registry](quint32 name, quint32 version) { m_xdgShell = registry->createXdgShell(name, version, this); m_xdgShell->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_xdgShell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); m_xdgShellSurface = m_xdgShell->createSurface(m_surface, this); Q_ASSERT(m_xdgShellSurface); connect(m_xdgShellSurface, &XdgShellSurface::configureRequested, this, [this](const QSize &size, KWayland::Client::XdgShellSurface::States states, int serial) { Q_UNUSED(size); Q_UNUSED(states); m_xdgShellSurface->ackConfigure(serial); render(); }); m_xdgShellSurface->setTitle(QStringLiteral("Test Window")); m_surface->commit(); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name) { Seat *s = registry->createSeat(name, 2, this); connect(s, &Seat::hasPointerChanged, this, [this, s](bool has) { if (!has) { return; } Pointer *p = s->createPointer(this); connect(p, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { Q_UNUSED(button) Q_UNUSED(serial) Q_UNUSED(time) if (state == Pointer::ButtonState::Released) { if (m_popupSurface) { m_popupSurface->deleteLater(); m_popupSurface = nullptr; } else { createPopup(); } } } ); } ); }); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void XdgTest::createPopup() { if (m_popupSurface) { m_popupSurface->deleteLater(); } m_popupSurface = m_compositor->createSurface(this); XdgPositioner positioner(QSize(200,200), QRect(50, 50, 400, 400)); positioner.setAnchorEdge(Qt::BottomEdge | Qt::RightEdge); positioner.setGravity(Qt::BottomEdge); positioner.setConstraints(XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::SlideY); m_xdgShellPopup = m_xdgShell->createPopup(m_popupSurface, m_xdgShellSurface, positioner, m_popupSurface); renderPopup(); } void XdgTest::render() { const QSize &size = m_xdgShellSurface->size().isValid() ? m_xdgShellSurface->size() : QSize(500, 500); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 255)); //draw a red rectangle indicating the anchor of the top level QPainter painter(&image); painter.setBrush(Qt::red); painter.setPen(Qt::black); painter.drawRect(50, 50, 400, 400); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } void XdgTest::renderPopup() { QSize size(200,200); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(0, 0, 255, 255)); m_popupSurface->attachBuffer(*buffer); m_popupSurface->damage(QRect(QPoint(0, 0), size)); m_popupSurface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); XdgTest client; client.init(); return app.exec(); } #include "xdgtest.moc"