├── .gitignore ├── COPYING ├── ChangeLog ├── Makefile ├── README.md ├── TODO ├── gedit-with-csd.png ├── gedit-without-csd.png ├── gtk3-nocsd.1 ├── gtk3-nocsd.bash-completion ├── gtk3-nocsd.c ├── gtk3-nocsd.in ├── test-dummylib.c ├── test-now.c └── test-static-tls.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.so.0 2 | *.o 3 | *~ 4 | gtk3-nocsd 5 | testlibs/ 6 | test-static-tls 7 | test-now 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 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 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | gtk3-nocsd ChangeLog 2 | ==================== 3 | 4 | New in version 4 (unreleased) 5 | ----------------------------- 6 | 7 | * Support python-gi again by not caching the result of the version 8 | check if Gtk is not yet loaded. (python-gi loads Glib before it 9 | loads Gtk.) 10 | * Handle the case when both Gtk+3 and Gtk+2 are loaded (e.g. via 11 | different plugins), but Gtk+2 is used. 12 | 13 | New in version 3 14 | ---------------- 15 | 16 | * Improvements to the documentation (Thanks to Bharat G) 17 | * Add bash completion support for gtk3-nocsd wrapper 18 | * Also override GtkDialog's GtkBuildable->add_child and the instance 19 | initializer of GtkShortcutsWindow; previously CSDs weren't removed 20 | from all windows because of that 21 | * Drop compositor trick in Gtk+3 3.16.1 or higher; we reimplement the 22 | set_titlebar function there anyway, and the compositor trick only 23 | causes problems with newer Gtk+3 versions 24 | * Selectively disable decorations: don't drop the application menu 25 | icon by using set_show_close_button (FALSE), but instead replace 26 | the decoration_layout string of header bars to remove the "icon", 27 | "minimize", "maximize" and "close" buttons 28 | * Add a test that verifies that no Glib/Gtk symbols are imported 29 | during link time (to make sure preloading works with BINDNOW 30 | binaries that don't use Gtk) 31 | * Apply custom CSS for cosmetic improvements of the header bar look 32 | when CSDs are disabled (should be largely theme-agnostic, but 33 | tested against Adwaita) 34 | * Thanks to Christoph Anton Mitterer for reporting many of these 35 | issues 36 | 37 | New in version 2 38 | ---------------- 39 | 40 | * fix crash because the development SONAME for Glib/GObject was 41 | referenced, instead of the SONAME of the package itself 42 | (Thanks to Simon McVittie) 43 | * fix crash with some GDK2 applications 44 | * add wrapper script for starting applications without CSDs 45 | (Thanks to nandhp) 46 | * don't use compiler-supported TLS (__thread) but rather use 47 | pthread_(get|set)specific, to make sure no DTV overflow entry 48 | is used by this library (fixes GitHub issue #11) 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG_CONFIG ?= pkg-config 2 | CFLAGS ?= -O2 -g 3 | override CFLAGS += $(shell ${PKG_CONFIG} --cflags gtk+-3.0) $(shell ${PKG_CONFIG} --cflags gobject-introspection-1.0) -pthread -Wall -Wextra -Wno-unused-parameter 4 | CFLAGS_LIB = $(filter-out -fPIE -fpie -pie,$(CFLAGS)) -fPIC 5 | LDFLAGS_LIB = $(filter-out -fPIE -fpie -pie,$(LDFLAGS)) -fPIC 6 | 7 | prefix ?= /usr/local 8 | libdir ?= $(prefix)/lib 9 | bindir ?= $(prefix)/bin 10 | datadir ?= ${prefix}/share 11 | mandir ?= $(datadir)/man 12 | bashcompletiondir ?= ${datadir}/bash-completion/completions 13 | 14 | all: libgtk3-nocsd.so.0 gtk3-nocsd 15 | 16 | clean: 17 | rm -f libgtk3-nocsd.so.0 *.o gtk3-nocsd test-static-tls test-now *~ 18 | [ ! -d testlibs ] || rm -r testlibs 19 | 20 | libgtk3-nocsd.so.0: gtk3-nocsd.o 21 | $(CC) -ldl -shared $(CFLAGS_LIB) $(LDFLAGS_LIB) -Wl,-soname,libgtk3-nocsd.so.0 -o $@ $^ $(LDLIBS) 22 | 23 | gtk3-nocsd.o: gtk3-nocsd.c 24 | $(CC) $(CPPFLAGS) $(CFLAGS_LIB) -o $@ -c $< 25 | 26 | gtk3-nocsd: gtk3-nocsd.in 27 | sed 's|@@libdir@@|$(libdir)|g' < $< > $@ 28 | chmod +x $@ 29 | 30 | install: 31 | install -D -m 0644 libgtk3-nocsd.so.0 $(DESTDIR)$(libdir)/libgtk3-nocsd.so.0 32 | install -D -m 0755 gtk3-nocsd $(DESTDIR)$(bindir)/gtk3-nocsd 33 | install -D -m 0644 gtk3-nocsd.1 $(DESTDIR)$(mandir)/man1/gtk3-nocsd.1 34 | install -D -m 0644 gtk3-nocsd.bash-completion $(DESTDIR)$(bashcompletiondir)/gtk3-nocsd 35 | 36 | check: libgtk3-nocsd.so.0 testlibs/stamp test-static-tls test-now 37 | @echo "RUNNING: test-symbols" 38 | @# Force LD_BIND_NOW to make sure we don't accidentally import 39 | @# any symbols from glib/gdk/gtk directly. (This ensures 40 | @# compatibility with software that is linked with -Wl,-z,now.) 41 | @LD_PRELOAD=./libgtk3-nocsd.so.0 LD_BIND_NOW=1 ./test-now 42 | @echo "RUNNING: test-static-tls" 43 | @[ "$$(LD_PRELOAD= ./test-static-tls none)" = "$$(LD_PRELOAD=./libgtk3-nocsd.so.0 ./test-static-tls gtk3-nocsd)" ] || \ 44 | { echo " Without any library preloaded: can dlopen() up to the following number of libraries with static TLS:" ; \ 45 | echo -n " " ; LD_PRELOAD= ./test-static-tls none ; \ 46 | echo " With libgtk3-nocsd preloaded, can dlopen() up to the following number of libraries with static TLS:"; \ 47 | echo -n " " ; LD_PRELOAD=./libgtk3-nocsd.so.0 ./test-static-tls gtk3-nocsd ; \ 48 | echo " These should match, but they don't." ; \ 49 | exit 1; \ 50 | } 51 | 52 | testlibs/stamp: test-dummylib.c 53 | @# Build a lot of dummy libraries. test-static-tls tries to load all 54 | @# of these libraries with dlopen(), which will fail at some point 55 | @# (because the DTV overflow entries are used up). Build a lot of 56 | @# them so even if the current default changes the cutoff point can 57 | @# be determined. 58 | mkdir -p testlibs 59 | for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \ 60 | a b c d e f g h i j k l m n o p q r s t u v w x y z \ 61 | 0 1 2 3 4 5 6 7 8 9 ; do \ 62 | $(CC) $(CPPFLAGS) $(CFLAGS_LIB) -ftls-model=initial-exec -DTESTLIB_NAME=$$i -c -o testlibs/libdummy-$$i.o test-dummylib.c ; \ 63 | $(CC) -shared $(CFLAGS_LIB) $(LDFLAGS_LIB) -Wl,-soname,libdummy-$$i.so.0 -o testlibs/libdummy-$$i.so.0 testlibs/libdummy-$$i.o $(LDLIBS) ; \ 64 | done 65 | touch testlibs/stamp 66 | 67 | test-static-tls: test-static-tls.o 68 | $(CC) $(CFLAGS) $(LDFLAGS) -o test-static-tls test-static-tls.o -ldl 69 | 70 | test-now: test-now.o 71 | $(CC) $(CFLAGS) $(LDFLAGS) -o test-now test-now.o -ldl 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gtk3-nocsd 2 | 3 | gtk3-nocsd is a small module used to disable the client side decoration 4 | of Gtk+ 3. 5 | 6 | ## Introduction: 7 | Since Gtk+ 3.10, its developers added a so-called header bar or custom 8 | title bar. With this and the client-side decoration, the original title 9 | bar and window border provided by the window manager are disabled by 10 | Gtk+. This makes all Gtk+ 3 programs look alike. Even worse, this may 11 | break some window manager or compositors. 12 | 13 | Unfortunately, the Gtk+ developers decided to be against the existing 14 | standards and provide "no option" to turn it off. 15 | 16 | Luckily, with gtk3-nocsd, we still have a way to (partially) turn it 17 | off. Window manager (title bar and window border) can be re-enabled. 18 | 19 | ## Preview: 20 | This is how the gtk3 windows look like before and after using 21 | `gtk3-nocsd`. 22 | 23 | ![Screenshot of gedit with CSDs still enabled](gedit-with-csd.png) 24 | 25 | ![Screenshot of gedit with CSDs disabled by gtk3-nocsd](gedit-without-csd.png) 26 | 27 | # How to use: 28 | 29 | * gtk3-nocsd should work with all Gtk+ 3 versions. 30 | 31 | * Install necessary packages: 32 | 33 | * On Debian-based systems (including Ubuntu and Mint), install 34 | `pkg-config`, `libgtk-3-dev`, `libgirepository1.0-dev`. 35 | 36 | * On Fedora-based distros (including RHEL, CentOS), install 37 | `pkgconfig`, `gtk3-devel`, `gtk+-devel`, `gobject-introspection-devel`. 38 | 39 | * Build the code. Run `make` from command line. 40 | After this you'll have the files `gtk3-nocsd`and `libgtk3-nocsd.so.0` 41 | in the same directory. 42 | 43 | * Now to run individual Gtk+ 3 apps (say gedit) using this hack, use 44 | the command `./gtk3-nocsd gedit` from the same directory. 45 | 46 | * To have all Gtk+ 3 apps (of current user) use this hack, export some 47 | environment variables in your `~/.bashrc`: 48 | 49 | export GTK_CSD=0 50 | export LD_PRELOAD=<"full path" of your libgtk3-nocsd.so.0 file> 51 | 52 | * On Arch Linux, you should use `~/.xsession` instead of `~/.bashrc` 53 | for the CSDs to be disabled properly. 54 | 55 | * On Debian-based systems with graphical login, instead modify (or 56 | create) `~/.xsessionrc` and add the following code: 57 | 58 | if [ -n "$STARTUP" ]; then 59 | BASESTARTUP=${STARTUP%% *} 60 | BASESTARTUP=${BASESTARTUP##*/} 61 | if [ "$BASESTARTUP" = x-session-manager ]; then 62 | BASESTARTUP=$(basename $(readlink /etc/alternatives/x-session-manager)) 63 | fi 64 | if [ x"$BASESTARTUP" = x"${BASESTARTUP#gnome-session}" ] ; then 65 | export GTK_CSD=0 66 | STARTUP="env LD_PRELOAD=/path/to/libgtk3-nocsd.so.0 $STARTUP" 67 | fi 68 | fi 69 | 70 | gtk3-nocsd is now packaged for Debian though (see below), so manual 71 | installation may not be necessary. 72 | 73 | * Re-login to make the environment variables take effect. 74 | 75 | * Hooray! GTK+ 3 client-side decorations are disabled now. 76 | 77 | # Application title: 78 | 79 | By default gtk3-nocsd will hide Gnome applications title. 80 | 81 | If you don't want that title to be hidden set enviroment variable `GTK3NOCSD_SHOW_HEADER` to `1`. 82 | 83 | Example: `env GTK3NOCSD_SHOW_HEADER=1 gtk3-nocsd gnome-mahjongg`. 84 | 85 | # Distribution packages: 86 | 87 | gtk3-nocsd is packaged in Debian's unstable and testing distributions, 88 | see [gtk3-nocsd in Debian's package database](https://packages.debian.org/testing/gtk3-nocsd). 89 | The Debian package already comes with integration code to automatically 90 | disable CSDs when installed, so after package installation only a 91 | re-login is required to have CSDs disabled on non-GNOME desktops. 92 | 93 | There is also a [gtk3-nocsd-git](https://aur.archlinux.org/packages/gtk3-nocsd-git/) 94 | package for Arch Linux. 95 | 96 | # How it works: 97 | 98 | `$LD_PRELOAD` is used to override several Gdk and glib/gobject APIs to 99 | intercept related calls Gtk+ 3 uses to setup CSDs. For older versions 100 | of Gtk+ 3, while it is trying to initialize CSDs, it is led to believe 101 | that there is no compositor available, so CSDs are not added. For later 102 | Gtk+ 3 versions (3.16.1+), the `gtk_window_set_titlebar` method is 103 | reimplemented, as tricking Gtk+ 3 into thinking the compositor is 104 | disabled has side effects and is not sufficent anymore. 105 | 106 | Additionally, as gtk_window_set_titlebar is also called from Gtk+ 107 | internally (and LD_PRELOAD cannot override function calls within a 108 | library), several other places in Gtk+3 (e.g. buildable interfaces for 109 | GtkWindow and GtkDialog) are also overridden to ensure that CSDs are 110 | disabled. 111 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | gtk3-nocsd TODO 2 | --------------- 3 | 4 | - Investigate whether we should force gtk-dialogs-use-header to FALSE 5 | (the current default in non-GNOME environments) or not. 6 | 7 | - Hide header bar if it were to contain only the title. 8 | 9 | - Split source code into multiple files. 10 | 11 | - Use same code paths for all Gtk versions, remove compositing hack 12 | completely. 13 | 14 | - Potentially add support to selectively enable gtk3-nocsd only for 15 | specific applications. (How?) 16 | -------------------------------------------------------------------------------- /gedit-with-csd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaWertun/gtk3-nocsd/512c2bd866d71def6ffd4d85ca98ae12fe989c62/gedit-with-csd.png -------------------------------------------------------------------------------- /gedit-without-csd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZaWertun/gtk3-nocsd/512c2bd866d71def6ffd4d85ca98ae12fe989c62/gedit-without-csd.png -------------------------------------------------------------------------------- /gtk3-nocsd.1: -------------------------------------------------------------------------------- 1 | .TH gtk3-nocsd 1 "October 2015" 2 | .SH NAME 3 | gtk3-nocsd \- transparently disable Gtk+3 client side decorations (CSD) 4 | .SH SYNOPSIS 5 | .B gtk3-nocsd 6 | \fIcommand\fR [ \fIcommand arguments\fR ... ] 7 | .TP 8 | .B symlinked-command 9 | [ \fIcommand arguments\fR ... ] 10 | .SH DESCRIPTION 11 | \fBgtk3-nocsd\fR runs a command in the environment where Gtk+3 client side 12 | decorations (CSD) are disabled, by using LD_PRELOAD to load the library 13 | \fBlibgtk3-nocsd.so.0\fR that overrides some GLib, GDK and Gtk API calls 14 | with customized variants. 15 | .SH OPTIONS 16 | .TP 17 | .I command 18 | The command to execute. It may be either a full path or the name of the command 19 | in PATH. In case command cannot be found in PATH, \fBgtk3-nocsd\fR will fail. 20 | .TP 21 | .I command arguments 22 | Arbitrary number of arguments to pass to the command being executed. 23 | .SH USAGE VIA SYMLINKS 24 | If a symlink to \fBgtk3-nocsd\fR under a different name is called, 25 | \fBgtk3-nocsd\fR will assume that the name it was called under is the name 26 | of the command that is supposed to be executed. It will look for the command 27 | in the PATH environment variable, excluding itself, and execute it with the 28 | proper environment variables set. This is useful when is not desirable to add 29 | gtk3-nocsd to the system-wide LD_PRELOAD or if it should be applied only to 30 | certain applications. 31 | .SH CAVEATS 32 | .P 33 | When using \fBgtk3-nocsd\fR with \fBsetarch\fR (including alias such as 34 | \fBlinux32\fR), or anyway with chroots with different architectures than the 35 | host's, make sure to install the matching architecture version of 36 | \fBlibgtk3-nocsd.so.0\fR both in the \fBsetarch\fR environment and the host's 37 | environment. Note that the \fBgtk3-nocsd\fR binary must then be installed in 38 | a system path (\fI/usr/bin\fR, \fI/usr/local/bin\fR, ...), so that it will set 39 | LD_PRELOAD to just the library name and let the dynamic linker find it 40 | automatically - allowing the dynamic linker to pick the version for the 41 | correct architecture. If \fBgtk3-nocsd\fR is not installed in a system path, 42 | it will use a full path, allowing only for a single version of the library 43 | to be used. 44 | -------------------------------------------------------------------------------- /gtk3-nocsd.bash-completion: -------------------------------------------------------------------------------- 1 | # gtk3-nocsd bash completion module 2 | # 3 | # Copyright (C) 2014 Hong Jen Yee (PCMan) 4 | # 5 | # http://lxqt.org/ 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | 21 | _gtk3_nocsd() { 22 | local cur=${COMP_WORDS[COMP_CWORD]} 23 | local OPTS='-h --help' 24 | for (( i=1; i <= COMP_CWORD; i++ )); do 25 | if [[ ${COMP_WORDS[i]} != -* ]]; then 26 | local command=${COMP_WORDS[i]} 27 | _command_offset $i 28 | return 29 | fi 30 | done 31 | COMPREPLY=( $(compgen -W "$OPTS" -- "$cur") ) 32 | return 0 33 | } 34 | complete -F _gtk3_nocsd gtk3-nocsd 35 | -------------------------------------------------------------------------------- /gtk3-nocsd.c: -------------------------------------------------------------------------------- 1 | /* 2 | gtk3-nocsd, a module used to disable GTK+3 client side decoration. 3 | 4 | Copyright (C) 2014 Hong Jen Yee (PCMan) 5 | 6 | http://lxqt.org/ 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; either 11 | version 2.1 of the License, or (at your option) any later version. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public 19 | License along with this library; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | */ 22 | 23 | #define _GNU_SOURCE 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include 41 | 42 | #ifdef __FreeBSD__ 43 | #include 44 | #define ElfW(t) Elf##_##t 45 | #endif 46 | 47 | #ifdef G_TYPE_INSTANCE_GET_PRIVATE 48 | # undef G_TYPE_INSTANCE_GET_PRIVATE 49 | #endif 50 | #define G_TYPE_INSTANCE_GET_PRIVATE(instance, g_type, c_type) ((c_type*) g_type_instance_get_private ((GTypeInstance*) (instance),(g_type))) 51 | 52 | typedef void (*gtk_window_buildable_add_child_t) (GtkBuildable *buildable, GtkBuilder *builder, GObject *child, const gchar *type); 53 | typedef GObject* (*gtk_dialog_constructor_t) (GType type, guint n_construct_properties, GObjectConstructParam *construct_params); 54 | typedef char *(*gtk_check_version_t) (guint required_major, guint required_minor, guint required_micro); 55 | typedef void (*gtk_header_bar_set_property_t) (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); 56 | typedef void (*gtk_header_bar_realize_t) (GtkWidget *widget); 57 | typedef void (*gtk_header_bar_unrealize_t) (GtkWidget *widget); 58 | typedef void (*gtk_header_bar_hierarchy_changed_t) (GtkWidget *widget, GtkWidget *previous_toplevel); 59 | 60 | enum { 61 | GTK_LIBRARY, 62 | GDK_LIBRARY, 63 | GOBJECT_LIBRARY, 64 | GLIB_LIBRARY, 65 | GIREPOSITORY_LIBRARY, 66 | NUM_LIBRARIES 67 | }; 68 | 69 | #ifndef GTK_LIBRARY_SONAME 70 | #define GTK_LIBRARY_SONAME "libgtk-3.so.0" 71 | #endif 72 | 73 | #ifndef GDK_LIBRARY_SONAME 74 | #define GDK_LIBRARY_SONAME "libgdk-3.so.0" 75 | #endif 76 | 77 | #ifndef GDK_LIBRARY_SONAME_V2 78 | #define GDK_LIBRARY_SONAME_V2 "libgdk-x11-2.0.so.0" 79 | #endif 80 | 81 | #ifndef GOBJECT_LIBRARY_SONAME 82 | #define GOBJECT_LIBRARY_SONAME "libgobject-2.0.so.0" 83 | #endif 84 | 85 | #ifndef GLIB_LIBRARY_SONAME 86 | #define GLIB_LIBRARY_SONAME "libglib-2.0.so.0" 87 | #endif 88 | 89 | #ifndef GIREPOSITORY_LIBRARY_SONAME 90 | #define GIREPOSITORY_LIBRARY_SONAME "libgirepository-1.0.so.1" 91 | #endif 92 | 93 | static const char *library_sonames[NUM_LIBRARIES] = { 94 | GTK_LIBRARY_SONAME, 95 | GDK_LIBRARY_SONAME, 96 | GOBJECT_LIBRARY_SONAME, 97 | GLIB_LIBRARY_SONAME, 98 | GIREPOSITORY_LIBRARY_SONAME 99 | }; 100 | 101 | static const char *library_sonames_v2[NUM_LIBRARIES] = { 102 | NULL, 103 | GDK_LIBRARY_SONAME_V2, 104 | NULL, 105 | NULL, 106 | NULL 107 | }; 108 | 109 | static void * volatile library_handles[NUM_LIBRARIES * 2] = { 110 | NULL, 111 | NULL, 112 | NULL, 113 | NULL, 114 | NULL, 115 | NULL, 116 | NULL, 117 | NULL, 118 | NULL, 119 | NULL 120 | }; 121 | 122 | static pthread_key_t key_tls; 123 | static pthread_once_t key_tls_once = PTHREAD_ONCE_INIT; 124 | 125 | /* Marking both as volatile here saves the trouble of caring about 126 | * memory barriers. */ 127 | static volatile gboolean is_compatible_gtk_version_cached = FALSE; 128 | static volatile gboolean is_compatible_gtk_version_checked = FALSE; 129 | static volatile int gtk2_active; 130 | 131 | typedef struct gtk3_nocsd_tls_data_t { 132 | // When set to true, this override gdk_screen_is_composited() and let it 133 | // return FALSE temporarily. Then, client-side decoration (CSD) cannot be initialized. 134 | volatile int disable_composite; 135 | volatile int signal_capture_handler; 136 | volatile int in_info_collect; 137 | const char *volatile signal_capture_name; 138 | volatile gpointer signal_capture_instance; 139 | volatile gpointer signal_capture_data; 140 | volatile GCallback signal_capture_callback; 141 | } gtk3_nocsd_tls_data_t; 142 | 143 | static gtk3_nocsd_tls_data_t *tls_data_location(); 144 | #define TLSD (tls_data_location()) 145 | 146 | __attribute__((destructor)) static void cleanup_library_handles(void) { 147 | int i; 148 | 149 | for (i = 0; i < NUM_LIBRARIES * 2; i++) { 150 | if (library_handles[i]) 151 | (void) dlclose(library_handles[i]); 152 | } 153 | } 154 | 155 | static void *find_orig_function(int try_gtk2, int library_id, const char *symbol) { 156 | void *handle; 157 | void *symptr; 158 | 159 | /* Ok, so in case both gtk2 + gtk3 are loaded, but we are using 160 | * gtk2, we don't know what RTLD_NEXT is going to choose - so we 161 | * must explicitly pick up the gtk2 versions... */ 162 | if (try_gtk2 && gtk2_active) 163 | goto try_gtk2_version; 164 | 165 | /* This will work in most cases, and is completely thread-safe. */ 166 | handle = dlsym(RTLD_NEXT, symbol); 167 | if (handle) 168 | return handle; 169 | 170 | /* dlsym(RTLD_NEXT, ...) will fail if the library using the symbol 171 | * is dlopen()d itself (e.g. a python module or similar), so what 172 | * we need to do is load the corresponding library ourselves and 173 | * look for the symbol there. We keep a reference to that library 174 | * so that we may close it again in a destructor function once we 175 | * are unloaded. dlopen()/dlclose() are refcounted, so we need to 176 | * use a mutex to protect dlopen(), otherwise we possibly could 177 | * take out more than one reference on the library and the cleanup 178 | * function wouldn't completely free it. 179 | * 180 | * Note that we use RTLD_NOLOAD, since we don't want to mask 181 | * problems if plugins aren't properly linked against gtk itself. */ 182 | handle = library_handles[library_id]; 183 | 184 | if (!handle) { 185 | static pthread_mutex_t handle_mutex = PTHREAD_MUTEX_INITIALIZER; 186 | pthread_mutex_lock(&handle_mutex); 187 | /* we need to check again inside the mutex-protected block */ 188 | handle = library_handles[library_id]; 189 | if (!handle) 190 | handle = dlopen(library_sonames[library_id], RTLD_LAZY | RTLD_NOLOAD); 191 | if (handle) 192 | library_handles[library_id] = handle; 193 | pthread_mutex_unlock(&handle_mutex); 194 | if (!handle) { 195 | if (try_gtk2) 196 | goto try_gtk2_version; 197 | return NULL; 198 | } 199 | } 200 | 201 | symptr = dlsym(handle, symbol); 202 | if (symptr || !try_gtk2) 203 | return symptr; 204 | 205 | try_gtk2_version: 206 | /* We overwrite some functions that are already available in GDK2. 207 | * So just trying to dlopen() the GDK3 library will not work here, 208 | * because GDK2 is going to be loaded instead. Therefore, retry 209 | * with the GDK2 library - but only do so if the try_gtk2 flag is 210 | * set, because we only want to do that for functions that were 211 | * already available in Gtk/GDK2. Functions that were introduced 212 | * in Gtk3 will not receive this treatment. 213 | * 214 | * We are very fortunate that the two relevant functions are 215 | * binary compatible between GDK2 and GDK3. 216 | */ 217 | 218 | /* try_gtk2 should not be set for functions were we don't have a 219 | * Gtk2 variant of the library. So this should always hold. 220 | * Nevertheless, be paranoid. */ 221 | if (!library_sonames_v2[library_id]) 222 | return NULL; 223 | 224 | /* Same logic as above, but we use an offset in the library 225 | * handles and use the v2 soname. */ 226 | handle = library_handles[NUM_LIBRARIES + library_id]; 227 | if (!handle) { 228 | static pthread_mutex_t handle_v2_mutex = PTHREAD_MUTEX_INITIALIZER; 229 | pthread_mutex_lock(&handle_v2_mutex); 230 | /* we need to check again inside the mutex-protected block */ 231 | handle = library_handles[NUM_LIBRARIES + library_id]; 232 | if (!handle) 233 | handle = dlopen(library_sonames_v2[library_id], RTLD_LAZY | RTLD_NOLOAD); 234 | if (handle) 235 | library_handles[NUM_LIBRARIES + library_id] = handle; 236 | pthread_mutex_unlock(&handle_v2_mutex); 237 | if (!handle) 238 | return NULL; 239 | } 240 | 241 | return dlsym(handle, symbol); 242 | } 243 | 244 | /* If a binary is compiled with ELF flag NOW (corresponding to RTLD_NOW), 245 | * but is not linked against gtk, if we use symbols from gtk the binary 246 | * they will fail to load. But we can't link this library against gtk3, 247 | * because we don't want to pull that in to every program and that 248 | * would also be incompatible with gtk2. Therefore, make sure we import 249 | * every function, not just those that we override, at runtime. */ 250 | #define HIDDEN_NAME2(a,b) a ## b 251 | #define NAME2(a,b) HIDDEN_NAME2(a,b) 252 | #define RUNTIME_IMPORT_FUNCTION(try_gtk2, library, function_name, return_type, arg_def_list, arg_use_list) \ 253 | static return_type NAME2(rtlookup_, function_name) arg_def_list { \ 254 | static return_type (*orig_func) arg_def_list = NULL;\ 255 | if (!orig_func) \ 256 | orig_func = find_orig_function(try_gtk2, library, #function_name); \ 257 | return orig_func arg_use_list; \ 258 | } 259 | 260 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_css_provider_new, GtkCssProvider *, (), ()) 261 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_css_provider_load_from_data, void, (GtkCssProvider *provider, const gchar *data, gssize length, GError **error), (provider, data, length, error)) 262 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_window_new, GtkWidget *, (GtkWindowType type), (type)) 263 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_new, GtkWidget *, (), ()) 264 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_window_get_type, GType, (), ()) 265 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_get_type, GType, (), ()) 266 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_window_get_titlebar, GtkWidget *, (GtkWindow *window), (window)) 267 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_type, GType, (), ()) 268 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_buildable_get_type, GType, (), ()) 269 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_window_set_titlebar, void, (GtkWindow *window, GtkWidget *titlebar), (window, titlebar)) 270 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_set_show_close_button, void, (GtkHeaderBar *bar, gboolean setting), (bar, setting)) 271 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_set_decoration_layout, void, (GtkHeaderBar *bar, const gchar *layout), (bar, layout)) 272 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_get_decoration_layout, const gchar *, (GtkHeaderBar *bar), (bar)) 273 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_header_bar_get_custom_title, GtkWidget *, (GtkHeaderBar *bar), (bar)) 274 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_style_context_add_class, void, (GtkStyleContext *context, const gchar *class_name), (context, class_name)) 275 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_style_context_remove_class, void, (GtkStyleContext *context, const gchar *class_name), (context, class_name)) 276 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_style_context_add_provider_for_screen, void, (GdkScreen *screen, GtkStyleProvider *provider, guint priority), (screen, provider, priority)) 277 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_style_provider_get_type, GType, (), ()) 278 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_hide, void, (GtkWidget *widget), (widget)) 279 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_destroy, void, (GtkWidget *widget), (widget)) 280 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_mapped, gboolean, (GtkWidget *widget), (widget)) 281 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_realized, gboolean, (GtkWidget *widget), (widget)) 282 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_style_context, GtkStyleContext *, (GtkWidget *widget), (widget)) 283 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_map, void, (GtkWidget *widget), (widget)) 284 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_set_parent, void, (GtkWidget *widget, GtkWidget *parent), (widget, parent)) 285 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_unrealize, void, (GtkWidget *widget), (widget)) 286 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_realize, void, (GtkWidget *widget), (widget)) 287 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_settings, GtkSettings *, (GtkWidget *widget), (widget)) 288 | RUNTIME_IMPORT_FUNCTION(0, GTK_LIBRARY, gtk_widget_get_toplevel, GtkWidget *, (GtkWidget *widget), (widget)) 289 | RUNTIME_IMPORT_FUNCTION(0, GDK_LIBRARY, gdk_screen_get_default, GdkScreen *, (), ()) 290 | RUNTIME_IMPORT_FUNCTION(0, GDK_LIBRARY, gdk_window_get_user_data, void, (GdkWindow *window, gpointer *data), (window, data)) 291 | RUNTIME_IMPORT_FUNCTION(1, GDK_LIBRARY, gdk_screen_is_composited, gboolean, (GdkScreen *screen), (screen)) 292 | RUNTIME_IMPORT_FUNCTION(1, GDK_LIBRARY, gdk_window_set_decorations, void, (GdkWindow *window, GdkWMDecoration decorations), (window, decorations)) 293 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_get_data, gpointer, (GObject *object, const gchar *key), (object, key)) 294 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_set_data, void, (GObject *object, const gchar *key, gpointer data), (object, key, data)) 295 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_ref, gpointer, (gpointer object), (object)) 296 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_unref, void, (gpointer object), (object)) 297 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_check_class_cast, GTypeClass *, (GTypeClass *g_class, GType is_a_type), (g_class, is_a_type)) 298 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_check_instance_is_a, gboolean, (GTypeInstance *instance, GType iface_type), (instance, iface_type)) 299 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_check_instance_cast, GTypeInstance *, (GTypeInstance *instance, GType iface_type), (instance, iface_type)) 300 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_class_find_property, GParamSpec *, (GObjectClass *oclass, const gchar *property_name), (oclass, property_name)) 301 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_register_static_simple, GType, (GType parent_type, const gchar *type_name, guint class_size, GClassInitFunc class_init, guint instance_size, GInstanceInitFunc instance_init, GTypeFlags flags), (parent_type, type_name, class_size, class_init, instance_size, instance_init, flags)) 302 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_add_interface_static, void, (GType instance_type, GType interface_type, const GInterfaceInfo *info), (instance_type, interface_type, info)) 303 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_add_instance_private, gint, (GType class_type, gsize private_size), (class_type, private_size)) 304 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_instance_get_private, gpointer, (GTypeInstance *instance, GType private_type), (instance, private_type)) 305 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_value_table_peek, GTypeValueTable *, (GType type), (type)) 306 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_check_instance_is_fundamentally_a, gboolean, (GTypeInstance *instance, GType fundamental_type), (instance, fundamental_type)) 307 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_signal_connect_data, gulong, (gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags), (instance, detailed_signal, c_handler, data, destroy_data, connect_flags)) 308 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_signal_handlers_disconnect_matched, guint, (gpointer instance, GSignalMatchType mask, guint signal_id, GQuark detail, GClosure *closure, gpointer func, gpointer data), (instance, mask, signal_id, detail, closure, func, data)) 309 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_get_valist, void, (GObject *object, const gchar *first_property_name, va_list var_args), (object, first_property_name, var_args)) 310 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_object_get_property, void, (GObject *object, const gchar *property_name, GValue *value), (object, property_name, value)) 311 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_value_init, GValue *, (GValue *value, GType g_type), (value, g_type)) 312 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_value_unset, void, (GValue *value), (value)) 313 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_value_get_string, const gchar *, (const GValue *value), (value)) 314 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_value_get_boolean, gboolean, (const GValue *value), (value)) 315 | RUNTIME_IMPORT_FUNCTION(0, GOBJECT_LIBRARY, g_type_name, const gchar *, (GType type), (type)) 316 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_getenv, gchar *, (const char *name), (name)) 317 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_logv, void, (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, va_list args), (log_domain, log_level, format, args)) 318 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_free, void, (gpointer mem), (mem)) 319 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_strdup, gchar *, (const gchar *str), (str)) 320 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_strfreev, void, (gchar **str_array), (str_array)) 321 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_strlcat, gsize, (gchar *dest, const gchar *src, gsize dest_size), (dest, src, dest_size)) 322 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_strlcpy, gsize, (gchar *dest, const gchar *src, gsize dest_size), (dest, src, dest_size)) 323 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_strsplit, gchar **, (const gchar *string, const gchar *delimiter, gint max_tokens), (string, delimiter, max_tokens)) 324 | RUNTIME_IMPORT_FUNCTION(0, GLIB_LIBRARY, g_assertion_message_expr, void, (const char *domain, const char *file, int line, const char *func, const char *expr), (domain, file, line, func, expr)) 325 | RUNTIME_IMPORT_FUNCTION(0, GIREPOSITORY_LIBRARY, g_function_info_prep_invoker, gboolean, (GIFunctionInfo *info, GIFunctionInvoker *invoker, GError **error), (info, invoker, error)) 326 | 327 | /* All methods that we want to overwrite are named orig_, all methods 328 | * that we just want to call (either directly or indirectrly) 329 | */ 330 | #define gtk_css_provider_new rtlookup_gtk_css_provider_new 331 | #define gtk_css_provider_load_from_data rtlookup_gtk_css_provider_load_from_data 332 | #define gtk_window_new rtlookup_gtk_window_new 333 | #define gtk_header_bar_new rtlookup_gtk_header_bar_new 334 | #define gtk_window_get_type rtlookup_gtk_window_get_type 335 | #define gtk_header_bar_get_type rtlookup_gtk_header_bar_get_type 336 | #define gtk_widget_get_type rtlookup_gtk_widget_get_type 337 | #define gtk_buildable_get_type rtlookup_gtk_buildable_get_type 338 | #define gtk_window_get_titlebar rtlookup_gtk_window_get_titlebar 339 | #define orig_gtk_window_set_titlebar rtlookup_gtk_window_set_titlebar 340 | #define orig_gtk_header_bar_set_show_close_button rtlookup_gtk_header_bar_set_show_close_button 341 | #define orig_gtk_header_bar_set_decoration_layout rtlookup_gtk_header_bar_set_decoration_layout 342 | #define orig_gtk_header_bar_get_decoration_layout rtlookup_gtk_header_bar_get_decoration_layout 343 | #define gtk_header_bar_get_custom_title rtlookup_gtk_header_bar_get_custom_title 344 | #define gtk_style_context_add_class rtlookup_gtk_style_context_add_class 345 | #define gtk_style_context_remove_class rtlookup_gtk_style_context_remove_class 346 | #define gtk_style_context_add_provider_for_screen rtlookup_gtk_style_context_add_provider_for_screen 347 | #define gtk_style_provider_get_type rtlookup_gtk_style_provider_get_type 348 | #define gtk_widget_hide rtlookup_gtk_widget_hide 349 | #define gtk_widget_destroy rtlookup_gtk_widget_destroy 350 | #define gtk_widget_get_mapped rtlookup_gtk_widget_get_mapped 351 | #define gtk_widget_get_realized rtlookup_gtk_widget_get_realized 352 | #define gtk_widget_get_style_context rtlookup_gtk_widget_get_style_context 353 | #define gtk_widget_map rtlookup_gtk_widget_map 354 | #define gtk_widget_set_parent rtlookup_gtk_widget_set_parent 355 | #define gtk_widget_unrealize rtlookup_gtk_widget_unrealize 356 | #define gtk_widget_realize rtlookup_gtk_widget_realize 357 | #define gdk_screen_get_default rtlookup_gdk_screen_get_default 358 | #define gdk_window_get_user_data rtlookup_gdk_window_get_user_data 359 | #define orig_gdk_screen_is_composited rtlookup_gdk_screen_is_composited 360 | #define orig_gdk_window_set_decorations rtlookup_gdk_window_set_decorations 361 | #define g_list_free rtlookup_g_list_free 362 | #define g_object_get_data rtlookup_g_object_get_data 363 | #define g_object_set_data rtlookup_g_object_set_data 364 | #define g_type_check_class_cast rtlookup_g_type_check_class_cast 365 | #define g_type_check_instance_is_a rtlookup_g_type_check_instance_is_a 366 | #define g_type_check_instance_cast rtlookup_g_type_check_instance_cast 367 | #define g_object_class_find_property rtlookup_g_object_class_find_property 368 | #define g_object_get_valist rtlookup_g_object_get_valist 369 | #define g_object_get_property rtlookup_g_object_get_property 370 | #ifdef g_object_ref 371 | # undef g_object_ref 372 | #endif 373 | #define g_object_ref rtlookup_g_object_ref 374 | #define g_object_unref rtlookup_g_object_unref 375 | #define g_value_init rtlookup_g_value_init 376 | #define g_value_unset rtlookup_g_value_unset 377 | #define g_value_get_string rtlookup_g_value_get_string 378 | #define g_value_get_boolean rtlookup_g_value_get_boolean 379 | #define g_type_name rtlookup_g_type_name 380 | #define orig_g_type_register_static_simple rtlookup_g_type_register_static_simple 381 | #define orig_g_type_add_interface_static rtlookup_g_type_add_interface_static 382 | #define orig_g_type_add_instance_private rtlookup_g_type_add_instance_private 383 | #define orig_g_signal_connect_data rtlookup_g_signal_connect_data 384 | #define g_signal_handlers_disconnect_matched rtlookup_g_signal_handlers_disconnect_matched 385 | #define g_type_instance_get_private rtlookup_g_type_instance_get_private 386 | #define g_type_value_table_peek rtlookup_g_type_value_table_peek 387 | #define g_type_check_instance_is_fundamentally_a rtlookup_g_type_check_instance_is_fundamentally_a 388 | #define g_getenv rtlookup_g_getenv 389 | #define g_logv rtlookup_g_logv 390 | #define g_log static_g_log 391 | #define g_free rtlookup_g_free 392 | #define g_strdup rtlookup_g_strdup 393 | #define g_strfreev rtlookup_g_strfreev 394 | #define g_strlcat rtlookup_g_strlcat 395 | #define g_strlcpy rtlookup_g_strlcpy 396 | #define g_strsplit rtlookup_g_strsplit 397 | #define gtk_widget_get_settings rtlookup_gtk_widget_get_settings 398 | #define gtk_widget_get_toplevel rtlookup_gtk_widget_get_toplevel 399 | #define g_assertion_message_expr rtlookup_g_assertion_message_expr 400 | #define orig_g_function_info_prep_invoker rtlookup_g_function_info_prep_invoker 401 | 402 | /* Forwarding of varadic functions is tricky. */ 403 | static void static_g_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...) 404 | { 405 | va_list args; 406 | va_start (args, format); 407 | g_logv (log_domain, log_level, format, args); 408 | va_end (args); 409 | } 410 | 411 | int check_gtk2_callback(struct dl_phdr_info *info, size_t size, void *pointer) 412 | { 413 | ElfW(Half) n; 414 | 415 | if (G_UNLIKELY(strstr(info->dlpi_name, GDK_LIBRARY_SONAME_V2))) { 416 | for (n = 0; n < info->dlpi_phnum; n++) { 417 | uintptr_t start = (uintptr_t) (info->dlpi_addr + info->dlpi_phdr[n].p_vaddr); 418 | uintptr_t end = start + (uintptr_t) info->dlpi_phdr[n].p_memsz; 419 | if ((uintptr_t) pointer >= start && (uintptr_t) pointer < end) { 420 | gtk2_active = 1; 421 | /* The gtk version check could have already been cached 422 | * before we were able to determine that gtk2 is in 423 | * use, so force this to FALSE. (Regardless of the 424 | * _checked value.) */ 425 | is_compatible_gtk_version_cached = FALSE; 426 | return 0; 427 | } 428 | } 429 | } 430 | return 0; 431 | } 432 | 433 | static void detect_gtk2(void *pointer) 434 | { 435 | if (gtk2_active) 436 | return; 437 | /* There is a corner case where a program with plugins loads 438 | * multiple plugins, some of which are linked against gtk2, while 439 | * others are linked against gtk3. If the gtk2 plugins are used, 440 | * this causes problems if we detect gtk3 just on the fact of 441 | * whether gtk3 is loaded. Hence we iterate over all loaded 442 | * libraries and if the pointer passed to us is within the memory 443 | * region of gtk2, we set a global flag. */ 444 | dl_iterate_phdr(check_gtk2_callback, pointer); 445 | } 446 | 447 | static gboolean is_gtk_version_larger_or_equal2(guint major, guint minor, guint micro, int* gtk_loaded) { 448 | static gtk_check_version_t orig_func = NULL; 449 | if(!orig_func) 450 | orig_func = (gtk_check_version_t)find_orig_function(0, GTK_LIBRARY, "gtk_check_version"); 451 | 452 | /* We may have not been able to load the function IF a 453 | * gtk2-using plugin was loaded into a non-gtk application. In 454 | * that case, we don't want to do anything anyway, so just say 455 | * we aren't compatible. 456 | * 457 | * Note that if the application itself is using gtk2, RTLD_NEXT 458 | * will give us a reference to gtk_check_version. But since 459 | * that symbol is compatible with gtk3, this doesn't hurt. 460 | */ 461 | if (orig_func) { 462 | if (gtk_loaded) 463 | *gtk_loaded = TRUE; 464 | return (orig_func(major, minor, micro) == NULL); 465 | } else { 466 | if (gtk_loaded) 467 | *gtk_loaded = FALSE; 468 | return FALSE; 469 | } 470 | } 471 | 472 | static gboolean is_gtk_version_larger_or_equal(guint major, guint minor, guint micro) { 473 | return is_gtk_version_larger_or_equal2(major, minor, micro, NULL); 474 | } 475 | 476 | static gboolean are_csd_disabled() { 477 | static volatile int csd_disabled = -1; 478 | if (csd_disabled == -1) { 479 | const gchar *csd_env; 480 | csd_env = g_getenv ("GTK_CSD"); 481 | csd_disabled = csd_env != NULL && strcmp (csd_env, "1") != 0; 482 | } 483 | return csd_disabled; 484 | } 485 | 486 | static gboolean is_compatible_gtk_version() { 487 | int gtk_loaded = FALSE; 488 | 489 | if(G_UNLIKELY(!is_compatible_gtk_version_checked)) { 490 | if (gtk2_active) { 491 | is_compatible_gtk_version_cached = FALSE; 492 | } else if (!is_gtk_version_larger_or_equal2(3, 10, 0, >k_loaded)) { 493 | /* CSD was introduced there */ 494 | is_compatible_gtk_version_cached = FALSE; 495 | } else { 496 | is_compatible_gtk_version_cached = TRUE; 497 | } 498 | /* If in a dynamical program (e.g. using python-gi) Glib is loaded before 499 | * Gtk, then the Gtk version check is executed before Gtk is even loaded, 500 | * returning FALSE and caching it. This will not disable CSD if Gtk is 501 | * loaded later. To circumvent this, cache the value only if we know that 502 | * Gtk is loaded. */ 503 | if (gtk_loaded) 504 | is_compatible_gtk_version_checked = TRUE; 505 | } 506 | 507 | return is_compatible_gtk_version_cached; 508 | } 509 | 510 | static void set_has_custom_title(GtkWindow* window, gboolean set) { 511 | g_object_set_data(G_OBJECT(window), "custom_title", set ? GINT_TO_POINTER(1) : NULL); 512 | } 513 | 514 | static gboolean has_custom_title(GtkWindow* window) { 515 | return (gboolean)GPOINTER_TO_INT(g_object_get_data(G_OBJECT(window), "custom_title")); 516 | } 517 | 518 | typedef void (*on_titlebar_title_notify_t) (GtkHeaderBar *titlebar, GParamSpec *pspec, GtkWindow *self); 519 | typedef void (*update_window_buttons_t) (GtkHeaderBar *bar); 520 | typedef gboolean (*window_state_changed_t) (GtkWidget *widget, GdkEventWindowState *event, gpointer data); 521 | 522 | typedef struct gtk_window_private_info_t { 523 | gsize title_box_offset; 524 | on_titlebar_title_notify_t on_titlebar_title_notify; 525 | } gtk_window_private_info_t; 526 | 527 | typedef struct gtk_header_bar_private_info_t { 528 | gsize decoration_layout_offset; 529 | update_window_buttons_t update_window_buttons; 530 | window_state_changed_t window_state_changed; 531 | } gtk_header_bar_private_info_t; 532 | 533 | static GType gtk_window_type = -1; 534 | static GType gtk_header_bar_type = -1; 535 | 536 | static gtk_window_private_info_t gtk_window_private_info (); 537 | static gtk_header_bar_private_info_t gtk_header_bar_private_info (); 538 | 539 | static GtkStyleProvider *get_custom_css_provider () 540 | { 541 | static GtkStyleProvider *volatile provider = NULL; 542 | static pthread_mutex_t provider_mutex = PTHREAD_MUTEX_INITIALIZER; 543 | /* This CSS works around some design issues with the header bar 544 | * when used with gtk3-nocsd. We first disable the padding, else 545 | * the "drop shadow" (actually a bottom border) of the header bar 546 | * doesn't fill out the entire window and the edges look weird. 547 | * With CSD Gtk's CSS also disables the padding, so this should be 548 | * safe and relatively theme-agnostic. (There is additional padding 549 | * inside child widgets, this padding was only 1px or so anyway.) 550 | * Next, we make sure that the edges aren't rounded anymore, to 551 | * make sure that there are no small black pixels on the top left 552 | * and top right side of the window contents. This should also be 553 | * theme-agnostic. 554 | * IMPORTANT: The CSS selectors here have to have the same (or 555 | * higher) selectivity than the selectors used in the theme's CSS. 556 | * Otherwise the settings here will not take effect, even though 557 | * this CSS here has a higher priority. See 558 | * for details. 559 | */ 560 | static const char *custom_css = 561 | "label.title:only-child {\n" 562 | "opacity: 0;\n" 563 | "}\n" 564 | "window > .titlebar:not(headerbar) {\n" 565 | " padding: 0;\n" 566 | " border-style: none;\n" 567 | " border-color: transparent;\n" 568 | "}\n" 569 | ".background:not(.tiled):not(.maximized) .titlebar:backdrop,\n" 570 | ".background:not(.tiled):not(.maximized) .titlebar," 571 | ".background:not(.tiled):not(.maximized) .titlebar headerbar {\n" 572 | " border-top-left-radius: 0;\n" 573 | " border-top-right-radius: 0;\n" 574 | "}\n"; 575 | static int title_skipped = 0; 576 | 577 | const char* env_show_header = getenv("GTK3NOCSD_SHOW_HEADER"); 578 | if (env_show_header && !strncmp(env_show_header, "1", 2) && !title_skipped) { 579 | custom_css += 39; // skip CSS rule that hides title 580 | title_skipped = 1; 581 | } 582 | 583 | if (G_UNLIKELY (provider == NULL)) { 584 | GtkCssProvider *new_provider; 585 | 586 | pthread_mutex_lock(&provider_mutex); 587 | if (provider != NULL) { 588 | /* Handle race condition. */ 589 | pthread_mutex_unlock(&provider_mutex); 590 | return provider; 591 | } 592 | 593 | new_provider = gtk_css_provider_new (); 594 | gtk_css_provider_load_from_data (new_provider, custom_css, -1, NULL); 595 | 596 | provider = GTK_STYLE_PROVIDER (new_provider); 597 | pthread_mutex_unlock(&provider_mutex); 598 | } 599 | 600 | return provider; 601 | } 602 | 603 | static void add_custom_css (GtkWidget *widget) 604 | { 605 | GtkStyleProvider *my_provider = get_custom_css_provider (); 606 | if (!my_provider) 607 | return; 608 | 609 | /* Use a higher priority than SETTINGS, but lower than APPLICATION. 610 | * add_provider will make sure a given provider is not added twice. 611 | */ 612 | GdkScreen *screen = gdk_screen_get_default (); 613 | gtk_style_context_add_provider_for_screen (screen, my_provider, GTK_STYLE_PROVIDER_PRIORITY_SETTINGS + 50); 614 | } 615 | 616 | // This API exists since gtk+ 3.10 617 | extern void gtk_window_set_titlebar (GtkWindow *window, GtkWidget *titlebar) { 618 | if(!is_compatible_gtk_version() || !are_csd_disabled()) { 619 | orig_gtk_window_set_titlebar(window, titlebar); 620 | return; 621 | } 622 | if (titlebar && is_gtk_version_larger_or_equal (3, 16, 1)) { 623 | /* We have to reimplement gtk_window_set_titlebar ourselves, since 624 | * those Gtk versions don't support turning CSD off anymore. 625 | * This mainly does the same things as the original function 626 | * (consisting of adding the title bar widget + connecting signals), 627 | * but it will not enable CSD and not set the client_decorated flag 628 | * in the window private space. (We wouldn't know which bit it is 629 | * anyway.) */ 630 | gtk_window_private_info_t private_info = gtk_window_private_info (); 631 | char *priv = G_TYPE_INSTANCE_GET_PRIVATE (window, gtk_window_type, char); 632 | gboolean was_mapped = FALSE; 633 | GtkWidget *widget = GTK_WIDGET (window); 634 | GtkWidget **title_box_ptr = NULL; 635 | 636 | /* Something went wrong, so just stick with the original 637 | * implementation. */ 638 | if (private_info.title_box_offset == (gsize)-1 || private_info.title_box_offset == (gsize)-2 || !priv) 639 | goto orig_impl; 640 | 641 | title_box_ptr = (GtkWidget **) &priv[private_info.title_box_offset]; 642 | 643 | if (!*title_box_ptr) { 644 | was_mapped = gtk_widget_get_mapped (widget); 645 | if (gtk_widget_get_realized (widget)) { 646 | g_warning ("gtk_window_set_titlebar() called on a realized window"); 647 | gtk_widget_unrealize (widget); 648 | } 649 | } 650 | 651 | /* The solid-csd class is not removed when the titlebar 652 | * is unset in Gtk (it's probably a bug), so unset it 653 | * here explicitly, in case it's set. */ 654 | gtk_style_context_remove_class (gtk_widget_get_style_context (widget), "solid-csd"); 655 | 656 | /* We need to store the titlebar in priv->title_box, 657 | * which is where title_box_ptr points to. Then we 658 | * need to reparent the title bar and connect signals 659 | * if it's a GtkHeaderBar. Apart from CSD enablement, 660 | * this is what the original function boils down to. 661 | */ 662 | *title_box_ptr = titlebar; 663 | gtk_widget_set_parent (*title_box_ptr, widget); 664 | if (GTK_IS_HEADER_BAR (titlebar)) { 665 | g_signal_connect (titlebar, "notify::title", 666 | G_CALLBACK (private_info.on_titlebar_title_notify), window); 667 | private_info.on_titlebar_title_notify (GTK_HEADER_BAR (titlebar), NULL, window); 668 | 669 | /* Hiding GtkHeaderBar when custom title wasn't set (only for GtkDialog windows) */ 670 | const GtkWidget* custom = gtk_header_bar_get_custom_title(GTK_HEADER_BAR (titlebar)); 671 | const gchar* window_type = G_OBJECT_TYPE_NAME(window); 672 | if (custom == NULL && !strcmp(window_type, "GtkDialog")) { 673 | gtk_widget_hide(GTK_WIDGET (titlebar)); 674 | } 675 | } 676 | 677 | gtk_style_context_add_class (gtk_widget_get_style_context (titlebar), 678 | GTK_STYLE_CLASS_TITLEBAR); 679 | 680 | add_custom_css (titlebar); 681 | 682 | if (was_mapped) 683 | gtk_widget_map (widget); 684 | 685 | return; 686 | } 687 | 688 | orig_impl: 689 | ++(TLSD->disable_composite); 690 | orig_gtk_window_set_titlebar(window, titlebar); 691 | if(window && titlebar) 692 | set_has_custom_title(window, TRUE); 693 | --(TLSD->disable_composite); 694 | } 695 | 696 | static int _remove_buttons_from_layout (char *new_layout, const char *old_layout) 697 | { 698 | gchar **tokens; 699 | gchar **t; 700 | int i, j, k; 701 | 702 | /* Assumptions: new_layout fits 256 bytes (including NUL), so make sure 703 | * that the old layout will fit */ 704 | if (strlen (old_layout) > 255) 705 | return -1; 706 | 707 | new_layout[0] = '\0'; 708 | 709 | tokens = g_strsplit (old_layout, ":", 2); 710 | if (tokens) { 711 | for (i = 0; i < 2; i++) { 712 | if (tokens[i] == NULL) 713 | break; 714 | if (i) 715 | g_strlcat (new_layout, ":", 256); 716 | t = g_strsplit (tokens[i], ",", -1); 717 | for (j = 0, k = 0; t[j]; j++) { 718 | /* We want to remove all standard window icons, while retaining 719 | * custom stuff. */ 720 | if (!strcmp (t[j], "icon") || !strcmp (t[j], "minimize") || !strcmp (t[j], "maximize") || !strcmp (t[j], "close")) 721 | continue; 722 | if (k) 723 | g_strlcat (new_layout, ",", 256); 724 | g_strlcat (new_layout, t[j], 256); 725 | k++; 726 | } 727 | 728 | g_strfreev (t); 729 | } 730 | g_strfreev (tokens); 731 | } else { 732 | g_strlcpy (new_layout, ":", 256); 733 | } 734 | 735 | return 0; 736 | } 737 | 738 | static void _gtk_header_bar_update_window_buttons (GtkHeaderBar *bar) 739 | { 740 | gtk_header_bar_private_info_t info = gtk_header_bar_private_info (); 741 | char *priv = G_TYPE_INSTANCE_GET_PRIVATE (bar, gtk_header_bar_type, char); 742 | gchar **decoration_layout_ptr = NULL; 743 | gchar *orig_layout = NULL; 744 | gchar new_layout[256]; 745 | int r; 746 | 747 | if (info.decoration_layout_offset == (gsize) -1 || info.decoration_layout_offset == (gsize) -2 || !priv) { 748 | return; 749 | } 750 | 751 | /* We shouldn't hit this case, but check nevertheless. */ 752 | if (!is_compatible_gtk_version() || !are_csd_disabled()) { 753 | info.update_window_buttons (bar); 754 | return; 755 | } 756 | 757 | decoration_layout_ptr = (gchar **) &priv[info.decoration_layout_offset]; 758 | if (*decoration_layout_ptr) { 759 | orig_layout = *decoration_layout_ptr; 760 | r = _remove_buttons_from_layout (new_layout, *decoration_layout_ptr); 761 | if (r == 0) 762 | *decoration_layout_ptr = new_layout; 763 | } 764 | info.update_window_buttons (bar); 765 | if (*decoration_layout_ptr) { 766 | *decoration_layout_ptr = orig_layout; 767 | } 768 | } 769 | 770 | static gboolean _gtk_header_bar_window_state_changed (GtkWidget *widget, GdkEventWindowState *event, gpointer data) 771 | { 772 | gtk_header_bar_private_info_t info = gtk_header_bar_private_info (); 773 | GtkHeaderBar *bar = GTK_HEADER_BAR (data); 774 | gboolean ret; 775 | 776 | /* We can only be called if info.decoration_layout_offset is >= 0, 777 | * see hierarchy_changed, where this signal is connected, so this 778 | * shouldn't happen. If it does, though, just ignore the event, 779 | * it's certainly better than crashing with a segfault. */ 780 | if (info.decoration_layout_offset == (gsize) -1 || info.decoration_layout_offset == (gsize) -2 || !info.window_state_changed) { 781 | return FALSE; 782 | } 783 | 784 | /* Technically, we don't actually need to run the current version of 785 | * window_state_changed, as the current Gtk+3 code does nothing more 786 | * than we do here. Unfortunately, we need to be future-proof, so 787 | * make call it anyway, and later override it again with our own code. */ 788 | ret = info.window_state_changed (widget, event, data); 789 | if (!is_compatible_gtk_version() || !are_csd_disabled()) 790 | return ret; 791 | 792 | if (event->changed_mask & (GDK_WINDOW_STATE_FULLSCREEN | GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_TILED)) 793 | _gtk_header_bar_update_window_buttons (bar); 794 | 795 | return ret; 796 | } 797 | 798 | extern void g_object_get (gpointer _object, const gchar *first_property_name, ...) 799 | { 800 | GObject *object = _object; 801 | va_list var_args; 802 | const gchar *name; 803 | char new_layout[256]; 804 | int r; 805 | 806 | if (!G_IS_OBJECT (_object)) 807 | return; 808 | 809 | /* This is a really, really awful hack, because of the variable arguments 810 | * that g_object_get takes. At least Gtk+3 defines g_object_get_valist, 811 | * so we can default back to the valist original implementation if we 812 | * currently are not faking this for the decoration layout. Unfortunately, 813 | * there's no C way of forwarding to other varargs functions, so any other 814 | * preloaded library can't override the same function as we do here... 815 | * Fortunately, it's not very likely someone else wants to override 816 | * g_object_get(). */ 817 | 818 | va_start (var_args, first_property_name); 819 | if (are_csd_disabled()) { 820 | name = first_property_name; 821 | while (name) { 822 | GValue value = G_VALUE_INIT; 823 | GParamSpec *spec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name); 824 | gchar *error; 825 | 826 | if (!spec) 827 | break; 828 | 829 | g_value_init (&value, spec->value_type); 830 | g_object_get_property (object, name, &value); 831 | 832 | if (G_UNLIKELY (strcmp (name, "gtk-decoration-layout") == 0)) { 833 | gchar **v = va_arg (var_args, gchar **); 834 | const gchar *s = g_value_get_string (&value); 835 | 836 | r = _remove_buttons_from_layout (new_layout, s); 837 | if (r == 0) 838 | s = new_layout; 839 | *v = g_strdup (s); 840 | } else if (G_UNLIKELY (strcmp (name, "gtk-dialogs-use-header") == 0)) { 841 | gboolean *v = va_arg (var_args, gboolean *); 842 | // Save dialog will break if this prop has been set to TRUE (see #13) 843 | *v = FALSE; 844 | } else { 845 | G_VALUE_LCOPY (&value, var_args, 0, &error); 846 | if (error) { 847 | g_warning ("%s: %s", "g_object_get_valist", error); 848 | g_free (error); 849 | g_value_unset (&value); 850 | break; 851 | } 852 | } 853 | 854 | g_value_unset (&value); 855 | 856 | name = va_arg (var_args, gchar *); 857 | } 858 | } else { 859 | g_object_get_valist (object, first_property_name, var_args); 860 | } 861 | va_end (var_args); 862 | } 863 | 864 | extern void gtk_header_bar_set_show_close_button (GtkHeaderBar *bar, gboolean setting) 865 | { 866 | /* Ancient Gtk+3 versions: we fake it via disabling show_close_button, 867 | * but that has adverse consequences, so in newer versions, where the 868 | * API is more complete, call our own implemnetation of u_w_b after 869 | * the original routine to perform some fixups. */ 870 | if(is_compatible_gtk_version() && are_csd_disabled() && !is_gtk_version_larger_or_equal(3, 12, 0)) 871 | setting = FALSE; 872 | orig_gtk_header_bar_set_show_close_button (bar, setting); 873 | if (is_compatible_gtk_version () && are_csd_disabled () && is_gtk_version_larger_or_equal (3, 12, 0)) 874 | _gtk_header_bar_update_window_buttons (bar); 875 | } 876 | 877 | extern void gtk_header_bar_set_decoration_layout (GtkHeaderBar *bar, const gchar *layout) 878 | { 879 | /* We need to call the original routine here, because it modifies the 880 | * private data structures. We fixup afterwards. */ 881 | orig_gtk_header_bar_set_decoration_layout (bar, layout); 882 | if(is_compatible_gtk_version() && are_csd_disabled() && is_gtk_version_larger_or_equal(3, 12, 0)) { 883 | _gtk_header_bar_update_window_buttons (bar); 884 | } 885 | } 886 | 887 | extern gboolean gtk_window_get_decorated (GtkWindow *window) 888 | { 889 | return FALSE; 890 | } 891 | 892 | extern gboolean gdk_screen_is_composited (GdkScreen *screen) { 893 | /* With Gtk+3 3.16.1+ we reimplement gtk_window_set_titlebar ourselves, hence 894 | * we don't want to re-use the compositing hack, especially since it causes 895 | * problems in newer Gtk versions. */ 896 | if(is_compatible_gtk_version() && are_csd_disabled() && !is_gtk_version_larger_or_equal(3, 16, 1)) { 897 | if(TLSD->disable_composite) 898 | return FALSE; 899 | } 900 | return orig_gdk_screen_is_composited (screen); 901 | } 902 | 903 | extern void gdk_window_set_decorations (GdkWindow *window, GdkWMDecoration decorations) { 904 | if(is_compatible_gtk_version() && are_csd_disabled()) { 905 | if(decorations == GDK_DECOR_BORDER) { 906 | GtkWidget* widget = NULL; 907 | gdk_window_get_user_data(window, (void**)&widget); 908 | if(widget && GTK_IS_WINDOW(widget)) { // if this GdkWindow is associated with a GtkWindow 909 | // if this window has custom title (not using CSD), turn on all decorations 910 | if(has_custom_title(GTK_WINDOW(widget))) 911 | decorations = GDK_DECOR_ALL; 912 | } 913 | } 914 | } 915 | orig_gdk_window_set_decorations (window, decorations); 916 | } 917 | 918 | typedef void (*gtk_window_realize_t)(GtkWidget* widget); 919 | static gtk_window_realize_t orig_gtk_window_realize = NULL; 920 | 921 | static void fake_gtk_window_realize(GtkWidget* widget) { 922 | ++(TLSD->disable_composite); 923 | orig_gtk_window_realize(widget); 924 | --(TLSD->disable_composite); 925 | } 926 | 927 | static gtk_dialog_constructor_t orig_gtk_dialog_constructor = NULL; 928 | static GClassInitFunc orig_gtk_dialog_class_init = NULL; 929 | static GType gtk_dialog_type = 0; 930 | 931 | static GObject *fake_gtk_dialog_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { 932 | ++(TLSD->disable_composite); 933 | GObject* obj = orig_gtk_dialog_constructor(type, n_construct_properties, construct_params); 934 | --(TLSD->disable_composite); 935 | return obj; 936 | } 937 | 938 | static void fake_gtk_dialog_class_init (GtkDialogClass *klass, gpointer data) { 939 | orig_gtk_dialog_class_init(klass, data); 940 | // GDialogClass* dialog_class = GTK_DIALOG_CLASS(klass); 941 | GObjectClass* object_class = G_OBJECT_CLASS(klass); 942 | if(object_class) { 943 | orig_gtk_dialog_constructor = object_class->constructor; 944 | object_class->constructor = fake_gtk_dialog_constructor; 945 | } 946 | } 947 | 948 | 949 | static GClassInitFunc orig_gtk_window_class_init = NULL; 950 | 951 | static void fake_gtk_window_class_init (GtkWindowClass *klass, gpointer data) { 952 | orig_gtk_window_class_init(klass, data); 953 | GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); 954 | if(widget_class) { 955 | orig_gtk_window_realize = widget_class->realize; 956 | widget_class->realize = fake_gtk_window_realize; 957 | } 958 | } 959 | 960 | static gtk_header_bar_set_property_t orig_gtk_header_bar_set_property = NULL; 961 | static volatile int PROP_SHOW_CLOSE_BUTTON = -1; 962 | static void fake_gtk_header_bar_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) 963 | { 964 | /* We haven't determined the property yet... */ 965 | if (G_UNLIKELY(PROP_SHOW_CLOSE_BUTTON == -1)) { 966 | GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), "show-close-button"); 967 | if (spec) 968 | /* Technically, this is marked as internal in GParamSpec, 969 | * but it's the only way to access that trivially. It's 970 | * been stable in gobject for over a decade now. */ 971 | PROP_SHOW_CLOSE_BUTTON = spec->param_id; 972 | else 973 | /* We couldn't find out, for some reason. The value -2 974 | * will never match a valid property id, so should be safe. */ 975 | PROP_SHOW_CLOSE_BUTTON = -2; 976 | } 977 | 978 | /* In theory, we shouldn't need to override this, since 979 | * set_property in the gtk3 source code just calls that function, 980 | * but with active compiler optimization, an inline version of it 981 | * may be copied into set_propery, so we also need to override 982 | * this here. */ 983 | if(G_UNLIKELY((int)prop_id == PROP_SHOW_CLOSE_BUTTON)) 984 | gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (object), g_value_get_boolean (value)); 985 | else 986 | orig_gtk_header_bar_set_property (object, prop_id, value, pspec); 987 | } 988 | 989 | static gtk_header_bar_realize_t orig_gtk_header_bar_realize = NULL; 990 | static void fake_gtk_header_bar_realize (GtkWidget *widget) 991 | { 992 | gtk_header_bar_private_info_t info; 993 | GtkSettings *settings; 994 | 995 | orig_gtk_header_bar_realize (widget); 996 | settings = gtk_widget_get_settings (widget); 997 | 998 | /* realize() is called from gtk_header_bar_private_info, so make sure 999 | * we special-case that. */ 1000 | if (G_UNLIKELY (TLSD->in_info_collect)) 1001 | return; 1002 | 1003 | info = gtk_header_bar_private_info (); 1004 | if (info.decoration_layout_offset == (gsize) -1 || info.decoration_layout_offset == (gsize) -2) 1005 | return; 1006 | 1007 | /* Replace signal handlers with our own */ 1008 | g_signal_handlers_disconnect_by_func (settings, info.update_window_buttons, widget); 1009 | g_signal_connect_swapped (settings, "notify::gtk-shell-shows-app-menu", G_CALLBACK (_gtk_header_bar_update_window_buttons), widget); 1010 | g_signal_connect_swapped (settings, "notify::gtk-decoration-layout", G_CALLBACK (_gtk_header_bar_update_window_buttons), widget); 1011 | _gtk_header_bar_update_window_buttons (GTK_HEADER_BAR (widget)); 1012 | } 1013 | 1014 | static gtk_header_bar_unrealize_t orig_gtk_header_bar_unrealize = NULL; 1015 | static void fake_gtk_header_bar_unrealize (GtkWidget *widget) 1016 | { 1017 | /* Disconnect our own signal handlers */ 1018 | GtkSettings *settings = gtk_widget_get_settings (widget); 1019 | g_signal_handlers_disconnect_by_func (settings, _gtk_header_bar_update_window_buttons, widget); 1020 | orig_gtk_header_bar_unrealize (widget); 1021 | } 1022 | 1023 | static gtk_header_bar_hierarchy_changed_t orig_gtk_header_bar_hierarchy_changed = NULL; 1024 | static void fake_gtk_header_bar_hierarchy_changed (GtkWidget *widget, GtkWidget *previous_toplevel) 1025 | { 1026 | gtk_header_bar_private_info_t info; 1027 | GtkWidget *toplevel; 1028 | GtkHeaderBar *bar = GTK_HEADER_BAR (widget); 1029 | 1030 | /* Older Gtk+3 versions didn't set this, so just ignore the event. */ 1031 | if (!orig_gtk_header_bar_hierarchy_changed) 1032 | return; 1033 | 1034 | orig_gtk_header_bar_hierarchy_changed (widget, previous_toplevel); 1035 | 1036 | if (G_UNLIKELY (TLSD->in_info_collect)) 1037 | return; 1038 | 1039 | toplevel = gtk_widget_get_toplevel (widget); 1040 | 1041 | /* We can always do this. */ 1042 | if (previous_toplevel) 1043 | g_signal_handlers_disconnect_by_func (previous_toplevel, _gtk_header_bar_window_state_changed, widget); 1044 | 1045 | info = gtk_header_bar_private_info (); 1046 | 1047 | if (info.decoration_layout_offset == (gsize) -1 && info.decoration_layout_offset == (gsize) -2) 1048 | return; 1049 | 1050 | if (toplevel) { 1051 | g_signal_handlers_disconnect_by_func (toplevel, info.window_state_changed, widget); 1052 | g_signal_connect_after (toplevel, "window-state-event", G_CALLBACK (_gtk_header_bar_window_state_changed), widget); 1053 | } 1054 | 1055 | _gtk_header_bar_update_window_buttons (bar); 1056 | } 1057 | 1058 | static GClassInitFunc orig_gtk_header_bar_class_init = NULL; 1059 | 1060 | static void fake_gtk_header_bar_class_init (GtkWindowClass *klass, gpointer data) { 1061 | orig_gtk_header_bar_class_init(klass, data); 1062 | GObjectClass* object_class = G_OBJECT_CLASS(klass); 1063 | GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass); 1064 | if(object_class) { 1065 | orig_gtk_header_bar_set_property = object_class->set_property; 1066 | object_class->set_property = fake_gtk_header_bar_set_property; 1067 | } 1068 | if (widget_class) { 1069 | orig_gtk_header_bar_realize = widget_class->realize; 1070 | orig_gtk_header_bar_unrealize = widget_class->unrealize; 1071 | orig_gtk_header_bar_hierarchy_changed = widget_class->hierarchy_changed; 1072 | widget_class->realize = fake_gtk_header_bar_realize; 1073 | widget_class->unrealize = fake_gtk_header_bar_unrealize; 1074 | widget_class->hierarchy_changed = fake_gtk_header_bar_hierarchy_changed; 1075 | } 1076 | } 1077 | 1078 | static GInstanceInitFunc orig_gtk_shortcuts_window_init = NULL; 1079 | 1080 | static void fake_gtk_shortcuts_window_init (GtkWindow *window, gpointer klass) { 1081 | GtkHeaderBar *title_bar; 1082 | 1083 | orig_gtk_shortcuts_window_init ((GTypeInstance *) window, klass); 1084 | /* call our own set_titlebar to make sure we disable CSDs 1085 | * (the original Gtk function calls Gtk's internal set_titlebar) */ 1086 | title_bar = GTK_HEADER_BAR (gtk_window_get_titlebar (window)); 1087 | if (title_bar) { 1088 | /* We need to take a reference out on the title_bar, because 1089 | * unsetting it will unref() it indirectly via gtk_widget_unparent(), 1090 | * which would otherwise call the destructor, because it's only 1091 | * referenced once. */ 1092 | g_object_ref (title_bar); 1093 | orig_gtk_window_set_titlebar (window, NULL); 1094 | gtk_window_set_titlebar (window, GTK_WIDGET (title_bar)); 1095 | /* Drop our own reference (because it's not floating, and set_titlebar 1096 | * calls ref_sink indirectly via set_parent) */ 1097 | g_object_unref (title_bar); 1098 | } 1099 | } 1100 | 1101 | GType g_type_register_static_simple (GType parent_type, const gchar *type_name, guint class_size, GClassInitFunc class_init, guint instance_size, GInstanceInitFunc instance_init, GTypeFlags flags) { 1102 | GType type; 1103 | GType *save_type = NULL; 1104 | 1105 | if(!orig_gtk_window_class_init) { // GtkWindow is not overriden 1106 | if(type_name && G_UNLIKELY(strcmp(type_name, "GtkWindow") == 0)) { 1107 | // override GtkWindowClass 1108 | orig_gtk_window_class_init = class_init; 1109 | detect_gtk2((void *) class_init); 1110 | if(is_compatible_gtk_version() && are_csd_disabled()) { 1111 | class_init = (GClassInitFunc)fake_gtk_window_class_init; 1112 | save_type = >k_window_type; 1113 | goto out; 1114 | } 1115 | } 1116 | } 1117 | 1118 | if(!orig_gtk_dialog_class_init) { // GtkDialog::constructor is not overriden 1119 | if(type_name && G_UNLIKELY(strcmp(type_name, "GtkDialog") == 0)) { 1120 | // override GtkDialogClass 1121 | orig_gtk_dialog_class_init = class_init; 1122 | detect_gtk2((void *) class_init); 1123 | if(is_compatible_gtk_version() && are_csd_disabled()) { 1124 | class_init = (GClassInitFunc)fake_gtk_dialog_class_init; 1125 | save_type = >k_dialog_type; 1126 | goto out; 1127 | } 1128 | } 1129 | } 1130 | 1131 | if(!orig_gtk_header_bar_class_init) { // GtkHeaderBar::constructor is not overriden 1132 | if(type_name && G_UNLIKELY(strcmp(type_name, "GtkHeaderBar") == 0)) { 1133 | // override GtkHeaderBarClass 1134 | orig_gtk_header_bar_class_init = class_init; 1135 | detect_gtk2((void *) class_init); 1136 | if(is_compatible_gtk_version() && are_csd_disabled()) { 1137 | class_init = (GClassInitFunc)fake_gtk_header_bar_class_init; 1138 | save_type = >k_header_bar_type; 1139 | goto out; 1140 | } 1141 | } 1142 | } 1143 | 1144 | if(!orig_gtk_shortcuts_window_init) { // GtkShortcutsWindow::constructor is not overriden 1145 | if(type_name && G_UNLIKELY(strcmp(type_name, "GtkShortcutsWindow") == 0)) { 1146 | // override GtkShortcutsWindowClass 1147 | orig_gtk_shortcuts_window_init = instance_init; 1148 | detect_gtk2((void *) instance_init); 1149 | if(is_compatible_gtk_version() && are_csd_disabled()) { 1150 | instance_init = (GInstanceInitFunc) fake_gtk_shortcuts_window_init; 1151 | goto out; 1152 | } 1153 | } 1154 | } 1155 | 1156 | out: 1157 | type = orig_g_type_register_static_simple (parent_type, type_name, class_size, class_init, instance_size, instance_init, flags); 1158 | if (save_type) 1159 | *save_type = type; 1160 | return type; 1161 | } 1162 | 1163 | static gtk_window_buildable_add_child_t orig_gtk_window_buildable_add_child = NULL; 1164 | static gtk_window_buildable_add_child_t orig_gtk_dialog_buildable_add_child = NULL; 1165 | 1166 | static void fake_gtk_window_buildable_add_child (GtkBuildable *buildable, GtkBuilder *builder, GObject *child, const gchar *type) { 1167 | if (type && strcmp (type, "titlebar") == 0) { 1168 | gtk_window_set_titlebar (GTK_WINDOW (buildable), GTK_WIDGET (child)); 1169 | return; 1170 | } 1171 | orig_gtk_window_buildable_add_child(buildable, builder, child, type); 1172 | } 1173 | 1174 | static void fake_gtk_dialog_buildable_add_child (GtkBuildable *buildable, GtkBuilder *builder, GObject *child, const gchar *type) { 1175 | if (type && strcmp (type, "titlebar") == 0) { 1176 | gtk_window_set_titlebar (GTK_WINDOW (buildable), GTK_WIDGET (child)); 1177 | return; 1178 | } 1179 | orig_gtk_dialog_buildable_add_child(buildable, builder, child, type); 1180 | } 1181 | 1182 | static GInterfaceInitFunc orig_gtk_window_buildable_interface_init = NULL; 1183 | static GInterfaceInitFunc orig_gtk_dialog_buildable_interface_init = NULL; 1184 | 1185 | static void fake_gtk_window_buildable_interface_init (GtkBuildableIface *iface, gpointer data) 1186 | { 1187 | orig_gtk_window_buildable_interface_init(iface, data); 1188 | orig_gtk_window_buildable_add_child = iface->add_child; 1189 | iface->add_child = fake_gtk_window_buildable_add_child; 1190 | // printf("intercept gtk_window_buildable_interface_init!!\n"); 1191 | // iface->set_buildable_property = gtk_window_buildable_set_buildable_property; 1192 | } 1193 | 1194 | static void fake_gtk_dialog_buildable_interface_init (GtkBuildableIface *iface, gpointer data) 1195 | { 1196 | orig_gtk_dialog_buildable_interface_init(iface, data); 1197 | orig_gtk_dialog_buildable_add_child = iface->add_child; 1198 | iface->add_child = fake_gtk_dialog_buildable_add_child; 1199 | } 1200 | 1201 | void g_type_add_interface_static (GType instance_type, GType interface_type, const GInterfaceInfo *info) { 1202 | if (info && info->interface_init) 1203 | detect_gtk2((void *) info->interface_init); 1204 | 1205 | if(is_compatible_gtk_version() && are_csd_disabled() && (instance_type == gtk_window_type || instance_type == gtk_dialog_type)) { 1206 | if(interface_type == GTK_TYPE_BUILDABLE) { 1207 | // register GtkBuildable interface for GtkWindow/GtkDialog class 1208 | GInterfaceInfo fake_info = *info; 1209 | if (instance_type == gtk_window_type) { 1210 | orig_gtk_window_buildable_interface_init = info->interface_init; 1211 | fake_info.interface_init = (GInterfaceInitFunc)fake_gtk_window_buildable_interface_init; 1212 | } else { 1213 | orig_gtk_dialog_buildable_interface_init = info->interface_init; 1214 | fake_info.interface_init = (GInterfaceInitFunc)fake_gtk_dialog_buildable_interface_init; 1215 | } 1216 | orig_g_type_add_interface_static (instance_type, interface_type, &fake_info); 1217 | return; 1218 | } 1219 | } 1220 | orig_g_type_add_interface_static (instance_type, interface_type, info); 1221 | } 1222 | 1223 | static gsize gtk_window_private_size = 0; 1224 | static gint gtk_window_private_offset = 0; 1225 | static gsize gtk_header_bar_private_size = 0; 1226 | static gint gtk_header_bar_private_offset = 0; 1227 | gint g_type_add_instance_private (GType class_type, gsize private_size) 1228 | { 1229 | if (G_UNLIKELY (class_type == gtk_window_type && gtk_window_private_size == 0)) { 1230 | gtk_window_private_size = private_size; 1231 | gtk_window_private_offset = orig_g_type_add_instance_private (class_type, private_size); 1232 | return gtk_window_private_offset; 1233 | } else if (G_UNLIKELY (class_type == gtk_header_bar_type && gtk_header_bar_private_size == 0)) { 1234 | gtk_header_bar_private_size = private_size; 1235 | gtk_header_bar_private_offset = orig_g_type_add_instance_private (class_type, private_size); 1236 | return gtk_window_private_offset; 1237 | } 1238 | return orig_g_type_add_instance_private (class_type, private_size); 1239 | } 1240 | 1241 | gulong g_signal_connect_data (gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) 1242 | { 1243 | if (G_UNLIKELY (TLSD->signal_capture_handler)) { 1244 | const char *name = TLSD->signal_capture_name; 1245 | if (instance != NULL && TLSD->signal_capture_instance == instance && strcmp (detailed_signal, name) == 0) 1246 | TLSD->signal_capture_callback = c_handler; 1247 | else if (data != NULL && TLSD->signal_capture_data == data && strcmp (detailed_signal, name) == 0) 1248 | TLSD->signal_capture_callback = c_handler; 1249 | } 1250 | return orig_g_signal_connect_data (instance, detailed_signal, c_handler, data, destroy_data, connect_flags); 1251 | } 1252 | 1253 | static int find_unique_pointer_in_region (const char *haystack, gsize haystack_size, const void *needle) 1254 | { 1255 | gsize i; 1256 | int offset = -1; 1257 | /* This is definitely not the most efficient algorithm, but 1258 | * since this is only called twice per program, we're not 1259 | * going to optimize it. */ 1260 | for (i = 0; i <= haystack_size - sizeof (needle); i++) { 1261 | if (memcmp (&haystack[i], (void *) &needle, sizeof (needle)) == 0) { 1262 | /* The pointer should only occur once in this memory 1263 | * region - if not, then something's gone wrong. */ 1264 | if (offset != -1) 1265 | return -2; 1266 | offset = (int) i; 1267 | } 1268 | } 1269 | return offset; 1270 | } 1271 | 1272 | static gtk_window_private_info_t gtk_window_private_info () 1273 | { 1274 | static volatile gtk_window_private_info_t info = { (gsize) -1, NULL }; 1275 | if (G_UNLIKELY (info.title_box_offset == (gsize) -1)) { 1276 | if (gtk_window_private_size != 0) { 1277 | /* We have to detect the offset of where the title_box pointer 1278 | * is stored in a GtkWindowPrivate object. This is required 1279 | * because we need to change the pointer inside 1280 | * GtkWindowPrivate causing any side effects (especially 1281 | * without enabling CSD) - and the set_titlebar function will 1282 | * enable CSD (in >= 3.16.1) if any non-NULL titlebar is set. 1283 | * Therefore, we need to find the pointer offset within the 1284 | * private data at runtime (it's not guaranteed to be stable 1285 | * ABI-wise, so we can't just hard-code things, that would 1286 | * lead to crashes). Furthermore, we need to know the address 1287 | * of the static callback function that's used to process the 1288 | * notify::title signal emitted by header bars. 1289 | * 1290 | * The basic algorithm is as follows: create a dummy GtkWindow, 1291 | * create a dummy GtkHeaderBar, call the original 1292 | * gtk_window_set_titlebar function and then look for the pointer 1293 | * in the private data region of the GtkWindow. The size of the 1294 | * region was recorded by us when the type was registered, so we 1295 | * will stay within the proper memory region. 1296 | */ 1297 | GtkWindow *dummy_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL)); 1298 | void *window_priv = NULL; 1299 | GtkHeaderBar *dummy_bar = GTK_HEADER_BAR (gtk_header_bar_new ()); 1300 | int offset = -1; 1301 | 1302 | /* We're collecting information, so make sure all hacks 1303 | * are NOOPS. */ 1304 | TLSD->in_info_collect = 1; 1305 | 1306 | if (!dummy_window || !dummy_bar) { 1307 | g_warning ("libgtk3-nocsd: couldn't create dummy objects (GtkWindow, GtkHeaderBar) to determine this Gtk's runtime data structure layout"); 1308 | goto out; 1309 | } 1310 | 1311 | /* First let's try to make sure the pointer is not present in 1312 | * the memory region. */ 1313 | window_priv = G_TYPE_INSTANCE_GET_PRIVATE (dummy_window, gtk_window_type, void); 1314 | offset = find_unique_pointer_in_region (window_priv, gtk_window_private_size, dummy_bar); 1315 | /* Make sure the pointer isn't in there, else */ 1316 | if (offset != -1) { 1317 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's runtime data structure layout: GtkWindow private structure already contained a pointer to GtkHeaderBar before setting title bar"); 1318 | goto out; 1319 | } 1320 | 1321 | /* Set the title bar via the original title bar function. */ 1322 | TLSD->signal_capture_callback = NULL; 1323 | TLSD->signal_capture_handler = 1; 1324 | TLSD->signal_capture_name = "notify::title"; 1325 | TLSD->signal_capture_instance = dummy_bar; 1326 | TLSD->signal_capture_data = NULL; 1327 | orig_gtk_window_set_titlebar (dummy_window, GTK_WIDGET (dummy_bar)); 1328 | TLSD->signal_capture_handler = 0; 1329 | TLSD->signal_capture_instance = NULL; 1330 | TLSD->signal_capture_name = NULL; 1331 | 1332 | /* Now find the pointer in memory. */ 1333 | offset = find_unique_pointer_in_region (window_priv, gtk_window_private_size, dummy_bar); 1334 | if (offset < 0) { 1335 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's runtime data structure layout: GtkWindow private structure doesn't contain a pointer to GtkHeaderBar after setting title bar (error type %d)", -offset); 1336 | goto out; 1337 | } 1338 | 1339 | if (TLSD->signal_capture_callback == NULL) { 1340 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's callback routine for GtkHeaderBar/GtkWindow interaction"); 1341 | goto out; 1342 | } 1343 | 1344 | info.on_titlebar_title_notify = (on_titlebar_title_notify_t) TLSD->signal_capture_callback; 1345 | info.title_box_offset = offset; 1346 | out: 1347 | if (dummy_window) gtk_widget_destroy (GTK_WIDGET (dummy_window)); 1348 | else if (dummy_bar) gtk_widget_destroy (GTK_WIDGET (dummy_bar)); 1349 | 1350 | TLSD->in_info_collect = 0; 1351 | } 1352 | } 1353 | return info; 1354 | } 1355 | 1356 | static gtk_header_bar_private_info_t gtk_header_bar_private_info () 1357 | { 1358 | static volatile gtk_header_bar_private_info_t info = { (gsize) -1, NULL, NULL }; 1359 | if (G_UNLIKELY (info.decoration_layout_offset == (gsize) -1)) { 1360 | /* Was only introduced in Gtk+3 >= 3.12. Unlikely that someone is 1361 | * still using such an old version, but be safe nevertheless. */ 1362 | if (G_UNLIKELY (!is_gtk_version_larger_or_equal(3, 12, 0))) { 1363 | return info; 1364 | } 1365 | if (gtk_header_bar_private_size != 0) { 1366 | /* We want to detect the offset of the pointer for the 1367 | * decoration_layout string in the private structure, so we 1368 | * create a header bar and set a decoration layout. As the 1369 | * setter does g_strdup, we have to call the getter again, 1370 | * because that will return the actual pointer that's stored. 1371 | */ 1372 | GtkWindow *dummy_window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL)); 1373 | GtkHeaderBar *dummy_bar = GTK_HEADER_BAR (gtk_header_bar_new ()); 1374 | void *header_bar_priv = NULL; 1375 | const gchar *ptr = NULL; 1376 | const gchar **ptr_in_priv; 1377 | int offset = -1; 1378 | gpointer ws_cb = NULL; 1379 | 1380 | /* We're collecting information, so make sure all hacks 1381 | * are NOOPS. */ 1382 | TLSD->in_info_collect = 1; 1383 | 1384 | if (!dummy_bar || !dummy_window) { 1385 | g_warning ("libgtk3-nocsd: couldn't create dummy object (GtkHeaderBar) to determine this Gtk's runtime data structure layout"); 1386 | goto out; 1387 | } 1388 | 1389 | /* add it to our dummy window (we need a window here because 1390 | * we want to realize the header bar, and that we can only 1391 | * do if it's attached to a top-level window) */ 1392 | TLSD->signal_capture_callback = NULL; 1393 | TLSD->signal_capture_handler = 1; 1394 | TLSD->signal_capture_name = "window-state-event"; 1395 | TLSD->signal_capture_instance = dummy_window; 1396 | TLSD->signal_capture_data = NULL; 1397 | orig_gtk_window_set_titlebar (dummy_window, GTK_WIDGET (dummy_bar)); 1398 | TLSD->signal_capture_handler = 0; 1399 | TLSD->signal_capture_instance = NULL; 1400 | TLSD->signal_capture_name = NULL; 1401 | ws_cb = TLSD->signal_capture_callback; 1402 | 1403 | header_bar_priv = G_TYPE_INSTANCE_GET_PRIVATE (dummy_bar, gtk_header_bar_type, void); 1404 | 1405 | orig_gtk_header_bar_set_decoration_layout (dummy_bar, "menu:close"); 1406 | ptr = orig_gtk_header_bar_get_decoration_layout (dummy_bar); 1407 | offset = find_unique_pointer_in_region (header_bar_priv, gtk_header_bar_private_size, ptr); 1408 | if (offset < 0) { 1409 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's runtime data structure layout: GtkHeaderBar private structure doesn't contain a pointer to decoration_layout after setting it (error type %d)", -offset); 1410 | goto out; 1411 | } 1412 | 1413 | /* We now verify that the pointer is NULL */ 1414 | orig_gtk_header_bar_set_decoration_layout (dummy_bar, NULL); 1415 | ptr_in_priv = (const gchar **) &((char *)header_bar_priv)[offset]; 1416 | if (*ptr_in_priv != NULL) { 1417 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's runtime data structure layout: GtkHeaderBar's priv->decoration_layout pointer position sanity check failed (got pointer %p instead of NULL)", *ptr_in_priv); 1418 | goto out; 1419 | } 1420 | 1421 | /* realize the widget to capture the 1422 | * _gtk_header_bar_update_window_buttons callback */ 1423 | TLSD->signal_capture_callback = NULL; 1424 | TLSD->signal_capture_handler = 1; 1425 | TLSD->signal_capture_name = "notify::gtk-decoration-layout"; 1426 | TLSD->signal_capture_instance = NULL; 1427 | TLSD->signal_capture_data = dummy_bar; 1428 | gtk_widget_realize (GTK_WIDGET (dummy_bar)); 1429 | TLSD->signal_capture_handler = 0; 1430 | TLSD->signal_capture_data = NULL; 1431 | TLSD->signal_capture_name = NULL; 1432 | 1433 | if (TLSD->signal_capture_callback == NULL) { 1434 | g_warning ("libgtk3-nocsd: error trying to determine this Gtk's callback routine for GtkHeaderBar's button update"); 1435 | goto out; 1436 | } 1437 | 1438 | info.decoration_layout_offset = offset; 1439 | info.update_window_buttons = (update_window_buttons_t) TLSD->signal_capture_callback; 1440 | /* Don't check ws_cb, it may be NULL, because older Gtk+3 versions didn't use that. */ 1441 | info.window_state_changed = (window_state_changed_t) ws_cb; 1442 | out: 1443 | if (dummy_window) gtk_widget_destroy (GTK_WIDGET (dummy_window)); 1444 | else if (dummy_bar) gtk_widget_destroy (GTK_WIDGET (dummy_bar)); 1445 | 1446 | TLSD->in_info_collect = 0; 1447 | } 1448 | } 1449 | return info; 1450 | } 1451 | 1452 | gboolean g_function_info_prep_invoker (GIFunctionInfo *info, GIFunctionInvoker *invoker, GError **error) 1453 | { 1454 | static gpointer orig_set_titlebar = NULL, orig_set_show_close_button = NULL, orig_set_decoration_layout = NULL; 1455 | gboolean result; 1456 | 1457 | if (!orig_set_titlebar) 1458 | orig_set_titlebar = (gpointer) find_orig_function (0, GTK_LIBRARY, "gtk_window_set_titlebar"); 1459 | if (!orig_set_show_close_button) 1460 | orig_set_show_close_button = (gpointer) find_orig_function (0, GTK_LIBRARY, "gtk_header_bar_set_show_close_button"); 1461 | if (!orig_set_decoration_layout) 1462 | orig_set_decoration_layout = (gpointer) find_orig_function (0, GTK_LIBRARY, "gtk_header_bar_set_decoration_layout"); 1463 | 1464 | result = orig_g_function_info_prep_invoker (info, invoker, error); 1465 | if (result) { 1466 | if (G_UNLIKELY (invoker->native_address == orig_set_titlebar)) 1467 | invoker->native_address = gtk_window_set_titlebar; 1468 | if (G_UNLIKELY (invoker->native_address == orig_set_show_close_button)) 1469 | invoker->native_address = gtk_header_bar_set_show_close_button; 1470 | if (G_UNLIKELY (invoker->native_address == orig_set_decoration_layout)) 1471 | invoker->native_address = gtk_header_bar_set_decoration_layout; 1472 | } 1473 | 1474 | return result; 1475 | } 1476 | 1477 | static void create_key_tls() 1478 | { 1479 | int r; 1480 | r = pthread_key_create (&key_tls, free); 1481 | if (r < 0) 1482 | g_error ("libgtk3-nocsd: unable to initialize TLS data: %s", strerror(errno)); 1483 | } 1484 | 1485 | static gtk3_nocsd_tls_data_t *tls_data_location() 1486 | { 1487 | void *ptr; 1488 | int r; 1489 | 1490 | (void) pthread_once (&key_tls_once, create_key_tls); 1491 | if ((ptr = pthread_getspecific (key_tls)) == NULL) { 1492 | ptr = calloc (1, sizeof (gtk3_nocsd_tls_data_t)); 1493 | if (!ptr) 1494 | g_error ("libgtk3-nocsd: unable to initialize TLS data: %s", strerror(errno)); 1495 | r = pthread_setspecific (key_tls, ptr); 1496 | if (r < 0) 1497 | g_error ("libgtk3-nocsd: unable to initialize TLS data: %s", strerror(errno)); 1498 | } 1499 | 1500 | return (gtk3_nocsd_tls_data_t *) ptr; 1501 | } 1502 | -------------------------------------------------------------------------------- /gtk3-nocsd.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # gtk3-nocsd - Wrapper to apply gtk3-nocsd to application 4 | # 5 | # Usage: ln -s /path/to/gtk3-nocsd ~/bin/evince 6 | # Or: gtk3-nocsd evince 7 | # 8 | # Create a symlink to this wrapper script with the name of the program 9 | # you want run without client-side decorations. This wrapper script 10 | # will run the first matching executable in the PATH that is *not* a 11 | # symlink to gtk3-nocsd. This wrapper script is useful if you don't 12 | # want to add gtk3-nocsd to your system-wide LD_PRELOAD or if you only 13 | # want it applied to certain applications. 14 | # 15 | # Alternative mode of operation: call this wrapper directly with the 16 | # program to call as its arguments. 17 | # 18 | 19 | OS=$(uname | tr "[:upper:]" "[:lower:]") 20 | RLINK_ARGS=$([ "$OS" = "freebsd" ] && echo "-f" || echo "-fe") 21 | 22 | GTK3_NOCSD_NAME=libgtk3-nocsd.so.0 23 | 24 | # Determine if this script are installed in the system path 25 | GTK3_NOCSD_BINARY="$(readlink ${RLINK_ARGS} "$(which "$0")")" 26 | case "${GTK3_NOCSD_BINARY%/*}" in 27 | /sbin|/bin|/usr/bin|/usr/sbin|/usr/local/bin|/usr/local/sbin) IN_SYSTEM_PATH=1 ;; 28 | *) IN_SYSTEM_PATH=0 ;; 29 | esac 30 | 31 | # Determine if the library is in the system path. If that's the case, 32 | # just use the plain library name, because that way loading the library 33 | # on all architectures it's installed is supported (because the linker 34 | # will automatically pick out the right one). 35 | if [ $IN_SYSTEM_PATH -eq 1 ] ; then 36 | if LC_ALL=C LD_PRELOAD="${GTK3_NOCSD_NAME}" /bin/true 2>&1 | grep LD_PRELOAD | grep -qF "${GTK3_NOCSD_NAME}" ; then 37 | IN_SYSTEM_PATH=0 38 | else 39 | GTK3_NOCSD="${GTK3_NOCSD_NAME}" 40 | fi 41 | fi 42 | 43 | if [ $IN_SYSTEM_PATH -eq 0 ] ; then 44 | # Figure out where the library is installed. First try the path 45 | # that was used when building gtk3-nocsd, then try the directory 46 | # the binary is installed. 47 | INSTALLED_PATH="@@libdir@@" 48 | BINARY_PATH="${GTK3_NOCSD_BINARY%/*}" 49 | GTK3_NOCSD="" 50 | for _path in "$INSTALLED_PATH" "$BINARY_PATH" "${BINARY_PATH%/bin}/lib" ; do 51 | if [ -e "${_path}/${GTK3_NOCSD_NAME}" ] ; then 52 | GTK3_NOCSD="${_path}/${GTK3_NOCSD_NAME}" 53 | break 54 | fi 55 | done 56 | if [ -z "$GTK3_NOCSD" ] ; then 57 | # this will _probably_ not work (unless the library is 58 | # installed in a system path, but the binary wasn't), but at 59 | # least the user will have a useful error message 60 | GTK3_NOCSD="${GTK3_NOCSD_NAME}" 61 | fi 62 | fi 63 | 64 | # This program was called directly, instead of via a symlink. 65 | if [ x"$(basename "$0")"x = x"gtk3-nocsd"x ] ; then 66 | if [ x"$1"x = x"-h"x ] || [ x"$1"x = x"--help"x ] ; then 67 | echo "Usage: $0 program [args]" 68 | exit 0 69 | fi 70 | 71 | export GTK_CSD=0 72 | export LD_PRELOAD="${GTK3_NOCSD}${LD_PRELOAD:+:$LD_PRELOAD}" 73 | exec "$@" 74 | fi 75 | 76 | # Find the real program (the first one that's not symlinked to get3-nocsd) 77 | APPNAME="$(basename "$0")" 78 | for APPPATH in $(which -a "$APPNAME") /bin/false; do 79 | APPPATH_LINK="$(readlink ${RLINK_ARGS} "$APPPATH")" 80 | [ x"${APPPATH_LINK##*/}"x = x"gtk3-nocsd"x ] || break 81 | done 82 | 83 | # Provide error message at all 84 | if [ x"$APPNAME"x != x"false"x ] && [ x"${APPPATH}"x = x"/bin/false"x ] ; then 85 | APPPATH=/.gtk3-nocsd./"$APPNAME" 86 | fi 87 | 88 | # Run the program with CSD disabled 89 | export LD_PRELOAD="${GTK3_NOCSD}${LD_PRELOAD:+:$LD_PRELOAD}" 90 | export GTK_CSD=0 91 | exec "$APPPATH" "$@" 92 | -------------------------------------------------------------------------------- /test-dummylib.c: -------------------------------------------------------------------------------- 1 | /* Dummy test library. 2 | * 3 | * This library will be dlopen()d by test-static-tls in multiple different 4 | * versions to see if libgtk3-nocsd.so uses up a static TLS entry, which 5 | * it shouldn't. 6 | */ 7 | 8 | static __thread int i = 0; 9 | 10 | #define NAME2_HIDDEN(a, b) a ## b 11 | #define NAME2(a, b) NAME2_HIDDEN(a,b) 12 | 13 | int *NAME2(testlib_dummy_get_, TESTLIB_NAME) () 14 | { 15 | return &i; 16 | } 17 | -------------------------------------------------------------------------------- /test-now.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test-static-tls.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test-static-tls: Verify that libgtk3-nocsd.so doesn't use up an 3 | * overflow DTV entry in the loaded program 4 | * 5 | * Logic is simple: try to dlopen() as many different libraries as 6 | * possible that contain static TLS entries (62 were built by the 7 | * Makefile), so that it can be determined what the cutoff is for 8 | * the case with and without LD_PRELOAD. (The cutoff is limited by 9 | * the number of overflow static DTV entries the dynamic linker 10 | * users by default.) 11 | * 12 | * This program will output the number of libraries it could load. 13 | * 14 | * Threading is necessary, otherwise the DTV will not be used. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 24 | 25 | static const char *preloaded = "none"; 26 | 27 | static void *doit(void *dummy) 28 | { 29 | char buf[] = "testlibs/libdummy-_.so.0"; /* replace char @ index 18 (_) */ 30 | char buf2[] = "testlib_dummy_get__"; /* replace char @ index 18 (_) */ 31 | void *hdl; 32 | int *(*fn)(); 33 | size_t c; 34 | int lastlib = -1; 35 | int *ptr; 36 | 37 | for (c = 0; c < sizeof(alphabet) - 1; c++) { 38 | buf[18] = alphabet[c]; 39 | buf2[18] = alphabet[c]; 40 | hdl = dlopen(buf, RTLD_NOW | RTLD_GLOBAL); 41 | if (!hdl) { 42 | if (lastlib < 0) { 43 | printf("ERROR[preloaded = %s]: couldn't load ANY library at all: %s\n", preloaded, dlerror()); 44 | break; 45 | } 46 | printf("%d\n", lastlib + 1); 47 | break; 48 | } 49 | fn = (int *(*)())dlsym(hdl, buf2); 50 | if (!fn) { 51 | printf("ERROR[preloaded = %s]: symbol %s not found: %s\n", preloaded, buf2, dlerror()); 52 | break; 53 | } 54 | ptr = fn(); 55 | if (*ptr != 0) { 56 | printf("ERROR[preloaded = %s]: function %s did not give expected result\n", preloaded, buf2); 57 | break; 58 | } 59 | lastlib = c; 60 | } 61 | 62 | if (c == sizeof(alphabet) - 1) 63 | printf("%d\n", lastlib + 1); 64 | 65 | return dummy; 66 | } 67 | 68 | int main(int argc, char **argv) 69 | { 70 | int r; 71 | pthread_t thread; 72 | 73 | if (argc >= 2) 74 | preloaded = argv[1]; 75 | 76 | r = pthread_create(&thread, NULL, doit, NULL); 77 | if (r < 0) { 78 | printf("ERROR[preloaded = %s]: could not create thread: %s\n", preloaded, strerror(errno)); 79 | return 1; 80 | } 81 | (void) pthread_join(thread, NULL); 82 | return 0; 83 | } 84 | --------------------------------------------------------------------------------