├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── Modules │ ├── Findkwineffects.cmake │ └── PkgConfigGetVar.cmake ├── doc └── Screenshot.png └── src ├── CMakeLists.txt ├── Model.cc ├── Model.h ├── YetAnotherMagicLampConfig.kcfgc ├── YetAnotherMagicLampEffect.cc ├── YetAnotherMagicLampEffect.h ├── common.h ├── kcm ├── CMakeLists.txt ├── YetAnotherMagicLampEffectKCM.cc ├── YetAnotherMagicLampEffectKCM.h ├── YetAnotherMagicLampEffectKCM.ui ├── metadata.json └── plugin.cc ├── metadata.json ├── options.kcfg └── plugin.cc /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: false 10 | AlignTrailingComments: false 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: MultiLine 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: All 40 | BreakBeforeBraces: WebKit 41 | BreakBeforeInheritanceComma: false 42 | BreakInheritanceList: BeforeColon 43 | BreakBeforeTernaryOperators: true 44 | BreakConstructorInitializersBeforeComma: false 45 | BreakConstructorInitializers: BeforeComma 46 | BreakAfterJavaFieldAnnotations: false 47 | BreakStringLiterals: true 48 | ColumnLimit: 0 49 | CommentPragmas: '^ IWYU pragma:' 50 | CompactNamespaces: false 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 52 | ConstructorInitializerIndentWidth: 4 53 | ContinuationIndentWidth: 4 54 | Cpp11BracedListStyle: false 55 | DerivePointerAlignment: false 56 | DisableFormat: false 57 | ExperimentalAutoDetectBinPacking: false 58 | FixNamespaceComments: false 59 | ForEachMacros: 60 | - foreach 61 | - Q_FOREACH 62 | - BOOST_FOREACH 63 | IncludeBlocks: Preserve 64 | IncludeCategories: 65 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 66 | Priority: 2 67 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 68 | Priority: 3 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentPPDirectives: None 74 | IndentWidth: 4 75 | IndentWrappedFunctionNames: false 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: true 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 1 82 | NamespaceIndentation: Inner 83 | ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 4 85 | ObjCSpaceAfterProperty: true 86 | ObjCSpaceBeforeProtocolList: true 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 19 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyBreakTemplateDeclaration: 10 93 | PenaltyExcessCharacter: 1000000 94 | PenaltyReturnTypeOnItsOwnLine: 60 95 | PointerAlignment: Left 96 | ReflowComments: true 97 | SortIncludes: true 98 | SortUsingDeclarations: true 99 | SpaceAfterCStyleCast: false 100 | SpaceAfterTemplateKeyword: true 101 | SpaceBeforeAssignmentOperators: true 102 | SpaceBeforeCpp11BracedList: true 103 | SpaceBeforeCtorInitializerColon: true 104 | SpaceBeforeInheritanceColon: true 105 | SpaceBeforeParens: ControlStatements 106 | SpaceBeforeRangeBasedForLoopColon: true 107 | SpaceInEmptyParentheses: false 108 | SpacesBeforeTrailingComments: 1 109 | SpacesInAngles: false 110 | SpacesInContainerLiterals: true 111 | SpacesInCStyleCastParentheses: false 112 | SpacesInParentheses: false 113 | SpacesInSquareBrackets: false 114 | Standard: Cpp11 115 | TabWidth: 8 116 | UseTab: Never 117 | ... 118 | 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /build 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(kwin-effects-yaml) 3 | 4 | set(KF_MIN_VERSION "5.78") 5 | 6 | set(CMAKE_CXX_STANDARD 20) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) 11 | set(CMAKE_MODULE_PATH 12 | ${CMAKE_MODULE_PATH} 13 | ${ECM_MODULE_PATH} 14 | ${ECM_KDE_MODULE_DIR} 15 | ${CMAKE_SOURCE_DIR}/cmake 16 | ${CMAKE_SOURCE_DIR}/cmake/Modules 17 | ) 18 | 19 | include(FeatureSummary) 20 | include(KDEInstallDirs) 21 | include(KDECMakeSettings) 22 | include(KDECompilerSettings NO_POLICY_SCOPE) 23 | 24 | find_package(Qt5 REQUIRED COMPONENTS 25 | Core 26 | DBus 27 | Gui 28 | ) 29 | 30 | find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS 31 | Config 32 | ConfigWidgets 33 | CoreAddons 34 | WindowSystem 35 | ) 36 | 37 | find_package(kwineffects REQUIRED COMPONENTS 38 | kwineffects 39 | kwinglutils 40 | ) 41 | 42 | find_package(KWinDBusInterface CONFIG REQUIRED) 43 | 44 | find_package(epoxy REQUIRED) 45 | 46 | add_subdirectory(src) 47 | 48 | feature_summary(WHAT ALL) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yet Another Magic Lamp 2 | 3 | ![Screenshot](doc/Screenshot.png) 4 | 5 | [Demo](https://www.youtube.com/watch?v=BR4bUwFZDS8) 6 | 7 | Yet Another Magic Lamp is a window minimization effect for KWin. Whenever a window 8 | is minimized, it'll get sucked down into the dock/panel. The main difference 9 | between this effect and the one shipped with KWin is that this effect is more 10 | "curvy". In addition to that, this effect works correctly with weird setups 11 | (e.g. the panel is between screens) and has more configuration options. 12 | 13 | This is mostly eye-candy stuff. If you want to be more productive, consider 14 | using another window minimize animation (e.g. [Scale](https://store.kde.org/p/1267839/), etc). 15 | 16 | ### Installation 17 | 18 | #### Binary package 19 | 20 | On openSUSE Tumbleweed 21 | 22 | ```sh 23 | sudo zypper ar obs://home:trmdi trmdi 24 | sudo zypper in -r trmdi kwin-effects-yaml 25 | ``` 26 | 27 | #### Build from source 28 | 29 | You will need the following dependencies to build this effect: 30 | * CMake 31 | * any C++20 enabled compiler 32 | * Qt 33 | * libkwineffects 34 | * KDE Frameworks 5: 35 | - Config 36 | - CoreAddons 37 | - Extra CMake Modules 38 | - WindowSystem 39 | 40 | On Arch Linux 41 | 42 | ```sh 43 | sudo pacman -S cmake extra-cmake-modules kwin 44 | ``` 45 | 46 | On Fedora 47 | 48 | ```sh 49 | sudo dnf install cmake extra-cmake-modules kf5-kconfig-devel \ 50 | kf5-kcoreaddons-devel kf5-kwindowsystem-devel kwin-devel \ 51 | qt5-qtbase-devel libepoxy-devel kf5-kconfigwidgets-devel 52 | ``` 53 | 54 | On Ubuntu 55 | 56 | ```sh 57 | sudo apt install cmake extra-cmake-modules kwin-dev \ 58 | libkf5config-dev libkf5configwidgets-dev libkf5coreaddons-dev \ 59 | libkf5windowsystem-dev qtbase5-dev 60 | ``` 61 | 62 | After you installed all the required dependencies, you can build 63 | the effect: 64 | 65 | ```sh 66 | git clone https://github.com/zzag/kwin-effects-yet-another-magic-lamp.git 67 | cd kwin-effects-yet-another-magic-lamp 68 | git checkout Plasma/5.23 69 | mkdir build && cd build 70 | cmake .. \ 71 | -DCMAKE_BUILD_TYPE=Release \ 72 | -DCMAKE_INSTALL_PREFIX=/usr 73 | make 74 | sudo make install 75 | ``` 76 | 77 | 78 | #### Building the effect against older Plasma versions 79 | 80 | If you want to build this effect against an older Plasma release, checkout 81 | the corresponding `Plasma/x.yz` branch, for example 82 | 83 | ```sh 84 | git clone https://github.com/zzag/kwin-effects-yet-another-magic-lamp.git 85 | cd kwin-effects-yet-another-magic-lamp 86 | git checkout Plasma/5.20 87 | ``` 88 | 89 | 90 | ### Using the effect 91 | 92 | Go to System Settings > Desktop Behavior > Desktop Effects, and select 93 | "Yet Another Magic Lamp", then click Apply. 94 | 95 | ### Contributing 96 | 97 | Any help is welcome. If you have suggestions how to improve this effect(e.g. 98 | different duration for each stage of the animation, etc), please create a new 99 | issue. If you'd like to contribute by implementing some feature, make sure 100 | you've run clang-format before creating a PR. 101 | -------------------------------------------------------------------------------- /cmake/Modules/Findkwineffects.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # Findkwineffects 3 | # --------------- 4 | # 5 | # Try to find libkwineffects. 6 | # 7 | # This is a component-based find module, which makes use of the COMPONENTS 8 | # argument to find_modules. The following components are 9 | # available:: 10 | # 11 | # kwineffects 12 | # kwinglutils 13 | # kwinxrenderutils 14 | # 15 | # If no components are specified, this module will act as though all components 16 | # were passed to OPTIONAL_COMPONENTS. 17 | # 18 | # This module will define the following variables, independently of the 19 | # components searched for or found: 20 | # 21 | # ``kwineffects_FOUND`` 22 | # True if (the requestion version of) libkwineffects is available 23 | # ``kwineffects_TARGETS`` 24 | # A list of all targets imported by this module (note that there may be more 25 | # than the components that were requested) 26 | # ``kwineffects_LIBRARIES`` 27 | # This can be passed to target_link_libraries() instead of the imported 28 | # targets 29 | # ``kwineffects_INCLUDE_DIRS`` 30 | # This should be passed to target_include_directories() if the targets are 31 | # not used for linking 32 | # ``kwineffects_DEFINITIONS`` 33 | # This should be passed to target_compile_options() if the targets are not 34 | # used for linking 35 | # 36 | # For each searched-for components, ``kwineffects__FOUND`` will be 37 | # set to true if the corresponding libkwineffects library was found, and false 38 | # otherwise. If ``kwineffects__FOUND`` is true, the imported target 39 | # ``kwineffects::`` will be defined. 40 | 41 | #============================================================================= 42 | # Copyright 2019 Vlad Zagorodniy 43 | # 44 | # Redistribution and use in source and binary forms, with or without 45 | # modification, are permitted provided that the following conditions 46 | # are met: 47 | # 48 | # 1. Redistributions of source code must retain the copyright 49 | # notice, this list of conditions and the following disclaimer. 50 | # 2. Redistributions in binary form must reproduce the copyright 51 | # notice, this list of conditions and the following disclaimer in the 52 | # documentation and/or other materials provided with the distribution. 53 | # 3. The name of the author may not be used to endorse or promote products 54 | # derived from this software without specific prior written permission. 55 | # 56 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 57 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 58 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 59 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 60 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 61 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 62 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 63 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 64 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 65 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 66 | #============================================================================= 67 | 68 | include(ECMFindModuleHelpers) 69 | ecm_find_package_version_check(kwineffects) 70 | 71 | set(kwineffects_known_components 72 | kwineffects 73 | kwinglutils 74 | kwinxrenderutils 75 | ) 76 | set(kwineffects_default_components ${kwineffects_known_components}) 77 | 78 | set(kwineffects_kwineffects_header "kwineffects.h") 79 | set(kwineffects_kwineffects_lib "kwineffects") 80 | set(kwineffects_kwinglutils_header "kwinglutils.h") 81 | set(kwineffects_kwinglutils_lib "kwinglutils") 82 | set(kwineffects_kwinxrenderutils_header "kwinxrenderutils.h") 83 | set(kwineffects_kwinxrenderutils_lib "kwinxrenderutils") 84 | 85 | ecm_find_package_parse_components(kwineffects 86 | RESULT_VAR kwineffects_components 87 | KNOWN_COMPONENTS ${kwineffects_known_components} 88 | DEFAULT_COMPONENTS ${kwineffects_default_components} 89 | ) 90 | 91 | ecm_find_package_handle_library_components(kwineffects 92 | COMPONENTS ${kwineffects_components} 93 | ) 94 | 95 | find_package_handle_standard_args(kwineffects 96 | FOUND_VAR 97 | kwineffects_FOUND 98 | REQUIRED_VARS 99 | kwineffects_LIBRARIES 100 | HANDLE_COMPONENTS 101 | ) 102 | -------------------------------------------------------------------------------- /cmake/Modules/PkgConfigGetVar.cmake: -------------------------------------------------------------------------------- 1 | include (UsePkgConfig) 2 | 3 | macro (pkgconfig_getvar _package _var _output_variable) 4 | SET (${_output_variable}) 5 | 6 | if (PKGCONFIG_EXECUTABLE) 7 | exec_program (${PKGCONFIG_EXECUTABLE} 8 | ARGS ${_package} --exists 9 | RETURN_VALUE _return_VALUE 10 | OUTPUT_VARIABLE _pkgconfigDevNull 11 | ) 12 | 13 | if (NOT _return_VALUE) 14 | exec_program (${PKGCONFIG_EXECUTABLE} 15 | ARGS ${_package} --variable ${_var} 16 | OUTPUT_VARIABLE ${_output_variable} 17 | ) 18 | endif () 19 | endif () 20 | endmacro () 21 | -------------------------------------------------------------------------------- /doc/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzag/kwin-effects-yet-another-magic-lamp/f3bc2e8488e2d6e9f1afbc435b1054e7fc00218c/doc/Screenshot.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(kcm) 2 | 3 | set(effect_SRCS 4 | Model.cc 5 | YetAnotherMagicLampEffect.cc 6 | plugin.cc 7 | ) 8 | 9 | kconfig_add_kcfg_files(effect_SRCS 10 | YetAnotherMagicLampConfig.kcfgc 11 | ) 12 | 13 | add_library(kwin4_effect_yetanothermagiclamp SHARED ${effect_SRCS}) 14 | 15 | target_link_libraries(kwin4_effect_yetanothermagiclamp 16 | Qt5::Core 17 | Qt5::Gui 18 | KF5::ConfigCore 19 | KF5::ConfigGui 20 | KF5::CoreAddons 21 | KF5::WindowSystem 22 | kwineffects::kwineffects 23 | kwineffects::kwinglutils 24 | epoxy::epoxy 25 | ) 26 | 27 | install( 28 | TARGETS 29 | kwin4_effect_yetanothermagiclamp 30 | 31 | DESTINATION 32 | ${PLUGIN_INSTALL_DIR}/kwin/effects/plugins/ 33 | ) 34 | -------------------------------------------------------------------------------- /src/Model.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Own 19 | #include "Model.h" 20 | 21 | static inline std::chrono::milliseconds durationFraction(std::chrono::milliseconds duration, qreal fraction) 22 | { 23 | return std::chrono::milliseconds(qMax(qRound(duration.count() * fraction), 1)); 24 | } 25 | 26 | Model::Model(KWin::EffectWindow* window) 27 | : m_window(window) 28 | { 29 | } 30 | 31 | static KWin::EffectWindow* findDock(const KWin::EffectWindow* client) 32 | { 33 | const KWin::EffectWindowList windows = KWin::effects->stackingOrder(); 34 | 35 | for (KWin::EffectWindow* window : windows) { 36 | if (!window->isDock()) 37 | continue; 38 | 39 | if (!window->frameGeometry().intersects(client->iconGeometry())) 40 | continue; 41 | 42 | return window; 43 | } 44 | 45 | return nullptr; 46 | } 47 | 48 | static Direction realizeDirection(const KWin::EffectWindow* window) 49 | { 50 | const KWin::EffectWindow* dock = findDock(window); 51 | 52 | Direction direction; 53 | QPointF screenDelta; 54 | 55 | if (dock) { 56 | const QRectF screenRect = KWin::effects->clientArea(KWin::ScreenArea, dock); 57 | 58 | if (dock->width() >= dock->height()) { 59 | if (qFuzzyIsNull(dock->y() - screenRect.y())) 60 | direction = Direction::Top; 61 | else 62 | direction = Direction::Bottom; 63 | } else { 64 | if (qFuzzyIsNull(dock->x() - screenRect.x())) 65 | direction = Direction::Left; 66 | else 67 | direction = Direction::Right; 68 | } 69 | 70 | screenDelta += screenRect.center(); 71 | } else { 72 | // Perhaps the dock is hidden, deduce direction to the icon. 73 | const QRectF iconRect = window->iconGeometry(); 74 | 75 | const KWin::EffectScreen* screen = KWin::effects->screenAt(iconRect.center().toPoint()); 76 | const int desktop = KWin::effects->currentDesktop(); 77 | 78 | const QRectF screenRect = KWin::effects->clientArea(KWin::ScreenArea, screen, desktop); 79 | const QRectF constrainedRect = screenRect.intersected(iconRect); 80 | 81 | if (qFuzzyIsNull(constrainedRect.left() - screenRect.left())) 82 | direction = Direction::Left; 83 | else if (qFuzzyIsNull(constrainedRect.top() - screenRect.top())) 84 | direction = Direction::Top; 85 | else if (qFuzzyIsNull(constrainedRect.right() - screenRect.right())) 86 | direction = Direction::Right; 87 | else 88 | direction = Direction::Bottom; 89 | 90 | screenDelta += screenRect.center(); 91 | } 92 | 93 | const QRectF screenRect = KWin::effects->clientArea(KWin::ScreenArea, window); 94 | screenDelta -= screenRect.center(); 95 | 96 | // Dock and window are on the same screen, no further adjustments are required. 97 | if (screenDelta.isNull()) 98 | return direction; 99 | 100 | const int safetyMargin = 100; 101 | 102 | switch (direction) { 103 | case Direction::Top: 104 | case Direction::Bottom: 105 | // Approach the icon along horizontal direction. 106 | if (qAbs(screenDelta.x()) - qAbs(screenDelta.y()) > safetyMargin) 107 | return direction; 108 | 109 | // Approach the icon from bottom. 110 | if (screenDelta.y() < 0) 111 | return Direction::Top; 112 | 113 | // Approach the icon from top. 114 | return Direction::Bottom; 115 | 116 | case Direction::Left: 117 | case Direction::Right: 118 | // Approach the icon along vertical direction. 119 | if (qAbs(screenDelta.y()) - qAbs(screenDelta.x()) > safetyMargin) 120 | return direction; 121 | 122 | // Approach the icon from right side. 123 | if (screenDelta.x() < 0) 124 | return Direction::Left; 125 | 126 | // Approach the icon from left side. 127 | return Direction::Right; 128 | 129 | default: 130 | Q_UNREACHABLE(); 131 | } 132 | } 133 | 134 | void Model::start(AnimationKind kind) 135 | { 136 | m_kind = kind; 137 | 138 | if (m_timeLine.running()) { 139 | m_timeLine.toggleDirection(); 140 | return; 141 | } 142 | 143 | m_direction = realizeDirection(m_window); 144 | m_bumpDistance = computeBumpDistance(); 145 | m_shapeFactor = computeShapeFactor(); 146 | 147 | switch (m_kind) { 148 | case AnimationKind::Minimize: 149 | if (m_bumpDistance != 0) { 150 | m_stage = AnimationStage::Bump; 151 | m_timeLine.reset(); 152 | m_timeLine.setDirection(KWin::TimeLine::Forward); 153 | m_timeLine.setDuration(m_parameters.bumpDuration); 154 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 155 | m_clip = false; 156 | } else { 157 | m_stage = AnimationStage::Stretch1; 158 | m_timeLine.reset(); 159 | m_timeLine.setDirection(KWin::TimeLine::Forward); 160 | m_timeLine.setDuration( 161 | durationFraction(m_parameters.stretchDuration, m_shapeFactor)); 162 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 163 | m_clip = true; 164 | } 165 | break; 166 | 167 | case AnimationKind::Unminimize: 168 | m_stage = AnimationStage::Squash; 169 | m_timeLine.reset(); 170 | m_timeLine.setDirection(KWin::TimeLine::Backward); 171 | m_timeLine.setDuration(m_parameters.squashDuration); 172 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 173 | m_clip = true; 174 | break; 175 | 176 | default: 177 | Q_UNREACHABLE(); 178 | } 179 | } 180 | 181 | void Model::advance(std::chrono::milliseconds presentTime) 182 | { 183 | m_timeLine.advance(presentTime); 184 | if (!m_timeLine.done()) { 185 | return; 186 | } 187 | 188 | switch (m_kind) { 189 | case AnimationKind::Minimize: 190 | updateMinimizeStage(); 191 | break; 192 | 193 | case AnimationKind::Unminimize: 194 | updateUnminimizeStage(); 195 | break; 196 | 197 | default: 198 | Q_UNREACHABLE(); 199 | } 200 | } 201 | 202 | void Model::updateMinimizeStage() 203 | { 204 | switch (m_stage) { 205 | case AnimationStage::Bump: 206 | m_stage = AnimationStage::Stretch1; 207 | m_timeLine.reset(); 208 | m_timeLine.setDirection(KWin::TimeLine::Forward); 209 | m_timeLine.setDuration( 210 | durationFraction(m_parameters.stretchDuration, m_shapeFactor)); 211 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 212 | m_clip = true; 213 | m_done = false; 214 | return; 215 | 216 | case AnimationStage::Stretch1: 217 | case AnimationStage::Stretch2: 218 | m_stage = AnimationStage::Squash; 219 | m_timeLine.reset(); 220 | m_timeLine.setDirection(KWin::TimeLine::Forward); 221 | m_timeLine.setDuration(m_parameters.squashDuration); 222 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 223 | m_clip = true; 224 | m_done = false; 225 | return; 226 | 227 | case AnimationStage::Squash: 228 | m_done = true; 229 | return; 230 | 231 | default: 232 | Q_UNREACHABLE(); 233 | } 234 | } 235 | 236 | void Model::updateUnminimizeStage() 237 | { 238 | switch (m_stage) { 239 | case AnimationStage::Bump: 240 | m_done = true; 241 | return; 242 | 243 | case AnimationStage::Stretch1: 244 | if (m_bumpDistance == 0) { 245 | m_done = true; 246 | return; 247 | } 248 | m_stage = AnimationStage::Bump; 249 | m_timeLine.reset(); 250 | m_timeLine.setDirection(KWin::TimeLine::Backward); 251 | m_timeLine.setDuration(m_parameters.bumpDuration); 252 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 253 | m_clip = false; 254 | m_done = false; 255 | return; 256 | 257 | case AnimationStage::Stretch2: 258 | m_done = true; 259 | return; 260 | 261 | case AnimationStage::Squash: 262 | m_stage = AnimationStage::Stretch2; 263 | m_timeLine.reset(); 264 | m_timeLine.setDirection(KWin::TimeLine::Backward); 265 | m_timeLine.setDuration( 266 | durationFraction(m_parameters.stretchDuration, m_shapeFactor)); 267 | m_timeLine.setEasingCurve(QEasingCurve::Linear); 268 | m_clip = false; 269 | m_done = false; 270 | return; 271 | 272 | default: 273 | Q_UNREACHABLE(); 274 | } 275 | } 276 | 277 | bool Model::done() const 278 | { 279 | return m_done; 280 | } 281 | 282 | struct TransformParameters { 283 | QEasingCurve shapeCurve; 284 | Direction direction; 285 | qreal stretchProgress; 286 | qreal squashProgress; 287 | qreal bumpProgress; 288 | qreal bumpDistance; 289 | }; 290 | 291 | static inline qreal interpolate(qreal from, qreal to, qreal t) 292 | { 293 | return from * (1.0 - t) + to * t; 294 | } 295 | 296 | static void transformQuadsLeft( 297 | const KWin::EffectWindow* window, 298 | const TransformParameters& params, 299 | QVector& quads) 300 | { 301 | // FIXME: Have a generic function that transforms window quads. Perhaps, 302 | // a better approach is to have a transform method that operates on each 303 | // individual vertex, e.g. void Model::transform(WindowVertex& vertex). 304 | 305 | const QRectF iconRect = window->iconGeometry(); 306 | const QRectF windowRect = window->frameGeometry(); 307 | 308 | const qreal distance = windowRect.right() - iconRect.right() + params.bumpDistance; 309 | 310 | for (int i = 0; i < quads.count(); ++i) { 311 | KWin::WindowQuad& quad = quads[i]; 312 | 313 | const qreal leftOffset = quad[0].x() - interpolate(0.0, distance, params.squashProgress); 314 | const qreal rightOffset = quad[2].x() - interpolate(0.0, distance, params.squashProgress); 315 | 316 | const qreal leftScale = params.stretchProgress * params.shapeCurve.valueForProgress((windowRect.width() - leftOffset) / distance); 317 | const qreal rightScale = params.stretchProgress * params.shapeCurve.valueForProgress((windowRect.width() - rightOffset) / distance); 318 | 319 | const qreal targetTopLeftY = iconRect.y() + iconRect.height() * quad[0].y() / windowRect.height(); 320 | const qreal targetTopRightY = iconRect.y() + iconRect.height() * quad[1].y() / windowRect.height(); 321 | const qreal targetBottomRightY = iconRect.y() + iconRect.height() * quad[2].y() / windowRect.height(); 322 | const qreal targetBottomLeftY = iconRect.y() + iconRect.height() * quad[3].y() / windowRect.height(); 323 | 324 | quad[0].setY(quad[0].y() + leftScale * (targetTopLeftY - (windowRect.y() + quad[0].y()))); 325 | quad[3].setY(quad[3].y() + leftScale * (targetBottomLeftY - (windowRect.y() + quad[3].y()))); 326 | quad[1].setY(quad[1].y() + rightScale * (targetTopRightY - (windowRect.y() + quad[1].y()))); 327 | quad[2].setY(quad[2].y() + rightScale * (targetBottomRightY - (windowRect.y() + quad[2].y()))); 328 | 329 | const qreal targetLeftOffset = leftOffset + params.bumpDistance * params.bumpProgress; 330 | const qreal targetRightOffset = rightOffset + params.bumpDistance * params.bumpProgress; 331 | 332 | quad[0].setX(targetLeftOffset); 333 | quad[3].setX(targetLeftOffset); 334 | quad[1].setX(targetRightOffset); 335 | quad[2].setX(targetRightOffset); 336 | } 337 | } 338 | 339 | static void transformQuadsTop( 340 | const KWin::EffectWindow* window, 341 | const TransformParameters& params, 342 | QVector& quads) 343 | { 344 | // FIXME: Have a generic function that transforms window quads. Perhaps, 345 | // a better approach is to have a transform method that operates on each 346 | // individual vertex, e.g. void Model::transform(WindowVertex& vertex). 347 | 348 | const QRectF iconRect = window->iconGeometry(); 349 | const QRectF windowRect = window->frameGeometry(); 350 | 351 | const qreal distance = windowRect.bottom() - iconRect.bottom() + params.bumpDistance; 352 | 353 | for (int i = 0; i < quads.count(); ++i) { 354 | KWin::WindowQuad& quad = quads[i]; 355 | 356 | const qreal topOffset = quad[0].y() - interpolate(0.0, distance, params.squashProgress); 357 | const qreal bottomOffset = quad[2].y() - interpolate(0.0, distance, params.squashProgress); 358 | 359 | const qreal topScale = params.stretchProgress * params.shapeCurve.valueForProgress((windowRect.height() - topOffset) / distance); 360 | const qreal bottomScale = params.stretchProgress * params.shapeCurve.valueForProgress((windowRect.height() - bottomOffset) / distance); 361 | 362 | const qreal targetTopLeftX = iconRect.x() + iconRect.width() * quad[0].x() / windowRect.width(); 363 | const qreal targetTopRightX = iconRect.x() + iconRect.width() * quad[1].x() / windowRect.width(); 364 | const qreal targetBottomRightX = iconRect.x() + iconRect.width() * quad[2].x() / windowRect.width(); 365 | const qreal targetBottomLeftX = iconRect.x() + iconRect.width() * quad[3].x() / windowRect.width(); 366 | 367 | quad[0].setX(quad[0].x() + topScale * (targetTopLeftX - (windowRect.x() + quad[0].x()))); 368 | quad[1].setX(quad[1].x() + topScale * (targetTopRightX - (windowRect.x() + quad[1].x()))); 369 | quad[2].setX(quad[2].x() + bottomScale * (targetBottomRightX - (windowRect.x() + quad[2].x()))); 370 | quad[3].setX(quad[3].x() + bottomScale * (targetBottomLeftX - (windowRect.x() + quad[3].x()))); 371 | 372 | const qreal targetTopOffset = topOffset + params.bumpDistance * params.bumpProgress; 373 | const qreal targetBottomOffset = bottomOffset + params.bumpDistance * params.bumpProgress; 374 | 375 | quad[0].setY(targetTopOffset); 376 | quad[1].setY(targetTopOffset); 377 | quad[2].setY(targetBottomOffset); 378 | quad[3].setY(targetBottomOffset); 379 | } 380 | } 381 | 382 | static void transformQuadsRight( 383 | const KWin::EffectWindow* window, 384 | const TransformParameters& params, 385 | QVector& quads) 386 | { 387 | // FIXME: Have a generic function that transforms window quads. Perhaps, 388 | // a better approach is to have a transform method that operates on each 389 | // individual vertex, e.g. void Model::transform(WindowVertex& vertex). 390 | 391 | const QRectF iconRect = window->iconGeometry(); 392 | const QRectF windowRect = window->frameGeometry(); 393 | 394 | const qreal distance = iconRect.left() - windowRect.left() + params.bumpDistance; 395 | 396 | for (int i = 0; i < quads.count(); ++i) { 397 | KWin::WindowQuad& quad = quads[i]; 398 | 399 | const qreal leftOffset = quad[0].x() + interpolate(0.0, distance, params.squashProgress); 400 | const qreal rightOffset = quad[2].x() + interpolate(0.0, distance, params.squashProgress); 401 | 402 | const qreal leftScale = params.stretchProgress * params.shapeCurve.valueForProgress(leftOffset / distance); 403 | const qreal rightScale = params.stretchProgress * params.shapeCurve.valueForProgress(rightOffset / distance); 404 | 405 | const qreal targetTopLeftY = iconRect.y() + iconRect.height() * quad[0].y() / windowRect.height(); 406 | const qreal targetTopRightY = iconRect.y() + iconRect.height() * quad[1].y() / windowRect.height(); 407 | const qreal targetBottomRightY = iconRect.y() + iconRect.height() * quad[2].y() / windowRect.height(); 408 | const qreal targetBottomLeftY = iconRect.y() + iconRect.height() * quad[3].y() / windowRect.height(); 409 | 410 | quad[0].setY(quad[0].y() + leftScale * (targetTopLeftY - (windowRect.y() + quad[0].y()))); 411 | quad[3].setY(quad[3].y() + leftScale * (targetBottomLeftY - (windowRect.y() + quad[3].y()))); 412 | quad[1].setY(quad[1].y() + rightScale * (targetTopRightY - (windowRect.y() + quad[1].y()))); 413 | quad[2].setY(quad[2].y() + rightScale * (targetBottomRightY - (windowRect.y() + quad[2].y()))); 414 | 415 | const qreal targetLeftOffset = leftOffset - params.bumpDistance * params.bumpProgress; 416 | const qreal targetRightOffset = rightOffset - params.bumpDistance * params.bumpProgress; 417 | 418 | quad[0].setX(targetLeftOffset); 419 | quad[3].setX(targetLeftOffset); 420 | quad[1].setX(targetRightOffset); 421 | quad[2].setX(targetRightOffset); 422 | } 423 | } 424 | 425 | static void transformQuadsBottom( 426 | const KWin::EffectWindow* window, 427 | const TransformParameters& params, 428 | QVector& quads) 429 | { 430 | // FIXME: Have a generic function that transforms window quads. Perhaps, 431 | // a better approach is to have a transform method that operates on each 432 | // individual vertex, e.g. void Model::transform(WindowVertex& vertex). 433 | 434 | const QRectF iconRect = window->iconGeometry(); 435 | const QRectF windowRect = window->frameGeometry(); 436 | 437 | const qreal distance = iconRect.top() - windowRect.top() + params.bumpDistance; 438 | 439 | for (int i = 0; i < quads.count(); ++i) { 440 | KWin::WindowQuad& quad = quads[i]; 441 | 442 | const qreal topOffset = quad[0].y() + interpolate(0.0, distance, params.squashProgress); 443 | const qreal bottomOffset = quad[2].y() + interpolate(0.0, distance, params.squashProgress); 444 | 445 | const qreal topScale = params.stretchProgress * params.shapeCurve.valueForProgress(topOffset / distance); 446 | const qreal bottomScale = params.stretchProgress * params.shapeCurve.valueForProgress(bottomOffset / distance); 447 | 448 | const qreal targetTopLeftX = iconRect.x() + iconRect.width() * quad[0].x() / windowRect.width(); 449 | const qreal targetTopRightX = iconRect.x() + iconRect.width() * quad[1].x() / windowRect.width(); 450 | const qreal targetBottomRightX = iconRect.x() + iconRect.width() * quad[2].x() / windowRect.width(); 451 | const qreal targetBottomLeftX = iconRect.x() + iconRect.width() * quad[3].x() / windowRect.width(); 452 | 453 | quad[0].setX(quad[0].x() + topScale * (targetTopLeftX - (windowRect.x() + quad[0].x()))); 454 | quad[1].setX(quad[1].x() + topScale * (targetTopRightX - (windowRect.x() + quad[1].x()))); 455 | quad[2].setX(quad[2].x() + bottomScale * (targetBottomRightX - (windowRect.x() + quad[2].x()))); 456 | quad[3].setX(quad[3].x() + bottomScale * (targetBottomLeftX - (windowRect.x() + quad[3].x()))); 457 | 458 | const qreal targetTopOffset = topOffset - params.bumpDistance * params.bumpProgress; 459 | const qreal targetBottomOffset = bottomOffset - params.bumpDistance * params.bumpProgress; 460 | 461 | quad[0].setY(targetTopOffset); 462 | quad[1].setY(targetTopOffset); 463 | quad[2].setY(targetBottomOffset); 464 | quad[3].setY(targetBottomOffset); 465 | } 466 | } 467 | 468 | static void transformQuads( 469 | const KWin::EffectWindow* window, 470 | const TransformParameters& params, 471 | QVector& quads) 472 | { 473 | switch (params.direction) { 474 | case Direction::Left: 475 | transformQuadsLeft(window, params, quads); 476 | break; 477 | 478 | case Direction::Top: 479 | transformQuadsTop(window, params, quads); 480 | break; 481 | 482 | case Direction::Right: 483 | transformQuadsRight(window, params, quads); 484 | break; 485 | 486 | case Direction::Bottom: 487 | transformQuadsBottom(window, params, quads); 488 | break; 489 | 490 | default: 491 | Q_UNREACHABLE(); 492 | } 493 | } 494 | 495 | void Model::apply(QVector& quads) const 496 | { 497 | switch (m_stage) { 498 | case AnimationStage::Bump: 499 | applyBump(quads); 500 | break; 501 | 502 | case AnimationStage::Stretch1: 503 | applyStretch1(quads); 504 | break; 505 | 506 | case AnimationStage::Stretch2: 507 | applyStretch2(quads); 508 | break; 509 | 510 | case AnimationStage::Squash: 511 | applySquash(quads); 512 | break; 513 | } 514 | } 515 | 516 | void Model::applyBump(QVector& quads) const 517 | { 518 | TransformParameters params; 519 | params.shapeCurve = m_parameters.shapeCurve; 520 | params.direction = m_direction; 521 | params.squashProgress = 0.0; 522 | params.stretchProgress = 0.0; 523 | params.bumpProgress = m_timeLine.value(); 524 | params.bumpDistance = m_bumpDistance; 525 | transformQuads(m_window, params, quads); 526 | } 527 | 528 | void Model::applyStretch1(QVector& quads) const 529 | { 530 | TransformParameters params; 531 | params.shapeCurve = m_parameters.shapeCurve; 532 | params.direction = m_direction; 533 | params.squashProgress = 0.0; 534 | params.stretchProgress = m_shapeFactor * m_timeLine.value(); 535 | params.bumpProgress = 1.0; 536 | params.bumpDistance = m_bumpDistance; 537 | transformQuads(m_window, params, quads); 538 | } 539 | 540 | void Model::applyStretch2(QVector& quads) const 541 | { 542 | TransformParameters params; 543 | params.shapeCurve = m_parameters.shapeCurve; 544 | params.direction = m_direction; 545 | params.squashProgress = 0.0; 546 | params.stretchProgress = m_shapeFactor * m_timeLine.value(); 547 | params.bumpProgress = params.stretchProgress; 548 | params.bumpDistance = m_bumpDistance; 549 | transformQuads(m_window, params, quads); 550 | } 551 | 552 | void Model::applySquash(QVector& quads) const 553 | { 554 | TransformParameters params; 555 | params.shapeCurve = m_parameters.shapeCurve; 556 | params.direction = m_direction; 557 | params.squashProgress = m_timeLine.value(); 558 | params.stretchProgress = qMin(m_shapeFactor + params.squashProgress, 1.0); 559 | params.bumpProgress = 1.0; 560 | params.bumpDistance = m_bumpDistance; 561 | transformQuads(m_window, params, quads); 562 | } 563 | 564 | Model::Parameters Model::parameters() const 565 | { 566 | return m_parameters; 567 | } 568 | 569 | void Model::setParameters(const Parameters& parameters) 570 | { 571 | m_parameters = parameters; 572 | } 573 | 574 | KWin::EffectWindow* Model::window() const 575 | { 576 | return m_window; 577 | } 578 | 579 | void Model::setWindow(KWin::EffectWindow* window) 580 | { 581 | m_window = window; 582 | } 583 | 584 | bool Model::needsClip() const 585 | { 586 | return m_clip; 587 | } 588 | 589 | QRegion Model::clipRegion() const 590 | { 591 | const QRectF iconRect = m_window->iconGeometry(); 592 | QRectF clipRect = m_window->expandedGeometry(); 593 | 594 | switch (m_direction) { 595 | case Direction::Top: 596 | clipRect.translate(0, m_bumpDistance); 597 | clipRect.setTop(iconRect.top()); 598 | clipRect.setLeft(qMin(iconRect.left(), clipRect.left())); 599 | clipRect.setRight(qMax(iconRect.right(), clipRect.right())); 600 | break; 601 | 602 | case Direction::Right: 603 | clipRect.translate(-m_bumpDistance, 0); 604 | clipRect.setRight(iconRect.right()); 605 | clipRect.setTop(qMin(iconRect.top(), clipRect.top())); 606 | clipRect.setBottom(qMax(iconRect.bottom(), clipRect.bottom())); 607 | break; 608 | 609 | case Direction::Bottom: 610 | clipRect.translate(0, -m_bumpDistance); 611 | clipRect.setBottom(iconRect.bottom()); 612 | clipRect.setLeft(qMin(iconRect.left(), clipRect.left())); 613 | clipRect.setRight(qMax(iconRect.right(), clipRect.right())); 614 | break; 615 | 616 | case Direction::Left: 617 | clipRect.translate(m_bumpDistance, 0); 618 | clipRect.setLeft(iconRect.left()); 619 | clipRect.setTop(qMin(iconRect.top(), clipRect.top())); 620 | clipRect.setBottom(qMax(iconRect.bottom(), clipRect.bottom())); 621 | break; 622 | 623 | default: 624 | Q_UNREACHABLE(); 625 | } 626 | 627 | return clipRect.toAlignedRect(); 628 | } 629 | 630 | int Model::computeBumpDistance() const 631 | { 632 | const QRectF windowRect = m_window->frameGeometry(); 633 | const QRectF iconRect = m_window->iconGeometry(); 634 | 635 | qreal bumpDistance = 0; 636 | switch (m_direction) { 637 | case Direction::Top: 638 | bumpDistance = std::max(qreal(0), iconRect.y() + iconRect.height() - windowRect.y()); 639 | break; 640 | 641 | case Direction::Right: 642 | bumpDistance = std::max(qreal(0), windowRect.x() + windowRect.width() - iconRect.x()); 643 | break; 644 | 645 | case Direction::Bottom: 646 | bumpDistance = std::max(qreal(0), windowRect.y() + windowRect.height() - iconRect.y()); 647 | break; 648 | 649 | case Direction::Left: 650 | bumpDistance = std::max(qreal(0), iconRect.x() + iconRect.width() - windowRect.x()); 651 | break; 652 | 653 | default: 654 | Q_UNREACHABLE(); 655 | } 656 | 657 | bumpDistance += std::min(bumpDistance, m_parameters.bumpDistance); 658 | 659 | return bumpDistance; 660 | } 661 | 662 | qreal Model::computeShapeFactor() const 663 | { 664 | const QRectF windowRect = m_window->frameGeometry(); 665 | const QRectF iconRect = m_window->iconGeometry(); 666 | 667 | int movingExtent = 0; 668 | int distanceToIcon = 0; 669 | switch (m_direction) { 670 | case Direction::Top: 671 | movingExtent = windowRect.height(); 672 | distanceToIcon = windowRect.bottom() - iconRect.bottom() + m_bumpDistance; 673 | break; 674 | 675 | case Direction::Right: 676 | movingExtent = windowRect.width(); 677 | distanceToIcon = iconRect.left() - windowRect.left() + m_bumpDistance; 678 | break; 679 | 680 | case Direction::Bottom: 681 | movingExtent = windowRect.height(); 682 | distanceToIcon = iconRect.top() - windowRect.top() + m_bumpDistance; 683 | break; 684 | 685 | case Direction::Left: 686 | movingExtent = windowRect.width(); 687 | distanceToIcon = windowRect.right() - iconRect.right() + m_bumpDistance; 688 | break; 689 | 690 | default: 691 | Q_UNREACHABLE(); 692 | } 693 | 694 | const qreal minimumShapeFactor = static_cast(movingExtent) / distanceToIcon; 695 | return qMax(m_parameters.shapeFactor, minimumShapeFactor); 696 | } 697 | -------------------------------------------------------------------------------- /src/Model.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // Own 21 | #include "common.h" 22 | 23 | // kwineffects 24 | #include 25 | 26 | /** 27 | * Model for the magic lamp animation. 28 | **/ 29 | class Model { 30 | public: 31 | struct Parameters { 32 | // The duration of the squash stage. 33 | std::chrono::milliseconds squashDuration; 34 | 35 | // The duration of the stretch stage. 36 | std::chrono::milliseconds stretchDuration; 37 | 38 | // How long it takes to raise the window. 39 | std::chrono::milliseconds bumpDuration; 40 | 41 | // Defines shape of transformed windows. 42 | QEasingCurve shapeCurve; 43 | 44 | // The blend factor between Squash and Stretch stage. 45 | qreal shapeFactor; 46 | 47 | // How much the transformed window should be raised. 48 | qreal bumpDistance; 49 | }; 50 | 51 | explicit Model(KWin::EffectWindow* window = nullptr); 52 | 53 | /** 54 | * This enum type is used to specify kind of the current animation. 55 | **/ 56 | enum class AnimationKind { 57 | Minimize, 58 | Unminimize 59 | }; 60 | 61 | /** 62 | * Starts animation with the given kind. 63 | * 64 | * @param kind Kind of the animation (minimize or unminimize). 65 | **/ 66 | void start(AnimationKind kind); 67 | 68 | /** 69 | * Updates the model to @p milliseconds. 70 | **/ 71 | void advance(std::chrono::milliseconds presentTime); 72 | 73 | /** 74 | * Returns whether the animation is complete. 75 | **/ 76 | bool done() const; 77 | 78 | /** 79 | * Applies the current state of the model to the given list of 80 | * window quads. 81 | * 82 | * @param quadds The list of window quads to be transformed. 83 | **/ 84 | void apply(QVector& quads) const; 85 | 86 | /** 87 | * Returns the parameters of the model. 88 | **/ 89 | Parameters parameters() const; 90 | 91 | /** 92 | * Sets parameters of the model. 93 | * 94 | * @param parameters The new paramaters. 95 | **/ 96 | void setParameters(const Parameters& parameters); 97 | 98 | /** 99 | * Returns the associated window. 100 | **/ 101 | KWin::EffectWindow* window() const; 102 | 103 | /** 104 | * Sets the associated window. 105 | * 106 | * @param window The window associated with this model. 107 | **/ 108 | void setWindow(KWin::EffectWindow* window); 109 | 110 | /** 111 | * Returns whether the painted result has to be clipped. 112 | * 113 | * @see clipRegion 114 | **/ 115 | bool needsClip() const; 116 | 117 | /** 118 | * Returns the clip region. 119 | * 120 | * @see needsClip 121 | **/ 122 | QRegion clipRegion() const; 123 | 124 | private: 125 | void applyBump(QVector& quads) const; 126 | void applyStretch1(QVector& quads) const; 127 | void applyStretch2(QVector& quads) const; 128 | void applySquash(QVector& quads) const; 129 | 130 | void updateMinimizeStage(); 131 | void updateUnminimizeStage(); 132 | 133 | int computeBumpDistance() const; 134 | qreal computeShapeFactor() const; 135 | 136 | Parameters m_parameters; 137 | 138 | enum class AnimationStage { 139 | Bump, 140 | Stretch1, 141 | Stretch2, 142 | Squash 143 | }; 144 | 145 | KWin::EffectWindow* m_window; 146 | AnimationKind m_kind; 147 | AnimationStage m_stage; 148 | KWin::TimeLine m_timeLine; 149 | Direction m_direction; 150 | int m_bumpDistance; 151 | qreal m_shapeFactor; 152 | bool m_clip; 153 | bool m_done = false; 154 | }; 155 | -------------------------------------------------------------------------------- /src/YetAnotherMagicLampConfig.kcfgc: -------------------------------------------------------------------------------- 1 | File=options.kcfg 2 | ClassName=YetAnotherMagicLampConfig 3 | Singleton=true 4 | Mutators=true 5 | -------------------------------------------------------------------------------- /src/YetAnotherMagicLampEffect.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Own 19 | #include "YetAnotherMagicLampEffect.h" 20 | #include "Model.h" 21 | 22 | // Auto-generated 23 | #include "YetAnotherMagicLampConfig.h" 24 | 25 | // std 26 | #include 27 | 28 | enum ShapeCurve { 29 | Linear = 0, 30 | Quad = 1, 31 | Cubic = 2, 32 | Quart = 3, 33 | Quint = 4, 34 | Sine = 5, 35 | Circ = 6, 36 | Bounce = 7, 37 | Bezier = 8 38 | }; 39 | 40 | YetAnotherMagicLampEffect::YetAnotherMagicLampEffect() 41 | { 42 | reconfigure(ReconfigureAll); 43 | 44 | connect(KWin::effects, &KWin::EffectsHandler::windowMinimized, 45 | this, &YetAnotherMagicLampEffect::slotWindowMinimized); 46 | connect(KWin::effects, &KWin::EffectsHandler::windowUnminimized, 47 | this, &YetAnotherMagicLampEffect::slotWindowUnminimized); 48 | connect(KWin::effects, &KWin::EffectsHandler::windowDeleted, 49 | this, &YetAnotherMagicLampEffect::slotWindowDeleted); 50 | connect(KWin::effects, &KWin::EffectsHandler::activeFullScreenEffectChanged, 51 | this, &YetAnotherMagicLampEffect::slotActiveFullScreenEffectChanged); 52 | } 53 | 54 | YetAnotherMagicLampEffect::~YetAnotherMagicLampEffect() 55 | { 56 | } 57 | 58 | void YetAnotherMagicLampEffect::reconfigure(ReconfigureFlags flags) 59 | { 60 | Q_UNUSED(flags) 61 | 62 | YetAnotherMagicLampConfig::self()->read(); 63 | 64 | QEasingCurve curve; 65 | const auto shapeCurve = static_cast(YetAnotherMagicLampConfig::shapeCurve()); 66 | switch (shapeCurve) { 67 | case ShapeCurve::Linear: 68 | curve.setType(QEasingCurve::Linear); 69 | break; 70 | 71 | case ShapeCurve::Quad: 72 | curve.setType(QEasingCurve::InOutQuad); 73 | break; 74 | 75 | case ShapeCurve::Cubic: 76 | curve.setType(QEasingCurve::InOutCubic); 77 | break; 78 | 79 | case ShapeCurve::Quart: 80 | curve.setType(QEasingCurve::InOutQuart); 81 | break; 82 | 83 | case ShapeCurve::Quint: 84 | curve.setType(QEasingCurve::InOutQuint); 85 | break; 86 | 87 | case ShapeCurve::Sine: 88 | curve.setType(QEasingCurve::InOutSine); 89 | break; 90 | 91 | case ShapeCurve::Circ: 92 | curve.setType(QEasingCurve::InOutCirc); 93 | break; 94 | 95 | case ShapeCurve::Bounce: 96 | curve.setType(QEasingCurve::InOutBounce); 97 | break; 98 | 99 | case ShapeCurve::Bezier: 100 | // With the cubic bezier curve, "0" corresponds to the furtherst edge 101 | // of a window, "1" corresponds to the closest edge. 102 | curve.setType(QEasingCurve::BezierSpline); 103 | curve.addCubicBezierSegment( 104 | QPointF(0.3, 0.0), 105 | QPointF(0.7, 1.0), 106 | QPointF(1.0, 1.0)); 107 | break; 108 | default: 109 | // Fallback to the sine curve. 110 | curve.setType(QEasingCurve::InOutSine); 111 | break; 112 | } 113 | m_modelParameters.shapeCurve = curve; 114 | 115 | const int baseDuration = animationTime(300); 116 | m_modelParameters.squashDuration = std::chrono::milliseconds(baseDuration); 117 | m_modelParameters.stretchDuration = std::chrono::milliseconds(qMax(qRound(baseDuration * 0.7), 1)); 118 | m_modelParameters.bumpDuration = std::chrono::milliseconds(baseDuration); 119 | m_modelParameters.shapeFactor = YetAnotherMagicLampConfig::initialShapeFactor(); 120 | m_modelParameters.bumpDistance = YetAnotherMagicLampConfig::maxBumpDistance(); 121 | 122 | m_gridResolution = YetAnotherMagicLampConfig::gridResolution(); 123 | } 124 | 125 | void YetAnotherMagicLampEffect::prePaintScreen(KWin::ScreenPrePaintData& data, std::chrono::milliseconds presentTime) 126 | { 127 | for (AnimationData& data : m_animations) { 128 | data.model.advance(presentTime); 129 | } 130 | 131 | data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; 132 | 133 | KWin::effects->prePaintScreen(data, presentTime); 134 | } 135 | 136 | void YetAnotherMagicLampEffect::postPaintScreen() 137 | { 138 | for (auto it = m_animations.begin(); it != m_animations.end();) { 139 | if (it->model.done()) { 140 | unredirect(it.key()); 141 | it = m_animations.erase(it); 142 | } else { 143 | ++it; 144 | } 145 | } 146 | 147 | KWin::effects->addRepaintFull(); 148 | KWin::effects->postPaintScreen(); 149 | } 150 | 151 | void YetAnotherMagicLampEffect::paintWindow(KWin::EffectWindow* w, int mask, QRegion region, KWin::WindowPaintData& data) 152 | { 153 | QRegion clip = region; 154 | 155 | auto it = m_animations.constFind(w); 156 | if (it != m_animations.constEnd()) { 157 | if (it->model.needsClip()) { 158 | clip = it->model.clipRegion(); 159 | } 160 | } 161 | 162 | KWin::effects->paintWindow(w, mask, clip, data); 163 | } 164 | 165 | void YetAnotherMagicLampEffect::apply(KWin::EffectWindow* window, int mask, KWin::WindowPaintData& data, KWin::WindowQuadList& quads) 166 | { 167 | Q_UNUSED(mask) 168 | Q_UNUSED(data) 169 | 170 | auto it = m_animations.constFind(window); 171 | if (it == m_animations.constEnd()) { 172 | return; 173 | } 174 | 175 | quads = quads.makeGrid(m_gridResolution); 176 | (*it).model.apply(quads); 177 | } 178 | 179 | bool YetAnotherMagicLampEffect::isActive() const 180 | { 181 | return !m_animations.isEmpty(); 182 | } 183 | 184 | bool YetAnotherMagicLampEffect::supported() 185 | { 186 | if (!KWin::effects->animationsSupported()) { 187 | return false; 188 | } 189 | if (KWin::effects->isOpenGLCompositing()) { 190 | return true; 191 | } 192 | return false; 193 | } 194 | 195 | void YetAnotherMagicLampEffect::slotWindowMinimized(KWin::EffectWindow* w) 196 | { 197 | if (KWin::effects->activeFullScreenEffect()) { 198 | return; 199 | } 200 | 201 | const QRectF iconRect = w->iconGeometry(); 202 | if (!iconRect.isValid()) { 203 | return; 204 | } 205 | 206 | AnimationData& data = m_animations[w]; 207 | data.model.setWindow(w); 208 | data.model.setParameters(m_modelParameters); 209 | data.model.start(Model::AnimationKind::Minimize); 210 | data.visibleRef = KWin::EffectWindowVisibleRef(w, KWin::EffectWindow::PAINT_DISABLED_BY_MINIMIZE); 211 | 212 | redirect(w); 213 | 214 | KWin::effects->addRepaintFull(); 215 | } 216 | 217 | void YetAnotherMagicLampEffect::slotWindowUnminimized(KWin::EffectWindow* w) 218 | { 219 | if (KWin::effects->activeFullScreenEffect()) { 220 | return; 221 | } 222 | 223 | const QRectF iconRect = w->iconGeometry(); 224 | if (!iconRect.isValid()) { 225 | return; 226 | } 227 | 228 | AnimationData& data = m_animations[w]; 229 | data.model.setWindow(w); 230 | data.model.setParameters(m_modelParameters); 231 | data.model.start(Model::AnimationKind::Unminimize); 232 | 233 | redirect(w); 234 | 235 | KWin::effects->addRepaintFull(); 236 | } 237 | 238 | void YetAnotherMagicLampEffect::slotWindowDeleted(KWin::EffectWindow* w) 239 | { 240 | m_animations.remove(w); 241 | } 242 | 243 | void YetAnotherMagicLampEffect::slotActiveFullScreenEffectChanged() 244 | { 245 | if (KWin::effects->activeFullScreenEffect() != nullptr) { 246 | for (auto it = m_animations.constBegin(); it != m_animations.constEnd(); ++it) { 247 | unredirect(it.key()); 248 | } 249 | m_animations.clear(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/YetAnotherMagicLampEffect.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // Own 21 | #include "Model.h" 22 | #include "common.h" 23 | 24 | // kwineffects 25 | #include 26 | 27 | struct AnimationData { 28 | Model model; 29 | KWin::EffectWindowVisibleRef visibleRef; 30 | }; 31 | 32 | class YetAnotherMagicLampEffect : public KWin::OffscreenEffect { 33 | Q_OBJECT 34 | 35 | public: 36 | YetAnotherMagicLampEffect(); 37 | ~YetAnotherMagicLampEffect() override; 38 | 39 | void reconfigure(ReconfigureFlags flags) override; 40 | 41 | void prePaintScreen(KWin::ScreenPrePaintData& data, std::chrono::milliseconds presentTime) override; 42 | void postPaintScreen() override; 43 | 44 | void paintWindow(KWin::EffectWindow* w, int mask, QRegion region, KWin::WindowPaintData& data) override; 45 | 46 | bool isActive() const override; 47 | int requestedEffectChainPosition() const override; 48 | 49 | static bool supported(); 50 | 51 | protected: 52 | void apply(KWin::EffectWindow* window, int mask, KWin::WindowPaintData& data, KWin::WindowQuadList& quads) override; 53 | 54 | private Q_SLOTS: 55 | void slotWindowMinimized(KWin::EffectWindow* w); 56 | void slotWindowUnminimized(KWin::EffectWindow* w); 57 | void slotWindowDeleted(KWin::EffectWindow* w); 58 | void slotActiveFullScreenEffectChanged(); 59 | 60 | private: 61 | Model::Parameters m_modelParameters; 62 | int m_gridResolution; 63 | 64 | QMap m_animations; 65 | }; 66 | 67 | inline int YetAnotherMagicLampEffect::requestedEffectChainPosition() const 68 | { 69 | return 50; 70 | } 71 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | /** 21 | * This enum type is used to specify direction. 22 | **/ 23 | enum class Direction { 24 | Left, 25 | Top, 26 | Right, 27 | Bottom 28 | }; 29 | -------------------------------------------------------------------------------- /src/kcm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(kcm_SRCS 2 | YetAnotherMagicLampEffectKCM.cc 3 | plugin.cc 4 | ) 5 | 6 | kconfig_add_kcfg_files(kcm_SRCS 7 | ../YetAnotherMagicLampConfig.kcfgc 8 | ) 9 | 10 | qt5_wrap_ui(kcm_SRCS YetAnotherMagicLampEffectKCM.ui) 11 | 12 | qt5_add_dbus_interface(kcm_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) 13 | 14 | add_library(kwin_yetanothermagiclamp_config MODULE ${kcm_SRCS}) 15 | 16 | target_link_libraries(kwin_yetanothermagiclamp_config 17 | Qt5::Core 18 | Qt5::DBus 19 | Qt5::Gui 20 | KF5::ConfigCore 21 | KF5::ConfigGui 22 | KF5::ConfigWidgets 23 | ) 24 | 25 | install( 26 | TARGETS 27 | kwin_yetanothermagiclamp_config 28 | 29 | DESTINATION 30 | ${PLUGIN_INSTALL_DIR}/kwin/effects/configs 31 | ) 32 | -------------------------------------------------------------------------------- /src/kcm/YetAnotherMagicLampEffectKCM.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Own 19 | #include "YetAnotherMagicLampEffectKCM.h" 20 | 21 | // Auto-generated 22 | #include "YetAnotherMagicLampConfig.h" 23 | #include "kwineffects_interface.h" 24 | 25 | YetAnotherMagicLampEffectKCM::YetAnotherMagicLampEffectKCM(QWidget* parent, const QVariantList& args) 26 | : KCModule(parent, args) 27 | , m_ui(new Ui::YetAnotherMagicLampEffectKCM) 28 | { 29 | m_ui->setupUi(this); 30 | addConfig(YetAnotherMagicLampConfig::self(), this); 31 | load(); 32 | } 33 | 34 | YetAnotherMagicLampEffectKCM::~YetAnotherMagicLampEffectKCM() 35 | { 36 | delete m_ui; 37 | } 38 | 39 | void YetAnotherMagicLampEffectKCM::save() 40 | { 41 | KCModule::save(); 42 | OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), 43 | QStringLiteral("/Effects"), 44 | QDBusConnection::sessionBus()); 45 | interface.reconfigureEffect(QStringLiteral("kwin4_effect_yetanothermagiclamp")); 46 | } 47 | -------------------------------------------------------------------------------- /src/kcm/YetAnotherMagicLampEffectKCM.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #pragma once 19 | 20 | // KF5 21 | #include 22 | 23 | // Auto-generated 24 | #include "ui_YetAnotherMagicLampEffectKCM.h" 25 | 26 | class YetAnotherMagicLampEffectKCM : public KCModule { 27 | Q_OBJECT 28 | 29 | public: 30 | explicit YetAnotherMagicLampEffectKCM(QWidget* parent = nullptr, const QVariantList& args = {}); 31 | ~YetAnotherMagicLampEffectKCM() override; 32 | 33 | void save() override; 34 | 35 | private: 36 | Ui::YetAnotherMagicLampEffectKCM* m_ui; 37 | }; 38 | -------------------------------------------------------------------------------- /src/kcm/YetAnotherMagicLampEffectKCM.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | YetAnotherMagicLampEffectKCM 4 | 5 | 6 | 7 | 0 8 | 0 9 | 455 10 | 215 11 | 12 | 13 | 14 | 15 | 16 | 17 | Duration: 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 0 26 | 0 27 | 28 | 29 | 30 | Default 31 | 32 | 33 | milliseconds 34 | 35 | 36 | 9999 37 | 38 | 39 | 5 40 | 41 | 42 | 43 | 44 | 45 | 46 | Grid resolution: 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 0 55 | 0 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Max bump distance: 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 0 72 | 0 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Initial shape factor: 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 0 89 | 0 90 | 91 | 92 | 93 | 1.000000000000000 94 | 95 | 96 | 0.050000000000000 97 | 98 | 99 | 100 | 101 | 102 | 103 | Shape curve: 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 0 112 | 0 113 | 114 | 115 | 116 | 117 | Linear 118 | 119 | 120 | 121 | 122 | Quad 123 | 124 | 125 | 126 | 127 | Cubic 128 | 129 | 130 | 131 | 132 | Quart 133 | 134 | 135 | 136 | 137 | Quint 138 | 139 | 140 | 141 | 142 | Sine 143 | 144 | 145 | 146 | 147 | Circ 148 | 149 | 150 | 151 | 152 | Bounce 153 | 154 | 155 | 156 | 157 | Bezier 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/kcm/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Name": "Yet Another Magic Lamp", 4 | "ServiceTypes": [ 5 | "KCModule" 6 | ] 7 | }, 8 | "X-KDE-ParentComponents": [ 9 | "kwin4_effect_yetanothermagiclamp" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/kcm/plugin.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Own 19 | #include "YetAnotherMagicLampEffectKCM.h" 20 | 21 | // KF5 22 | #include 23 | 24 | K_PLUGIN_FACTORY_WITH_JSON(YetAnotherMagicLampEffectKCMFactory, 25 | "metadata.json", 26 | registerPlugin();) 27 | 28 | #include "plugin.moc" 29 | -------------------------------------------------------------------------------- /src/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Authors": [ 4 | { 5 | "Email": "vladzzag@gmail.com", 6 | "Name": "Vlad Zagorodniy" 7 | } 8 | ], 9 | "Category": "Appearance", 10 | "Description": "Alternative magic lamp effect", 11 | "EnabledByDefault": false, 12 | "Icon": "preferences-system-windows-effect-yetanothermagiclamp", 13 | "Id": "kwin4_effect_yetanothermagiclamp", 14 | "License": "GPL", 15 | "Name": "Yet Another Magic Lamp", 16 | "ServiceTypes": [ 17 | "KWin/Effect" 18 | ] 19 | }, 20 | "org.kde.kwin.effect": { 21 | "exclusiveGroup": "minimize" 22 | }, 23 | "X-KDE-ConfigModule": "kwin_yetanothermagiclamp_config" 24 | } 25 | -------------------------------------------------------------------------------- /src/options.kcfg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 0 10 | 11 | 12 | 30 13 | 14 | 15 | 30 16 | 17 | 18 | 0.2 19 | 0.0 20 | 1.0 21 | 22 | 23 | 5 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/plugin.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Vlad Zagorodniy 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Own 19 | #include "YetAnotherMagicLampEffect.h" 20 | 21 | KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(YetAnotherMagicLampEffect, 22 | "metadata.json", 23 | return YetAnotherMagicLampEffect::supported();, 24 | return false;) 25 | 26 | #include "plugin.moc" 27 | --------------------------------------------------------------------------------