cinnamon-screensaver-6.2.0/0000775000175000017500000000000014632071776014600 5ustar fabiofabiocinnamon-screensaver-6.2.0/debian/0000775000175000017500000000000014632071776016022 5ustar fabiofabiocinnamon-screensaver-6.2.0/meson.build0000664000175000017500000001064114632071776016744 0ustar fabiofabioproject('cinnamon-screensaver', 'c', version : '6.2.0', meson_version : '>=0.56.0') cc = meson.get_compiler('c') conf = configuration_data() conf.set_quoted('PACKAGE', meson.project_name()) conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) # project options # commandline options prefix = get_option('prefix') bindir = join_paths(prefix, get_option('bindir')) datadir = join_paths(prefix, get_option('datadir')) pkgdatadir = join_paths(datadir, meson.project_name()) libexecdir = join_paths(prefix, get_option('libexecdir')) libdir = join_paths(prefix, get_option('libdir')) #needed? sysconfdir = join_paths(prefix, get_option('sysconfdir')) localstatedir = join_paths(prefix, get_option('localstatedir')) localedir = join_paths(prefix, get_option('localedir')) # object used for configure_file because meson 0.47 is needed for dicts # and 0.49 for using them in configure_file misc_conf = configuration_data() misc_conf.set('prefix', prefix) misc_conf.set('EXPANDED_BINDIR', bindir) misc_conf.set('datadir', datadir) misc_conf.set('pkgdatadir', pkgdatadir) misc_conf.set('libexecdir', libexecdir) misc_conf.set('libdir', libdir) misc_conf.set('PACKAGE', meson.project_name()) misc_conf.set('VERSION', meson.project_version()) misc_conf.set('GETTEXT_PACKAGE', meson.project_name()) gtk = dependency('gtk+-3.0') glib = dependency('glib-2.0') gio = dependency('gio-2.0') gio_unix = dependency('gio-unix-2.0') gthread = dependency('gthread-2.0') gobject = dependency('gobject-2.0') gdk = dependency('gdk-x11-3.0') x11 = dependency('x11') xext = dependency('xext') xrandr = dependency('xrandr', required: false) m = cc.find_library('m') xdo = dependency('libxdo', required: false) if not xdo.found() xdo = cc.find_library('xdo') endif dbus_services_dir = dependency('dbus-1').get_variable(pkgconfig: 'session_bus_services_dir', pkgconfig_define: ['datadir', datadir]) # check for symbols and headers foreach header : [ 'unistd.h' ] if cc.has_header(header) conf.set('HAVE_' + header.underscorify().to_upper(), true) endif endforeach foreach sym : [ 'sigaction' ] if cc.has_function(sym, args : '-D_GNU_SOURCE') conf.set('HAVE_' + sym.to_upper(), true) endif endforeach use_xinerama = get_option('xinerama') if use_xinerama if host_machine.system() == 'solaris' xinerama = cc.find_library('Xext') xinerama_h = cc.has_header('X11/extensions/xinerama.h') if not xinerama.found() or xinerama_h error('could not find usable xinerama library') endif conf.set('HAVE_SOLARIS_XINERAMA', 1) else xinerama = dependency('xinerama') conf.set('HAVE_XFREE_XINERAMA', 1) endif conf.set('HAVE_XINERAMA', 1) endif pam_compile = '''#include #include #include int main () { pam_handle_t *pamh = 0; char *s = pam_strerror(pamh, PAM_SUCCESS); return 0; }''' pam = cc.find_library('pam') if not cc.has_function('sigtimedwait') pam = [pam, cc.find_library('rt')] endif if cc.compiles(pam_compile) conf.set('PAM_STRERROR_TWO_ARGS', 1) endif # this check is not used anywhere if cc.has_function('pam_syslog', dependencies: pam) conf.set('HAVE_PAM_SYSLOG', 1) endif # Sun-type pam, this check is not used anywhere if not cc.has_header_symbol('security/pam_appl.h', 'const struct pam_message') conf.set('PAM_MESSAGE_NONCONST', 1) endif # do we care if the header exists? Just use pkg-config if cc.has_header('X11/extensions/Xrandr.h') xrandr = 'Xrandr' else xrandr = '' endif xrandr = cc.find_library(xrandr, required: false) if xrandr.found() conf.set('HAVE_RANDR', 1) endif if cc.has_header('X11/extensions/shape.h') conf.set('HAVE_SHAPE_EXT', 1) endif conf_h = configure_file( output : 'config.h', configuration : conf) inc = include_directories('.') if not get_option('deprecated-warnings') add_global_arguments([ '-Wno-deprecated-declarations', '-Wno-deprecated', '-Wno-declaration-after-statement', '-DGLIB_DISABLE_DEPRECATION_WARNINGS', ], language: 'c', ) endif # add_global_arguments( # [ # '-Wall', # '-Wextra', # '-Wno-unused-parameter', # '-Wmissing-prototypes', # '-Wstrict-prototypes', # '-pedantic' # ], # language: 'c' # ) subdir('install-scripts') subdir('libcscreensaver') subdir('data') subdir('src') subdir('backup-locker') cinnamon-screensaver-6.2.0/.gitignore0000664000175000017500000000024514632071776016571 0ustar fabiofabioobj-* debian/tmp debian/libcscreensaver0 debian/libcscreensaver-dbg debian/cinnamon-screensaver *.debhelper* *.substvars debian/debhelper-build-stamp debian/files cinnamon-screensaver-6.2.0/data/0000775000175000017500000000000014632071776015511 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/meson.build0000664000175000017500000000135614632071776017660 0ustar fabiofabiosubdir('icons') pamdir = get_option('pam-prefix') if pamdir == '' pamdir = sysconfdir endif dbus_service = configure_file( output: 'org.cinnamon.ScreenSaver.service', input: 'org.cinnamon.ScreenSaver.service.in', # meson 0.49 # configuration: { 'EXPANDED_BINDIR': bindir } configuration: misc_conf ) if get_option('use-debian-pam') install_data( 'cinnamon-screensaver.pam.debian', rename: 'cinnamon-screensaver', install_dir: join_paths(pamdir, 'pam.d') ) else install_data( 'cinnamon-screensaver', install_dir: join_paths(pamdir, 'pam.d') ) endif install_data('org.cinnamon.ScreenSaver.desktop', install_dir: join_paths(datadir, 'applications')) install_data(dbus_service, install_dir: dbus_services_dir) cinnamon-screensaver-6.2.0/data/org.cinnamon.ScreenSaver.service.in0000664000175000017500000000013214632071776024303 0ustar fabiofabio[D-BUS Service] Name=org.cinnamon.ScreenSaver Exec=@EXPANDED_BINDIR@/cinnamon-screensaver cinnamon-screensaver-6.2.0/data/icons/0000775000175000017500000000000014632071776016624 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/meson.build0000664000175000017500000000010514632071776020762 0ustar fabiofabioinstall_subdir('hicolor', install_dir: join_paths(datadir, 'icons')) cinnamon-screensaver-6.2.0/data/icons/hicolor/0000775000175000017500000000000014632071776020263 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/0000775000175000017500000000000014632071776022031 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/actions/0000775000175000017500000000000014632071776023471 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/actions/screensaver-unlock-symbolic.svg0000664000175000017500000000512514632071776031645 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme cinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/actions/screensaver-switch-users-symbolic.svg0000664000175000017500000000533114632071776033011 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme cinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/status/0000775000175000017500000000000014632071776023354 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/status/screensaver-notification-symbolic.svg0000664000175000017500000000451114632071776032721 0ustar fabiofabio image/svg+xml Gnome Symbolic Icon Theme Gnome Symbolic Icon Theme cinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/status/screensaver-blank.svg0000664000175000017500000000367014632071776027510 0ustar fabiofabio image/svg+xml cinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/apps/0000775000175000017500000000000014632071776022774 5ustar fabiofabiocinnamon-screensaver-6.2.0/data/icons/hicolor/scalable/apps/csr-backup-locker-icon.svg0000664000175000017500000000426414632071776027760 0ustar fabiofabio image/svg+xml cinnamon-screensaver-6.2.0/data/org.cinnamon.ScreenSaver.desktop0000664000175000017500000000026414632071776023715 0ustar fabiofabio[Desktop Entry] Type=Application Name=Screensaver Comment=Launch screensaver and locker program Exec=cinnamon-screensaver OnlyShowIn=X-Cinnamon; NoDisplay=true DBusActivatable=truecinnamon-screensaver-6.2.0/data/cinnamon-screensaver0000664000175000017500000000074214632071776021557 0ustar fabiofabio#%PAM-1.0 # Fedora & Arch -auth sufficient pam_selinux_permit.so auth include system-auth -auth optional pam_gnome_keyring.so account include system-auth password include system-auth session include system-auth # SuSE/Novell #auth include common-auth #auth optional pam_gnome_keyring.so #account include common-account #password include common-password #session include common-session cinnamon-screensaver-6.2.0/data/cinnamon-screensaver.pam.debian0000664000175000017500000000007014632071776023546 0ustar fabiofabio@include common-auth auth optional pam_gnome_keyring.so cinnamon-screensaver-6.2.0/AUTHORS0000664000175000017500000000005114632071776015644 0ustar fabiofabioMichael Webster cinnamon-screensaver-6.2.0/COPYING0000664000175000017500000004311314632071776015635 0ustar fabiofabio GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street - Suite 500, Boston, MA, 02110-1335, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA, 02110-1335, USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. cinnamon-screensaver-6.2.0/.github/0000775000175000017500000000000014632071776016140 5ustar fabiofabiocinnamon-screensaver-6.2.0/.github/workflows/0000775000175000017500000000000014632071776020175 5ustar fabiofabiocinnamon-screensaver-6.2.0/.github/workflows/build.yml0000664000175000017500000000114314632071776022016 0ustar fabiofabioname: Build on: push: branches: - master pull_request: branches: - master workflow_dispatch: inputs: debug_enabled: type: boolean description: 'Start an SSH server on failure.' required: false default: false jobs: build: uses: linuxmint/github-actions/.github/workflows/do-builds.yml@master with: commit_id: master ############################## Comma separated list - like 'linuxmint/xapp, linuxmint/cinnamon-desktop' dependencies: linuxmint/xapp, linuxmint/cinnamon-desktop ############################## cinnamon-screensaver-6.2.0/.github/ISSUE_TEMPLATE/0000775000175000017500000000000014632071776020323 5ustar fabiofabiocinnamon-screensaver-6.2.0/.github/ISSUE_TEMPLATE/bug_report.yaml0000664000175000017500000000666714632071776023376 0ustar fabiofabioname: Bug Report description: File a bug report body: - type: markdown attributes: value: | ### STOP! Before continuing: - Please try searching for any existing report that may match the behavior you're seeing. *If you find one, add to that issue, instead of opening a new one*. Include any relevant details that may differ from the original author's. - Check your logs - there may be some obvious cause for your trouble - in Linux Mint you can check ~/.xsession-errors (a hidden file in your Home folder). - Run cinnamon-screensaver from a terminal to see if any errors or warnings are printed while reproducing the issue: `cinnamon-screensaver --debug --hold` - If this is a *crash*, provide information about it if possible (CoreDump file, stack trace, etc...). In Linux Mint you can check the System Reports program. Entries may show as 'backup-locker' as well. --- Thank you for taking the time to report this issue. To allow us to work as efficiently as possible at resolving this, we need some information from you. - type: input id: distro attributes: label: Distribution description: Which Linux distribution are you using? Please be as specific as possible. placeholder: "example: Mint 21.1" validations: required: true - type: input id: pkgver attributes: label: Package version description: Please provide the cinnamon-screensaver version. You can get this by running `cinnamon-screensaver --version` in a terminal. placeholder: "example: 5.8.0" validations: required: true - type: input id: graphics attributes: label: Graphics hardware in use description: Please provide information about your graphics hardware, if known. If you are using a dual-gpu system please specify that also. placeholder: "example: NVIDIA GeForce GTX 1660 TI" validations: required: false - type: dropdown id: frequency attributes: label: Frequency description: How often does this behavior occur? options: - Always - Quite often - Only occasionally validations: required: true - type: textarea id: current-behavior attributes: label: Bug description description: Please describe what is happening validations: required: true - type: textarea id: steps attributes: label: Steps to reproduce description: Please try to provide **detailed** steps on the most direct way to reproduce this issue. The chances of a bug being fixed go up **considerably** if we are able to duplicate the behavior ourselves. validations: required: true - type: textarea id: expected-behavior attributes: label: Expected behavior description: Describe what you think should happen instead of the current behavior. validations: required: true - type: textarea id: more-info attributes: label: Additional information description: You can add any other information you think may be relevant. validations: required: false - type: markdown attributes: value: | #### By submitting this report you agree to behave respectfully and in a mature manner. If in doubt, refer to the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule) and [Github's Community Guidelines](https://docs.github.com/en/site-policy/github-terms/github-community-guidelines). cinnamon-screensaver-6.2.0/.github/ISSUE_TEMPLATE/feature_request.md0000664000175000017500000000042014632071776024044 0ustar fabiofabio--- name: Feature request (OBSOLETE) about: Feature requests are no longer accepted here, please use https://github.com/orgs/linuxmint/discussions instead. title: "" labels: ["FEATURE REQUEST"] assignees: '' --- Please use: https://github.com/orgs/linuxmint/discussionscinnamon-screensaver-6.2.0/makepot0000775000175000017500000000040614632071776016166 0ustar fabiofabio#!/bin/bash xgettext --language=Python --from-code=UTF-8 --keyword=_ --output=cinnamon-screensaver.pot src/*.py xgettext --language=C --from-code=UTF-8 --add-comments --keyword=_ --keyword=N_ --join-existing --output=cinnamon-screensaver.pot backup-locker/*.c cinnamon-screensaver-6.2.0/install-scripts/0000775000175000017500000000000014632071776017733 5ustar fabiofabiocinnamon-screensaver-6.2.0/install-scripts/meson.build0000664000175000017500000000106214632071776022074 0ustar fabiofabio# These scripts run as post-installation scripts. # They're designed to do nothing if DESTDIR is set, which happens # during debian builds for instance - there's a fake install target # so running these would be pointless. # When using deb packaging, these aren't needed, as these operations # are run automatically by the package manager. # They're really only necessary in straight builds where 'ninja install' # will be run directly, to install the program onto the system. # Generate python bytecode meson.add_install_script('meson_compile_python.py') cinnamon-screensaver-6.2.0/install-scripts/meson_compile_python.py0000664000175000017500000000045114632071776024537 0ustar fabiofabio#!/usr/bin/python3 import os import subprocess pythondir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], 'share', 'cinnamon-screensaver') if not os.environ.get('DESTDIR'): print('Generating python bytecode...') subprocess.call(['sh', '-c', 'python3 -m compileall "%s"' % pythondir]) cinnamon-screensaver-6.2.0/README.md0000664000175000017500000001657514632071776016075 0ustar fabiofabio# Cinnamon Screensaver ![build](https://github.com/linuxmint/cinnamon-screensaver/actions/workflows/build.yml/badge.svg) ### Program Entry Main entry is from /usr/bin/cinnamon-screensaver to cinnamon-screensaver-main.py, which launches a dbus service (org.cinnamon.Screensaver) service.py launches the ScreensaverManager (manager.py), which is central command for all things here, as well as a session proxy to listen for idle changes from cinnamon-session, and logind or consolekit proxues. ### Running At this point we're listening, either for a command sent by the user (like via cinnamon-screensaver-command) or from one of our proxies. ### Locking (active) Once a lock command is received, the manager spawns a main window (Stage in stage.py) which covers the entire Gdk screen size (an imaginary rectangle containing all monitors). Think of this overlay as very basic window manager. Into this overlay it places: - MonitorViews (monitorView.py) - one for each monitor, painted with the user's background, placed at the exact location of each window. - A clock widget, which bounces around mostly randomly around all monitors - An unlock widget, which is initially hidden At this point we're also now setup to receive user events like button and key presses, and motion events. This is assisted by the GrabHelper and EventHandler that ensure only those keystrokes we want are allowed, and muffin is blocked from processing global keybindings. - Any motion is a wake event (show the unlock widget or kill the stage if we're not locked) - Any click is a wake event (ditto) - Keypresses are first filtered - media keys are checked, and things like volume, brightness controls will not raise the unlock dialog (for now, only if they're simple key combinations - complex ones with modifiers will still do it) - key strokes for characters will be forwarded to the dialog - if you start typing your password on a blank screensaver screen, it will be forwarded to the password entry. ### Unlocking Once the user types their password and hits enter or clicks unlock, we authenticate via a pam helper in cinnamon-desktop. If the authentication is successful, all widgets are destroyed, all grabs released, and we go back to the idle listening state. Files: application.css: Application priority css, stuff to make the unlock dialog, clock widgets look ok against varying backgrounds cinnamon-screensaver-command.py: Send commands to the screensaver via the command line cinnamon-screensaver-main.py: Main entry point into the program, handles a couple of arguments, adds our css provider, fires up the ScreensaverService. baseWindow.py: A base revealer class that the Clock and Unlock widgets implement - any widget that will move around the Stage should implement this (except the monitorViews) - the revealer base lets you do simple fade-ins and fade-outs. cinnamonProxy.py: Connects to Cinnamon's dbus interface, asks Cinnamon to make sure expo or overview are closed (as they make a server grab that we can't wrest focus from, preventing the screensaver from activating). clock.py (inherits BaseWindow): The clock widget that bounces around the screen, this contains all of that. Positioning is done via a randomizer on a timer that adjusts vertical and horizontal alignment properties (one of start, center, or end) along with current monitor, which is used by the Stage positioning function to tell it where to place the Clock widget. config.py.in (compiles into config.py): Contains system-specific file locations that are used by various files here. consoleKitProxy.py: Listens to commands from consolekit over dbus constants.py: A file containing simply a list of hardcoded screens/values that various files here use. eventHandler.py: Gets forwarded all events received from various sources, and acts on them. Does not propagate except in the case of motion. fader.py: A helper for the Stage that uses a frame tick callback to fade the stage in our out over a specific timeframe. Since it uses the frame clock instead of a GSource, times remain consistent, and only as many frames are drawn as there is time for (a 1 second animation will take one second, but you might not see all 60 frame draws) focusNavigator.py: A helper for navigating focus and performing activation on the navigable widgets on the unlock screen. Since we funnel events so strictly, and don't perform any propagation (to prevent wm or desktop keybindings from triggering) we have to manage the focus ourselves, as well as performing activation on the focused widget when enter or space is pressed. grabHelper.py: A helper for achieving exclusive mouse and key grabs, as well as hiding the mouse pointer when appropriate. keybindings.py: gets fed key events from the EventHandler, and acts on them or not - allows certain media keys, handles escape, enter, space events. logindProxy.py: Listens to commands from logind over dbus manager.py: This is the head honcho, the big cheese, el numero uno. It spawns the GrabHelper, FocusNavigator, along with the session and logind/ck proxies. It acts on commands received from there, as well as our own dbus service (ScreensaverService). It manages all the flags in status.py, spawns and despawns the stage. monitorView.py: This is a widget that gets placed in the stage that provides the backgrounds or screensaver plugin view. There is one per monitor, and they are positioned directly where each monitor is by the Stage positioning function. It handles transitioning between backgrounds (during a slideshow) and transitioning between plugins and wallpaper. service.py: This is our implementation of the dbus service "org.cinnamon.Screensaver" - commands received via this interface are sent to the manager for answers and action. This spawns the manager. sessionProxy.py: Listens to cinnamon-session for idle changes and notifies the manager when they change. settings.py: holds our GSettings instances, as well as getters for each of the different keys. Also takes care of our CinnamonDesktop BG instance, and updates it when settings change. stage.py: This is our toplevel window, a GtkWindow. It is made the size of the GdkScreen (a theoretical rectangle that exactly encompasses all monitors). At its core is a GtkOverlay, which we sort of use like a window manager. The position_overlay_child callback is used to position our children (Clock, UnlockWidget, MonitorView). A forced call to this position function can be done via overlay.queue_resize(). This class talks fairly freely with the manager, even though it is spawned and despawned by the manager repeatedly (when the screensaver is activated/deactivated) status.py: A global state tracker, used by many widgets - Active means the screensaver stage exists, and we're displaying wallpaper or whatever. Locked means we will need to enter our password to unlock. Awake means the unlock dialog is currently shown. focusChain is where a list of focusable widgets should be stored that tab will navigate between. (This is currently done just in the UnlockDialog) trackers.py: A utility for easy tracking of timers and signal connections. It basically performs any cleanup for you, with no need to track source ids or signal ids generally. unlock.py: Provides the unlock dialog, including the user image, name, password entry and buttons utils.py: Various utilities that seem best to keep in one place. x11.py: X11-specific focus helper function - optional, python3-xlib doesn't exist everywhere yet. cinnamon-screensaver-6.2.0/libcscreensaver/0000775000175000017500000000000014632071776017752 5ustar fabiofabiocinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.login1.Manager.xml0000664000175000017500000000056414632071776026343 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/org.cinnamon.SettingsDaemon.KeybindingHandler.xml0000664000175000017500000000076714632071776031401 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/meson.build0000664000175000017500000001242614632071776022121 0ustar fabiofabiognome = import('gnome') pkgconfig = import('pkgconfig') dbus_files = [ [ 'cs-cinnamon-proxy', [ ['org.Cinnamon', 'org.gtk.GDBus.C.Name', 'Cinnamon'] ], 'org.Cinnamon' ], [ 'cs-session-presence-proxy', [ ['org.gnome.SessionManager.Presence', 'org.gtk.GDBus.C.Name', 'SessionPresence'] ], 'org.gnome.SessionManager.Presence' ], [ 'cs-upower-proxy', [ ['org.freedesktop.UPower', 'org.gtk.GDBus.C.Name', 'UPower'], ['org.freedesktop.UPower.EnumerateDevices()[devices]', 'org.gtk.GDBus.C.ForceGVariant', 'true'] ], 'org.freedesktop.UPower' ], [ 'cs-upower-device-proxy', [ ['org.freedesktop.UPower.Device', 'org.gtk.GDBus.C.Name', 'UPowerDevice'] ], 'org.freedesktop.UPower.Device' ], [ 'cs-login-manager-proxy', [ ['org.freedesktop.login1.Manager', 'org.gtk.GDBus.C.Name', 'LogindManager'] ], 'org.freedesktop.login1.Manager' ], [ 'cs-logind-session-proxy', [ ['org.freedesktop.login1.Session', 'org.gtk.GDBus.C.Name', 'LogindSession'] ], 'org.freedesktop.login1.Session' ], [ 'cs-consolekit-manager-proxy', [ ['org.freedesktop.ConsoleKit.Manager', 'org.gtk.GDBus.C.Name', 'ConsoleKitManager'] ], 'org.freedesktop.ConsoleKit.Manager' ], [ 'cs-consolekit-session-proxy', [ ['org.freedesktop.ConsoleKit.Session', 'org.gtk.GDBus.C.Name', 'ConsoleKitSession'] ], 'org.freedesktop.ConsoleKit.Session' ], [ 'cs-screensaver-proxy', [ ['org.cinnamon.ScreenSaver', 'org.gtk.GDBus.C.Name', 'ScreenSaver'] ], 'org.cinnamon.ScreenSaver' ], [ 'cs-keybinding-handler-proxy', [ ['org.cinnamon.SettingsDaemon.KeybindingHandler', 'org.gtk.GDBus.C.Name', 'KeybindingHandler'] ], 'org.cinnamon.SettingsDaemon.KeybindingHandler' ], [ 'cs-media-player-proxy', [ ['org.mpris.MediaPlayer2.Player', 'org.gtk.GDBus.C.Name', 'MediaPlayer'] ], 'org.mpris.MediaPlayer2.Player' ], [ 'cs-muffin-displayconfig-proxy', [ ['org.cinnamon.Muffin.DisplayConfig', 'org.gtk.GDBus.C.Name', 'MuffinDisplayConfig'] ], 'org.cinnamon.Muffin.DisplayConfig' ], [ 'cs-accounts-service-proxy', [ ['org.freedesktop.Accounts', 'org.gtk.GDBus.C.Name', 'AccountsService'] ], 'org.freedesktop.Accounts' ], [ 'cs-accounts-user-proxy', [ ['org.freedesktop.Accounts.User', 'org.gtk.GDBus.C.Name', 'AccountsUser'] ], 'org.freedesktop.Accounts.User' ], ] dbus_built = [] # foreach dbus: dbus_files # dbus_built += gnome.gdbus_codegen(dbus[0], '@0@.xml'.format(dbus[2]), # namespace: 'Cs', # annotations: dbus[1] # ) # endforeach # FIXME: Ugly workaround that simulates the generation of # two different targets, so headers can be included # explicitly for introspection. # # This can be removed once all platforms use meson >=.46 # and replaced with gnome.gdbus_codegen codegen = find_program('g-codegen.py') foreach dbus: dbus_files annotations = [] foreach item: dbus[1] annotations += '--annotate' annotations += item endforeach annotations_string = ' '.join(annotations) generated = custom_target( dbus[0], input: '@0@.xml'.format(dbus[2]), output: ['@0@.h'.format(dbus[0]), '@0@.c'.format(dbus[0])], command: [ codegen, dbus[2], dbus[0], annotations_string, meson.current_build_dir(), '@INPUT@', '@OUTPUT@' ] ) dbus_built += generated[0] dbus_built += generated[1] endforeach # non-pam auth implementations are not implemented at this time auth_impl = 'pam' cscreensaver_sources = [ 'cs-auth-@0@.c'.format(auth_impl), 'subprocs.c', 'subprocs.h', 'setuid.c' ] gir_sources = [ 'cs-event-grabber.h', 'cs-event-grabber.c', 'cs-gdk-event-filter.h', 'cs-gdk-event-filter-x11.c', 'cs-notification-watcher.h', 'cs-notification-watcher.c', 'cs-screen.h', 'cs-screen-x11.c', dbus_built ] libcscreensaver_deps = [gobject, gtk, gdk, x11, xrandr, xext, glib, gio, gio_unix, gthread, pam, m, xdo] if use_xinerama libcscreensaver_deps += xinerama endif libcscreensaver = library( 'cscreensaver', cscreensaver_sources + gir_sources, version: '0.0.0', include_directories: inc, cpp_args: '-DG_LOG_DOMAIN="CScreensaver"', dependencies: libcscreensaver_deps, install: true ) pkgconfig.generate( # TODO meson 0.46 replace libraries, name, version with positional library argument libraries: libcscreensaver, name: 'cscreensaver', version: meson.project_version(), description: 'Small utility lib used by cinnamon-screensaver v3.1+ to provide a GdkWindow filter (not currently introspectable)', subdirs: 'cscreensaver', # these should be their variables, but meson < 0.46 strikes again requires: ['gobject-2.0', 'gtk+-3.0', 'gdk-3.0'] ) cscreensaver_gir = gnome.generate_gir( libcscreensaver, sources: gir_sources, namespace: 'CScreensaver', nsversion: '1.0', identifier_prefix: 'Cs', symbol_prefix: 'cs_', includes: 'Gtk-3.0', install: true ) test_passwd = executable( 'test-passwd', 'test-passwd.c', include_directories: inc, link_with: libcscreensaver, dependencies: [gobject, gtk, gdk], build_by_default: false ) cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.login1.Session.xml0000664000175000017500000000055014632071776026407 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/cs-event-grabber.h0000664000175000017500000000444314632071776023256 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2004-2006 William Jon McCann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA 02110-1335, USA. * * Authors: William Jon McCann * */ #ifndef __CS_EVENT_GRABBER_H #define __CS_EVENT_GRABBER_H #include #include G_BEGIN_DECLS #define CS_TYPE_EVENT_GRABBER (cs_event_grabber_get_type ()) G_DECLARE_FINAL_TYPE (CsEventGrabber, cs_event_grabber, CS, EVENT_GRABBER, GObject) CsEventGrabber * cs_event_grabber_new (gboolean debug); void cs_event_grabber_release (CsEventGrabber *grab); gboolean cs_event_grabber_release_mouse (CsEventGrabber *grab); gboolean cs_event_grabber_grab_window (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor); gboolean cs_event_grabber_grab_root (CsEventGrabber *grab, gboolean hide_cursor); gboolean cs_event_grabber_grab_offscreen (CsEventGrabber *grab, gboolean hide_cursor); void cs_event_grabber_move_to_window (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor); void cs_event_grabber_mouse_reset (CsEventGrabber *grab); void cs_event_grabber_keyboard_reset (CsEventGrabber *grab); G_END_DECLS #endif /* __CS_EVENT_GRABBER_H */ cinnamon-screensaver-6.2.0/libcscreensaver/cs-event-grabber.c0000664000175000017500000005465514632071776023263 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2004-2006 William Jon McCann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Authors: William Jon McCann * */ #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_XF86MISCSETGRABKEYSSTATE # include #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ #include "cs-event-grabber.h" #include "cs-screen.h" static void cs_event_grabber_class_init (CsEventGrabberClass *klass); static void cs_event_grabber_init (CsEventGrabber *grab); static void cs_event_grabber_finalize (GObject *object); typedef struct { GDBusConnection *session_bus; guint mouse_hide_cursor : 1; GdkWindow *mouse_grab_window; GdkWindow *keyboard_grab_window; GdkScreen *mouse_grab_screen; GdkScreen *keyboard_grab_screen; xdo_t *xdo; GtkWidget *invisible; } CsEventGrabberPrivate; struct _CsEventGrabber { GObject parent_instance; CsEventGrabberPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE (CsEventGrabber, cs_event_grabber, G_TYPE_OBJECT) static gboolean debug_mode = FALSE; #define DEBUG(...) if (debug_mode) g_printerr (__VA_ARGS__) static gpointer grab_object = NULL; static const char * grab_string (int status) { switch (status) { case GDK_GRAB_SUCCESS: return "GrabSuccess"; case GDK_GRAB_ALREADY_GRABBED: return "AlreadyGrabbed"; case GDK_GRAB_INVALID_TIME: return "GrabInvalidTime"; case GDK_GRAB_NOT_VIEWABLE: return "GrabNotViewable"; case GDK_GRAB_FROZEN: return "GrabFrozen"; default: { static char foo [255]; sprintf (foo, "unknown status: %d", status); return foo; } } } #ifdef HAVE_XF86MISCSETGRABKEYSSTATE /* This function enables and disables the Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any grabs and/or kill the grabbing client. That would effectively unlock the screen, so we don't like that. The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on in XF86Config. I believe they are disabled by default. This does not affect any other keys (specifically Ctrl-Alt-BS or Ctrl-Alt-F1) but I wish it did. Maybe it will someday. */ static void xorg_lock_smasher_set_active (CsEventGrabber *grab, gboolean active) { int status, event, error; if (!XF86MiscQueryExtension (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &event, &error)) { DEBUG ("No XFree86-Misc extension present\n"); return; } if (active) { DEBUG ("Enabling the x.org grab smasher\n"); } else { DEBUG ("Disabling the x.org grab smasher\n"); } gdk_error_trap_push (); status = XF86MiscSetGrabKeysState (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), active); gdk_display_sync (gdk_display_get_default ()); error = gdk_error_trap_pop (); if (active && status == MiscExtGrabStateAlready) { /* shut up, consider this success */ status = MiscExtGrabStateSuccess; } if (error == Success) { DEBUG ("XF86MiscSetGrabKeysState(%s) returned %s\n", active ? "on" : "off", (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" : status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" : status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" : "unknown value")); } else { DEBUG ("XF86MiscSetGrabKeysState(%s) failed with error code %d\n", active ? "on" : "off", error); } } #else static void xorg_lock_smasher_set_active (CsEventGrabber *grab, gboolean active) { } #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ static void maybe_cancel_ui_grab (CsEventGrabber *grab) { if (grab->priv->xdo == NULL) { return; } xdo_send_keysequence_window (grab->priv->xdo, CURRENTWINDOW, "Escape", 12000); // 12ms as suggested in xdo.h xdo_send_keysequence_window (grab->priv->xdo, CURRENTWINDOW, "Escape", 12000); } static int cs_event_grabber_get_keyboard (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen) { GdkGrabStatus status; g_return_val_if_fail (window != NULL, FALSE); g_return_val_if_fail (screen != NULL, FALSE); DEBUG ("Grabbing keyboard widget=0x%lx\n", (gulong) GDK_WINDOW_XID (window)); status = gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME); if (status == GDK_GRAB_SUCCESS) { if (grab->priv->keyboard_grab_window != NULL) { g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window), (gpointer *) &grab->priv->keyboard_grab_window); } grab->priv->keyboard_grab_window = window; g_object_add_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window), (gpointer *) &grab->priv->keyboard_grab_window); grab->priv->keyboard_grab_screen = screen; } else { DEBUG ("Couldn't grab keyboard! (%s)\n", grab_string (status)); } return status; } static int cs_event_grabber_get_mouse (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor) { GdkGrabStatus status; GdkCursor *cursor; g_return_val_if_fail (window != NULL, FALSE); g_return_val_if_fail (screen != NULL, FALSE); cursor = gdk_cursor_new (GDK_BLANK_CURSOR); DEBUG ("Grabbing mouse widget=0x%lx\n", (gulong) GDK_WINDOW_XID (window)); status = gdk_pointer_grab (window, TRUE, 0, NULL, (hide_cursor ? cursor : NULL), GDK_CURRENT_TIME); if (status == GDK_GRAB_SUCCESS) { if (grab->priv->mouse_grab_window != NULL) { g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window), (gpointer *) &grab->priv->mouse_grab_window); } grab->priv->mouse_grab_window = window; g_object_add_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window), (gpointer *) &grab->priv->mouse_grab_window); grab->priv->mouse_grab_screen = screen; grab->priv->mouse_hide_cursor = hide_cursor; } g_object_unref (cursor); return status; } void cs_event_grabber_keyboard_reset (CsEventGrabber *grab) { if (grab->priv->keyboard_grab_window != NULL) { g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window), (gpointer *) &grab->priv->keyboard_grab_window); } grab->priv->keyboard_grab_window = NULL; grab->priv->keyboard_grab_screen = NULL; } static gboolean cs_event_grabber_release_keyboard (CsEventGrabber *grab) { DEBUG ("Ungrabbing keyboard\n"); gdk_keyboard_ungrab (GDK_CURRENT_TIME); cs_event_grabber_keyboard_reset (grab); return TRUE; } void cs_event_grabber_mouse_reset (CsEventGrabber *grab) { if (grab->priv->mouse_grab_window != NULL) { g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window), (gpointer *) &grab->priv->mouse_grab_window); } grab->priv->mouse_grab_window = NULL; grab->priv->mouse_grab_screen = NULL; } gboolean cs_event_grabber_release_mouse (CsEventGrabber *grab) { DEBUG ("Ungrabbing pointer\n"); gdk_pointer_ungrab (GDK_CURRENT_TIME); cs_event_grabber_mouse_reset (grab); return TRUE; } static gboolean cs_event_grabber_move_mouse (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor) { gboolean result; GdkWindow *old_window; GdkScreen *old_screen; gboolean old_hide_cursor; /* if the pointer is not grabbed and we have a mouse_grab_window defined then we lost the grab */ if (! gdk_pointer_is_grabbed ()) { cs_event_grabber_mouse_reset (grab); } if (grab->priv->mouse_grab_window == window) { DEBUG ("Window 0x%lx is already grabbed, skipping\n", (gulong) GDK_WINDOW_XID (grab->priv->mouse_grab_window)); return TRUE; } #if 0 DEBUG ("Intentionally skipping move pointer grabs\n"); /* FIXME: GTK doesn't like having the pointer grabbed */ return TRUE; #else if (grab->priv->mouse_grab_window) { DEBUG ("Moving pointer grab from 0x%lx to 0x%lx\n", (gulong) GDK_WINDOW_XID (grab->priv->mouse_grab_window), (gulong) GDK_WINDOW_XID (window)); } else { DEBUG ("Getting pointer grab on 0x%lx\n", (gulong) GDK_WINDOW_XID (window)); } #endif DEBUG ("*** doing X server grab\n"); gdk_x11_grab_server (); old_window = grab->priv->mouse_grab_window; old_screen = grab->priv->mouse_grab_screen; old_hide_cursor = grab->priv->mouse_hide_cursor; if (old_window) { cs_event_grabber_release_mouse (grab); } result = cs_event_grabber_get_mouse (grab, window, screen, hide_cursor); if (result != GDK_GRAB_SUCCESS) { sleep (1); result = cs_event_grabber_get_mouse (grab, window, screen, hide_cursor); } if ((result != GDK_GRAB_SUCCESS) && old_window) { DEBUG ("Could not grab mouse for new window. Resuming previous grab.\n"); cs_event_grabber_get_mouse (grab, old_window, old_screen, old_hide_cursor); } DEBUG ("*** releasing X server grab\n"); gdk_x11_ungrab_server (); gdk_flush (); return (result == GDK_GRAB_SUCCESS); } static gboolean cs_event_grabber_move_keyboard (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen) { gboolean result; GdkWindow *old_window; GdkScreen *old_screen; if (grab->priv->keyboard_grab_window == window) { DEBUG ("Window 0x%lx is already grabbed, skipping\n", (gulong) GDK_WINDOW_XID (grab->priv->keyboard_grab_window)); return TRUE; } if (grab->priv->keyboard_grab_window != NULL) { DEBUG ("Moving keyboard grab from 0x%lx to 0x%lx\n", (gulong) GDK_WINDOW_XID (grab->priv->keyboard_grab_window), (gulong) GDK_WINDOW_XID (window)); } else { DEBUG ("Getting keyboard grab on 0x%lx\n", (gulong) GDK_WINDOW_XID (window)); } DEBUG ("*** doing X server grab\n"); gdk_x11_grab_server (); old_window = grab->priv->keyboard_grab_window; old_screen = grab->priv->keyboard_grab_screen; if (old_window) { cs_event_grabber_release_keyboard (grab); } result = cs_event_grabber_get_keyboard (grab, window, screen); if (result != GDK_GRAB_SUCCESS) { sleep (1); result = cs_event_grabber_get_keyboard (grab, window, screen); } if ((result != GDK_GRAB_SUCCESS) && old_window) { DEBUG ("Could not grab keyboard for new window. Resuming previous grab.\n"); cs_event_grabber_get_keyboard (grab, old_window, old_screen); } DEBUG ("*** releasing X server grab\n"); gdk_x11_ungrab_server (); gdk_flush (); return (result == GDK_GRAB_SUCCESS); } static void cs_event_grabber_nuke_focus (void) { Window focus = 0; int rev = 0; DEBUG ("Nuking focus\n"); gdk_error_trap_push (); XGetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &focus, &rev); XSetInputFocus (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), None, RevertToNone, CurrentTime); gdk_error_trap_pop_ignored (); } void cs_event_grabber_release (CsEventGrabber *grab) { DEBUG ("Releasing all grabs\n"); cs_event_grabber_release_mouse (grab); cs_event_grabber_release_keyboard (grab); /* FIXME: is it right to enable this ? */ xorg_lock_smasher_set_active (grab, TRUE); gdk_display_sync (gdk_display_get_default ()); gdk_flush (); } /* The Cinnamon Shell holds an X grab when we're in the overview; * ask it to bounce out before we try locking the screen. */ static void request_shell_exit_overview (CsEventGrabber *grab) { GDBusMessage *message; /* Shouldn't happen, but... */ if (!grab->priv->session_bus) return; message = g_dbus_message_new_method_call ("org.Cinnamon", "/org/Cinnamon", "org.freedesktop.DBus.Properties", "Set"); g_dbus_message_set_body (message, g_variant_new ("(ssv)", "org.Cinnamon", "OverviewActive", g_variant_new ("b", FALSE))); g_dbus_connection_send_message (grab->priv->session_bus, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (message); message = g_dbus_message_new_method_call ("org.Cinnamon", "/org/Cinnamon", "org.freedesktop.DBus.Properties", "Set"); g_dbus_message_set_body (message, g_variant_new ("(ssv)", "org.Cinnamon", "ExpoActive", g_variant_new ("b", FALSE))); g_dbus_connection_send_message (grab->priv->session_bus, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (message); } gboolean cs_event_grabber_grab_window (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor) { gboolean mstatus = FALSE; gboolean kstatus = FALSE; int i; int retries = 4; gboolean focus_fuckus = FALSE; /* First, have stuff we control in GNOME un-grab */ request_shell_exit_overview (grab); AGAIN: for (i = 0; i < retries; i++) { kstatus = cs_event_grabber_get_keyboard (grab, window, screen); if (kstatus == GDK_GRAB_SUCCESS) { break; } /* else, wait a second and try to grab again. */ sleep (1); } if (kstatus != GDK_GRAB_SUCCESS) { if (!focus_fuckus) { focus_fuckus = TRUE; maybe_cancel_ui_grab (grab); cs_event_grabber_nuke_focus (); goto AGAIN; } } for (i = 0; i < retries; i++) { mstatus = cs_event_grabber_get_mouse (grab, window, screen, hide_cursor); if (mstatus == GDK_GRAB_SUCCESS) { break; } /* else, wait a second and try to grab again. */ sleep (1); } if (mstatus != GDK_GRAB_SUCCESS) { DEBUG ("Couldn't grab pointer! (%s)\n", grab_string (mstatus)); } #if 0 /* FIXME: release the pointer grab so GTK will work */ event_grabber_release_mouse (grab); #endif /* When should we allow blanking to proceed? The current theory is that both a keyboard grab and a mouse grab are mandatory - If we don't have a keyboard grab, then we won't be able to read a password to unlock, so the kbd grab is manditory. - If we don't have a mouse grab, then we might not see mouse clicks as a signal to unblank, on-screen widgets won't work ideally, and event_grabber_move_to_window() will spin forever when it gets called. */ if (kstatus != GDK_GRAB_SUCCESS || mstatus != GDK_GRAB_SUCCESS) { /* Do not blank without a keyboard and mouse grabs. */ /* Release keyboard or mouse which was grabbed. */ if (kstatus == GDK_GRAB_SUCCESS) { cs_event_grabber_release_keyboard (grab); } if (mstatus == GDK_GRAB_SUCCESS) { cs_event_grabber_release_mouse (grab); } return FALSE; } /* Grab is good, go ahead and blank. */ return TRUE; } /* this is used to grab the keyboard and mouse to the root */ gboolean cs_event_grabber_grab_root (CsEventGrabber *grab, gboolean hide_cursor) { GdkDisplay *display; GdkWindow *root; GdkScreen *screen; gboolean res; DEBUG ("Grabbing the root window\n"); display = gdk_display_get_default (); gdk_display_get_pointer (display, &screen, NULL, NULL, NULL); root = gdk_screen_get_root_window (screen); res = cs_event_grabber_grab_window (grab, root, screen, hide_cursor); return res; } /* this is used to grab the keyboard and mouse to an offscreen window */ gboolean cs_event_grabber_grab_offscreen (CsEventGrabber *grab, gboolean hide_cursor) { GdkScreen *screen; gboolean res; DEBUG ("Grabbing an offscreen window\n"); screen = gtk_invisible_get_screen (GTK_INVISIBLE (grab->priv->invisible)); res = cs_event_grabber_grab_window (grab, gtk_widget_get_window (grab->priv->invisible), screen, hide_cursor); return res; } /* This is similar to cs_event_grabber_grab_window but doesn't fail */ void cs_event_grabber_move_to_window (CsEventGrabber *grab, GdkWindow *window, GdkScreen *screen, gboolean hide_cursor) { gboolean result = FALSE; g_return_if_fail (CS_IS_EVENT_GRABBER (grab)); xorg_lock_smasher_set_active (grab, FALSE); do { result = cs_event_grabber_move_keyboard (grab, window, screen); gdk_flush (); } while (!result); do { result = cs_event_grabber_move_mouse (grab, window, screen, hide_cursor); gdk_flush (); } while (!result); } static void cs_event_grabber_class_init (CsEventGrabberClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cs_event_grabber_finalize; } static void cs_event_grabber_init (CsEventGrabber *grab) { grab->priv = cs_event_grabber_get_instance_private (grab); grab->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); grab->priv->xdo = xdo_new (NULL); if (grab->priv->xdo == NULL) { g_warning ("Xdo context could not be created."); } grab->priv->mouse_hide_cursor = FALSE; grab->priv->invisible = gtk_invisible_new (); cs_screen_set_net_wm_name (gtk_widget_get_window (grab->priv->invisible), "event-grabber-window"); gtk_widget_show (grab->priv->invisible); } static void cs_event_grabber_finalize (GObject *object) { CsEventGrabber *grab; g_return_if_fail (object != NULL); g_return_if_fail (CS_IS_EVENT_GRABBER (object)); grab = CS_EVENT_GRABBER (object); g_object_unref (grab->priv->session_bus); g_return_if_fail (grab->priv != NULL); gtk_widget_destroy (grab->priv->invisible); xdo_free (grab->priv->xdo); G_OBJECT_CLASS (cs_event_grabber_parent_class)->finalize (object); } CsEventGrabber * cs_event_grabber_new (gboolean debug) { debug_mode = debug; if (grab_object) { g_object_ref (grab_object); } else { grab_object = g_object_new (CS_TYPE_EVENT_GRABBER, NULL); g_object_add_weak_pointer (grab_object, (gpointer *) &grab_object); } return CS_EVENT_GRABBER (grab_object); } cinnamon-screensaver-6.2.0/libcscreensaver/cs-notification-watcher.h0000664000175000017500000000257414632071776024657 0ustar fabiofabio #ifndef __CS_NOTIFICATION_WATCHER_H #define __CS_NOTIFICATION_WATCHER_H #include #include #include G_BEGIN_DECLS #define CS_TYPE_NOTIFICATION_WATCHER (cs_notification_watcher_get_type ()) #define CS_NOTIFICATION_WATCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CS_TYPE_NOTIFICATION_WATCHER, CsNotificationWatcher)) #define CS_NOTIFICATION_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CS_TYPE_NOTIFICATION_WATCHER, CsNotificationWatcherClass)) #define CS_IS_NOTIFICATION_WATCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CS_TYPE_NOTIFICATION_WATCHER)) #define CS_IS_NOTIFICATION_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CS_TYPE_NOTIFICATION_WATCHER)) #define CS_NOTIFICATION_WATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CS_TYPE_NOTIFICATION_WATCHER, CsNotificationWatcherClass)) typedef struct { GObject obj; GDBusConnection *connection; gint filter_id; gboolean debug; } CsNotificationWatcher; typedef struct { GObjectClass parent_class; void (* notification_received) (CsNotificationWatcher *watcher, const gchar *sender); } CsNotificationWatcherClass; GType cs_notification_watcher_get_type (void); CsNotificationWatcher *cs_notification_watcher_new (gboolean debug); G_END_DECLS #endif /* __CS_NOTIFICATION_WATCHER_H */ cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.ConsoleKit.Manager.xml0000664000175000017500000000212614632071776027220 0ustar fabiofabio The object identifier for the current session Attempts to determine the session ID that the caller belongs to. See this example of using dbus-send: dbus-send --system --dest=org.freedesktop.ConsoleKit \ --type=method_call --print-reply --reply-timeout=2000 \ /org/freedesktop/ConsoleKit/Manager \ org.freedesktop.ConsoleKit.Manager.GetCurrentSession cinnamon-screensaver-6.2.0/libcscreensaver/cs-screen.h0000664000175000017500000000552514632071776022014 0ustar fabiofabio #ifndef __CS_SCREEN_H #define __CS_SCREEN_H #include #include #include G_BEGIN_DECLS #define CS_TYPE_SCREEN (cs_screen_get_type ()) #define CS_SCREEN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CS_TYPE_SCREEN, CsScreen)) #define CS_SCREEN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CS_TYPE_SCREEN, CsScreenClass)) #define CS_IS_SCREEN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CS_TYPE_SCREEN)) #define CS_IS_SCREEN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CS_TYPE_SCREEN)) #define CS_SCREEN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CS_TYPE_SCREEN, CsScreenClass)) typedef struct _CsMonitorInfo CsMonitorInfo; struct _CsMonitorInfo { int number; GdkRectangle rect; gboolean is_primary; XID output; /* The primary or first output for this crtc, None if no xrandr */ }; typedef struct { GObject obj; GdkRectangle rect; GdkScreen *gdk_screen; CsMonitorInfo *monitor_infos; gint primary_monitor_index; gint n_monitor_infos; gulong monitors_changed_id; gulong screen_size_changed_id; gulong composited_changed_id; gboolean low_res; gint smallest_width; gint smallest_height; } CsScreen; typedef struct { GObjectClass parent_class; } CsScreenClass; GType cs_screen_get_type (void); CsScreen *cs_screen_new (gboolean debug); void cs_screen_get_monitor_geometry (CsScreen *screen, gint monitor, GdkRectangle *geometry); void cs_screen_get_screen_geometry (CsScreen *screen, GdkRectangle *geometry); gint cs_screen_get_primary_monitor (CsScreen *screen); gint cs_screen_get_n_monitors (CsScreen *screen); gint cs_screen_get_mouse_monitor (CsScreen *screen); gboolean cs_screen_get_low_res_mode (CsScreen *screen); void cs_screen_get_smallest_monitor_sizes (CsScreen *screen, gint *width, gint *height); void cs_screen_place_pointer_in_primary_monitor (CsScreen *screen); void cs_screen_set_net_wm_name (GdkWindow *window, const gchar *name); gchar *cs_screen_get_net_wm_name (gulong xwindow); void cs_screen_reset_screensaver (void); gint cs_screen_get_global_scale (void); G_END_DECLS #endif /* __CS_SCREEN_H */ cinnamon-screensaver-6.2.0/libcscreensaver/org.Cinnamon.xml0000664000175000017500000000066714632071776023035 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/cs-notification-watcher.c0000664000175000017500000002301114632071776024637 0ustar fabiofabio/* * CsNotificationWatcher: An introspectable C class that eavesdrops on the * session bus, watching for calls to our notification handler (usually Cinnamon.) * * Any notifications received are checked for transiency (and ignored if they are) * and passed on to be handled by Cinnamon. * * Any non-transient notifications cause a signal to be sent to our notification * widget, along with the sender name (in order to filter our mpris player notifications.) */ #include "cs-notification-watcher.h" enum { NOTIFICATION_RECEIVED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (CsNotificationWatcher, cs_notification_watcher, G_TYPE_OBJECT) #define DBUS_SERVICE "org.freedesktop.DBus" #define DBUS_PATH "/org/freedesktop/DBus" #define DBUS_INTERFACE "org.freedesktop.DBus" #define MATCH_RULE "type='method_call', interface='org.freedesktop.Notifications', member='Notify', eavesdrop=true" #define NOTIFICATIONS_INTERFACE "org.freedesktop.Notifications" #define NOTIFY_METHOD "Notify" static gboolean debug_mode = FALSE; typedef struct { CsNotificationWatcher *watcher; gchar *sender; } NotificationIdleData; static gboolean idle_notify_received (gpointer user_data) { NotificationIdleData *data = (NotificationIdleData *) user_data; g_return_val_if_fail (CS_IS_NOTIFICATION_WATCHER (data->watcher), FALSE); g_signal_emit (data->watcher, signals[NOTIFICATION_RECEIVED], 0, data->sender); g_clear_pointer (&data->sender, g_free); g_slice_free (NotificationIdleData, data); return FALSE; } static GDBusMessage * notification_filter_func (GDBusConnection *connection, GDBusMessage *message, gboolean incoming, gpointer user_data) { GDBusMessage *ret = NULL; gboolean transient = FALSE; gboolean replaces_existing = FALSE; gchar *sender_str = NULL; CsNotificationWatcher *watcher = CS_NOTIFICATION_WATCHER (user_data); if (incoming && g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL && g_strcmp0 (g_dbus_message_get_interface (message), NOTIFICATIONS_INTERFACE) == 0 && g_strcmp0 (g_dbus_message_get_member (message), NOTIFY_METHOD) == 0) { GVariant *body = g_dbus_message_get_body (message); if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE_TUPLE) && g_variant_n_children (body) >= 7) { GVariant *hints, *sender, *replaces_id; if (debug_mode) { GVariant *dbg_var = NULL; const gchar *dbg_str; g_printerr ("Notification received...\n"); dbg_var = g_variant_get_child_value (body, 0); if (dbg_var != NULL && g_variant_is_of_type (dbg_var, G_VARIANT_TYPE_STRING)) { dbg_str = g_variant_get_string (dbg_var, NULL); if (dbg_str != NULL) { g_printerr ("Sender: %s\n", dbg_str); } } g_clear_pointer (&dbg_var, g_variant_unref); dbg_var = g_variant_get_child_value (body, 3); if (dbg_var != NULL && g_variant_is_of_type (dbg_var, G_VARIANT_TYPE_STRING)) { dbg_str = g_variant_get_string (dbg_var, NULL); if (dbg_str != NULL) { g_printerr ("Summary: %s\n", dbg_str); } } g_clear_pointer (&dbg_var, g_variant_unref); dbg_var = g_variant_get_child_value (body, 4); if (dbg_var != NULL && g_variant_is_of_type (dbg_var, G_VARIANT_TYPE_STRING)) { dbg_str = g_variant_get_string (dbg_var, NULL); if (dbg_str != NULL) { g_printerr ("Body: %s\n", dbg_str); } } g_clear_pointer (&dbg_var, g_variant_unref); } hints = g_variant_get_child_value (body, 6); if (hints != NULL && g_variant_is_of_type (hints, G_VARIANT_TYPE_DICTIONARY)) { GVariant *transient_hint; transient_hint = g_variant_lookup_value (hints, "transient", G_VARIANT_TYPE_BOOLEAN); if (transient_hint) { transient = g_variant_get_boolean (transient_hint); g_variant_unref (transient_hint); if (debug_mode) { g_printerr ("notification has transient BOOLEAN hint: %s\n", transient ? "TRUE" : "FALSE"); } } else { transient_hint = g_variant_lookup_value (hints, "transient", G_VARIANT_TYPE_INT32); if (transient_hint) { transient = g_variant_get_int32 (transient_hint); if (debug_mode) { g_printerr ("notification has transient INT32 hint: %d, transient: %s\n", g_variant_get_int32 (transient_hint), transient ? "TRUE" : "FALSE"); } g_variant_unref (transient_hint); } } } g_clear_pointer (&hints, g_variant_unref); sender = g_variant_get_child_value (body, 0); if (sender) { sender_str = g_variant_dup_string (sender, NULL); } g_clear_pointer (&sender, g_variant_unref); replaces_id = g_variant_get_child_value (body, 1); if (replaces_id) { /* The replaces_id will be 0 if the notification is not replacing * another - if it's not zero, we can ignore it, so we don't accumulate * notifications from spammy programs (like dropbox can be) */ replaces_existing = g_variant_get_uint32 (replaces_id) > 0; } g_clear_pointer (&replaces_id, g_variant_unref); } } else { ret = message; } if (ret == NULL && !transient && !replaces_existing) { NotificationIdleData *data = g_slice_new0 (NotificationIdleData); data->watcher = watcher; data->sender = sender_str; g_idle_add (idle_notify_received, data); } return ret; } static void cs_notification_watcher_init (CsNotificationWatcher *watcher) { GError *error = NULL; GVariant *rulev; GVariant *result; watcher->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (!watcher->connection) { g_printerr ("CsNotificationWatcher: Could not connect to session bus - %s\n", error->message); g_clear_error (&error); return; } rulev = g_variant_new("(s)", MATCH_RULE); result = g_dbus_connection_call_sync (watcher->connection, DBUS_SERVICE, DBUS_PATH, DBUS_INTERFACE, "AddMatch", rulev, G_VARIANT_TYPE ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (!result) { g_printerr ("CsNotificationWatcher: Could not add match rule to bus - %s\n", error->message); g_clear_error (&error); return; } watcher->filter_id = g_dbus_connection_add_filter (watcher->connection, (GDBusMessageFilterFunction) notification_filter_func, watcher, NULL); } static void cs_notification_watcher_dispose (GObject *object) { CsNotificationWatcher *watcher; g_return_if_fail (object != NULL); g_return_if_fail (CS_IS_NOTIFICATION_WATCHER (object)); watcher = CS_NOTIFICATION_WATCHER (object); if (watcher->filter_id > 0) { g_dbus_connection_remove_filter (watcher->connection, watcher->filter_id); watcher->filter_id = 0; } g_clear_object (&watcher->connection); G_OBJECT_CLASS (cs_notification_watcher_parent_class)->dispose (object); } static void cs_notification_watcher_class_init (CsNotificationWatcherClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = cs_notification_watcher_dispose; signals [NOTIFICATION_RECEIVED] = g_signal_new ("notification-received", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (CsNotificationWatcherClass, notification_received), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 1, G_TYPE_STRING); } CsNotificationWatcher * cs_notification_watcher_new (gboolean debug) { GObject *result; debug_mode = debug; result = g_object_new (CS_TYPE_NOTIFICATION_WATCHER, NULL); return CS_NOTIFICATION_WATCHER (result); } cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.ConsoleKit.Session.xml0000664000175000017500000000500414632071776027267 0ustar fabiofabio Session objects represent and store information related to a user session. The properties associated with the Session specifically refer to the properties of the "session leader". This will cause a Lock signal to be emitted for this session. This method is restricted to privileged users by D-Bus policy. Lock signal This will cause an Unlock signal to be emitted for this session. This can be used by login managers to unlock a session before it is re-activated during fast-user-switching. This method is restricted to privileged users by D-Bus policy. Unlock signal TRUE if the session is active, otherwise FALSE Emitted when the active property has changed. Lock the desktop. Unlock the desktop. cinnamon-screensaver-6.2.0/libcscreensaver/org.mpris.MediaPlayer2.Player.xml0000664000175000017500000006667414632071776026147 0ustar fabiofabio

This interface implements the methods for querying and providing basic control over what is currently playing.

A track is currently playing.

A track is currently paused.

There is no track currently playing.

A playback state.

The playback will stop when there are no more tracks to play

The current track will start again from the begining once it has finished playing

The playback loops through a list of tracks

A repeat / loop status

Unique track identifier.

If the media player implements the TrackList interface and allows the same track to appear multiple times in the tracklist, this must be unique within the scope of the tracklist.

Note that this should be a valid D-Bus object id, although clients should not assume that any object is actually exported with any interfaces at that path.

Media players may not use any paths starting with /org/mpris unless explicitly allowed by this specification. Such paths are intended to have special meaning, such as /org/mpris/MediaPlayer2/TrackList/NoTrack to indicate "no track".

This is a D-Bus object id as that is the definitive way to have unique identifiers on D-Bus. It also allows for future optional expansions to the specification where tracks are exported to D-Bus with an interface similar to org.gnome.UPnP.MediaItem2.

A playback rate

This is a multiplier, so a value of 0.5 indicates that playback is happening at half speed, while 1.5 means that 1.5 seconds of "track time" is consumed every second.

Audio volume level

  • 0.0 means mute.
  • 1.0 is a sensible maximum volume level (ex: 0dB).

Note that the volume may be higher than 1.0, although generally clients should not attempt to set it above 1.0.

Time in microseconds.

Skips to the next track in the tracklist.

If there is no next track (and endless playback and track repeat are both off), stop playback.

If playback is paused or stopped, it remains that way.

If CanGoNext is false, attempting to call this method should have no effect.

Skips to the previous track in the tracklist.

If there is no previous track (and endless playback and track repeat are both off), stop playback.

If playback is paused or stopped, it remains that way.

If CanGoPrevious is false, attempting to call this method should have no effect.

Pauses playback.

If playback is already paused, this has no effect.

Calling Play after this should cause playback to start again from the same position.

If CanPause is false, attempting to call this method should have no effect.

Pauses playback.

If playback is already paused, resumes playback.

If playback is stopped, starts playback.

If CanPause is false, attempting to call this method should have no effect and raise an error.

Stops playback.

If playback is already stopped, this has no effect.

Calling Play after this should cause playback to start again from the beginning of the track.

If CanControl is false, attempting to call this method should have no effect and raise an error.

Starts or resumes playback.

If already playing, this has no effect.

If paused, playback resumes from the current position.

If there is no track to play, this has no effect.

If CanPlay is false, attempting to call this method should have no effect.

The number of microseconds to seek forward.

Seeks forward in the current track by the specified number of microseconds.

A negative value seeks back. If this would mean seeking back further than the start of the track, the position is set to 0.

If the value passed in would mean seeking beyond the end of the track, acts like a call to Next.

If the CanSeek property is false, this has no effect.

The currently playing track's identifier.

If this does not match the id of the currently-playing track, the call is ignored as "stale".

/org/mpris/MediaPlayer2/TrackList/NoTrack is not a valid value for this argument.

Track position in microseconds.

This must be between 0 and <track_length>.

Sets the current track position in microseconds.

If the Position argument is less than 0, do nothing.

If the Position argument is greater than the track length, do nothing.

If the CanSeek property is false, this has no effect.

The reason for having this method, rather than making Position writable, is to include the TrackId argument to avoid race conditions where a client tries to seek to a position when the track has already changed.

Uri of the track to load. Its uri scheme should be an element of the org.mpris.MediaPlayer2.SupportedUriSchemes property and the mime-type should match one of the elements of the org.mpris.MediaPlayer2.SupportedMimeTypes.

Opens the Uri given as an argument

If the playback is stopped, starts playing

If the uri scheme or the mime-type of the uri to open is not supported, this method does nothing and may raise an error. In particular, if the list of available uri schemes is empty, this method may not be implemented.

Clients should not assume that the Uri has been opened as soon as this method returns. They should wait until the mpris:trackid field in the Metadata property changes.

If the media player implements the TrackList interface, then the opened track should be made part of the tracklist, the org.mpris.MediaPlayer2.TrackList.TrackAdded or org.mpris.MediaPlayer2.TrackList.TrackListReplaced signal should be fired, as well as the org.freedesktop.DBus.Properties.PropertiesChanged signal on the tracklist interface.

The current playback status.

May be "Playing", "Paused" or "Stopped".

The current loop / repeat status

May be:

  • "None" if the playback will stop when there are no more tracks to play
  • "Track" if the current track will start again from the begining once it has finished playing
  • "Playlist" if the playback loops through a list of tracks

If CanControl is false, attempting to set this property should have no effect and raise an error.

The current playback rate.

The value must fall in the range described by MinimumRate and MaximumRate, and must not be 0.0. If playback is paused, the PlaybackStatus property should be used to indicate this. A value of 0.0 should not be set by the client. If it is, the media player should act as though Pause was called.

If the media player has no ability to play at speeds other than the normal playback rate, this must still be implemented, and must return 1.0. The MinimumRate and MaximumRate properties must also be set to 1.0.

Not all values may be accepted by the media player. It is left to media player implementations to decide how to deal with values they cannot use; they may either ignore them or pick a "best fit" value. Clients are recommended to only use sensible fractions or multiples of 1 (eg: 0.5, 0.25, 1.5, 2.0, etc).

This allows clients to display (reasonably) accurate progress bars without having to regularly query the media player for the current position.

A value of false indicates that playback is progressing linearly through a playlist, while true means playback is progressing through a playlist in some other order.

If CanControl is false, attempting to set this property should have no effect and raise an error.

The metadata of the current element.

If there is a current track, this must have a "mpris:trackid" entry (of D-Bus type "o") at the very least, which contains a D-Bus path that uniquely identifies this track.

See the type documentation for more details.

The volume level.

When setting, if a negative value is passed, the volume should be set to 0.0.

If CanControl is false, attempting to set this property should have no effect and raise an error.

The current track position in microseconds, between 0 and the 'mpris:length' metadata entry (see Metadata).

Note: If the media player allows it, the current playback position can be changed either the SetPosition method or the Seek method on this interface. If this is not the case, the CanSeek property is false, and setting this property has no effect and can raise an error.

If the playback progresses in a way that is inconstistant with the Rate property, the Seeked signal is emited.

The minimum value which the Rate property can take. Clients should not attempt to set the Rate property below this value.

Note that even if this value is 0.0 or negative, clients should not attempt to set the Rate property to 0.0.

This value should always be 1.0 or less.

The maximum value which the Rate property can take. Clients should not attempt to set the Rate property above this value.

This value should always be 1.0 or greater.

Whether the client can call the Next method on this interface and expect the current track to change.

If it is unknown whether a call to Next will be successful (for example, when streaming tracks), this property should be set to true.

If CanControl is false, this property should also be false.

Even when playback can generally be controlled, there may not always be a next track to move to.

Whether the client can call the Previous method on this interface and expect the current track to change.

If it is unknown whether a call to Previous will be successful (for example, when streaming tracks), this property should be set to true.

If CanControl is false, this property should also be false.

Even when playback can generally be controlled, there may not always be a next previous to move to.

Whether playback can be started using Play or PlayPause.

Note that this is related to whether there is a "current track": the value should not depend on whether the track is currently paused or playing. In fact, if a track is currently playing (and CanControl is true), this should be true.

If CanControl is false, this property should also be false.

Even when playback can generally be controlled, it may not be possible to enter a "playing" state, for example if there is no "current track".

Whether playback can be paused using Pause or PlayPause.

Note that this is an intrinsic property of the current track: its value should not depend on whether the track is currently paused or playing. In fact, if playback is currently paused (and CanControl is true), this should be true.

If CanControl is false, this property should also be false.

Not all media is pausable: it may not be possible to pause some streamed media, for example.

Whether the client can control the playback position using Seek and SetPosition. This may be different for different tracks.

If CanControl is false, this property should also be false.

Not all media is seekable: it may not be possible to seek when playing some streamed media, for example.

Whether the media player may be controlled over this interface.

This property is not expected to change, as it describes an intrinsic capability of the implementation.

If this is false, clients should assume that all properties on this interface are read-only (and will raise errors if writing to them is attempted), no methods are implemented and all other properties starting with "Can" are also false.

This allows clients to determine whether to present and enable controls to the user in advance of attempting to call methods and write to properties.

The new position, in microseconds.

Indicates that the track position has changed in a way that is inconsistant with the current playing state.

When this signal is not received, clients should assume that:

  • When playing, the position progresses according to the rate property.
  • When paused, it remains constant.

This signal does not need to be emitted when playback starts or when the track changes, unless the track is starting at an unexpected position. An expected position would be the last known one when going from Paused to Playing, and 0 when going from Stopped to Playing.

cinnamon-screensaver-6.2.0/libcscreensaver/subprocs.c0000664000175000017500000001040614632071776021757 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * subprocs.c --- choosing, spawning, and killing screenhacks. * * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski * Modified: Copyright (c) 2004 William Jon McCann * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include "config.h" #include #include #include #include #ifndef ESRCH # include #endif #include /* sys/resource.h needs this for timeval */ # include /* for waitpid() and associated macros */ #ifdef VMS # include # include /* for close */ # include /* for getpid */ # define pid_t int # define fork vfork #endif /* VMS */ #include /* for the signal names */ #include #include "subprocs.h" #if !defined(SIGCHLD) && defined(SIGCLD) # define SIGCHLD SIGCLD #endif /* Semaphore to temporarily turn the SIGCHLD handler into a no-op. Don't alter this directly -- use block_sigchld() / unblock_sigchld(). */ static int block_sigchld_handler = 0; #ifdef HAVE_SIGACTION sigset_t #else /* !HAVE_SIGACTION */ int #endif /* !HAVE_SIGACTION */ block_sigchld (void) { #ifdef HAVE_SIGACTION sigset_t child_set; sigemptyset (&child_set); sigaddset (&child_set, SIGCHLD); sigaddset (&child_set, SIGPIPE); sigprocmask (SIG_BLOCK, &child_set, 0); #endif /* HAVE_SIGACTION */ block_sigchld_handler++; #ifdef HAVE_SIGACTION return child_set; #else /* !HAVE_SIGACTION */ return 0; #endif /* !HAVE_SIGACTION */ } void unblock_sigchld (void) { #ifdef HAVE_SIGACTION sigset_t child_set; sigemptyset (&child_set); sigaddset (&child_set, SIGCHLD); sigaddset (&child_set, SIGPIPE); sigprocmask (SIG_UNBLOCK, &child_set, 0); #endif /* HAVE_SIGACTION */ block_sigchld_handler--; } int signal_pid (int pid, int signal) { int status = -1; gboolean verbose = TRUE; if (block_sigchld_handler) /* This function should not be called from the signal handler. */ abort(); block_sigchld (); /* we control the horizontal... */ status = kill (pid, signal); if (verbose && status < 0) { if (errno == ESRCH) g_message ("Child process %lu was already dead.", (unsigned long) pid); else { char buf [1024]; snprintf (buf, sizeof (buf), "Couldn't kill child process %lu", (unsigned long) pid); perror (buf); } } unblock_sigchld (); if (block_sigchld_handler < 0) abort (); return status; } #ifndef VMS void await_dying_children (int pid, gboolean debug) { while (1) { int wait_status = 0; pid_t kid; errno = 0; kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED); if (debug) { if (kid < 0 && errno) g_message ("waitpid(%d) ==> %ld (%d)", pid, (long) kid, errno); else if (kid != 0) g_message ("waitpid(%d) ==> %ld", pid, (long) kid); } /* 0 means no more children to reap. -1 means error -- except "interrupted system call" isn't a "real" error, so if we get that, we should just try again. */ if (kid < 0 && errno != EINTR) break; } } #else /* VMS */ static void await_dying_children (saver_info *si) { return; } #endif /* VMS */ cinnamon-screensaver-6.2.0/libcscreensaver/org.cinnamon.ScreenSaver.xml0000664000175000017500000000174314632071776025310 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/cs-gdk-event-filter.h0000664000175000017500000000311514632071776023675 0ustar fabiofabio #ifndef __CS_GDK_EVENT_FILTER_H #define __CS_GDK_EVENT_FILTER_H #include #include G_BEGIN_DECLS #define CS_TYPE_GDK_EVENT_FILTER (cs_gdk_event_filter_get_type ()) #define CS_GDK_EVENT_FILTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CS_TYPE_GDK_EVENT_FILTER, CsGdkEventFilter)) #define CS_GDK_EVENT_FILTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CS_TYPE_GDK_EVENT_FILTER, CsGdkEventFilterClass)) #define CS_IS_GDK_EVENT_FILTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CS_TYPE_GDK_EVENT_FILTER)) #define CS_IS_GDK_EVENT_FILTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CS_TYPE_GDK_EVENT_FILTER)) #define CS_GDK_EVENT_FILTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CS_TYPE_GDK_EVENT_FILTER, CsGdkEventFilterClass)) typedef struct { GObject obj; GdkDisplay *display; GtkWidget *managed_window; gulong my_xid; /* Using XID/Window here would complicate introspection. */ gulong pretty_xid; gboolean we_are_backup_window; int shape_event_base; } CsGdkEventFilter; typedef struct { GObjectClass parent_class; } CsGdkEventFilterClass; GType cs_gdk_event_filter_get_type (void); CsGdkEventFilter *cs_gdk_event_filter_new (GtkWidget *managed_window, gulong pretty_xid); void cs_gdk_event_filter_start (CsGdkEventFilter *filter, gboolean fractional_scaling, gboolean debug); void cs_gdk_event_filter_stop (CsGdkEventFilter *filter); G_END_DECLS #endif /* __CS_GDK_EVENT_FILTER_H */ cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.UPower.xml0000664000175000017500000000603314632071776025017 0ustar fabiofabio The UPower service is available via the system message bus. To access the service, use the org.freedesktop.UPower interface on the /org/freedesktop/UPower object on the D-Bus system bus service with the well-known name org.freedesktop.UPower. $ dbus-send --print-reply \ --system \ --dest=org.freedesktop.UPower \ /org/freedesktop/UPower \ org.freedesktop.UPower.EnumerateDevices method return sender=:1.386 -> dest=:1.451 reply_serial=2 array [ object path "/org/freedesktop/UPower/devices/line_power_AC" object path "/org/freedesktop/UPower/devices/battery_BAT0" ] An array of object paths for devices. Enumerate all power objects on the system. Object path of device that was added. Emitted when a device is added. Object path of device that was removed. Emitted when a device is removed. Indicates whether the system is running on battery power. This property is provided for convenience. cinnamon-screensaver-6.2.0/libcscreensaver/org.gnome.SessionManager.Presence.xml0000664000175000017500000000320414632071776027046 0ustar fabiofabio The status of the session. The status parameter must be one of the following: 0 Available 1 Invisible 2 Busy 3 Idle The new status value Indicates that the session status value has changed. cinnamon-screensaver-6.2.0/libcscreensaver/g-codegen.py0000664000175000017500000000165114632071776022157 0ustar fabiofabio#!/usr/bin/env python3 ''' FIXME This script is used only to call gdbus-codegen and simulate the generation of the source code and header as different targets. Both are generated implicitly, so meson is not able to know how many files are generated, so it does generate only one opaque target that represents the two files. originally from: https://gitlab.gnome.org/GNOME/gnome-settings-daemon/commit/5924d72931a030b24554116a48140a661a99652b Please see: https://bugzilla.gnome.org/show_bug.cgi?id=791015 https://github.com/mesonbuild/meson/pull/2930 https://github.com/linuxmint/xapps/commit/539ed7377cff41643608cf61ed1a8430a367e283#diff-5800aaf205d98232d967a359f4657a06R4 ''' import subprocess import sys import os subprocess.call([ 'gdbus-codegen', '--interface-prefix=' + sys.argv[1], '--generate-c-code=' + os.path.join(sys.argv[4], sys.argv[2]), '--c-namespace=Cs'] + sys.argv[3].split() + [sys.argv[5]] ) cinnamon-screensaver-6.2.0/libcscreensaver/cs-gdk-event-filter-x11.c0000664000175000017500000002672414632071776024312 0ustar fabiofabio/* * CsGdkEventFilter: An introspectable C class that establishes an event * trap for the screensaver. It watches for any X events that could result * in other windows showing up over our Stage, and ensures the Stage stays on * top. This will only ever be other override-redirect (non-managed) X windows, * such as native Firefox or Chrome notification popups. * */ #include "config.h" #include "cs-gdk-event-filter.h" #include "cs-screen.h" #ifdef HAVE_SHAPE_EXT #include #endif #include #include #include enum { XSCREEN_SIZE, SCREENSAVER_WINDOW_CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (CsGdkEventFilter, cs_gdk_event_filter, G_TYPE_OBJECT) static gboolean ignore_fcitx_input_window (CsGdkEventFilter *filter, Window xid) { XClassHint *clh; Display *xdpy; gboolean ret = FALSE; int status; gdk_x11_display_error_trap_push (filter->display); xdpy = GDK_DISPLAY_XDISPLAY (filter->display); clh = XAllocClassHint(); status = XGetClassHint (xdpy, xid, clh); if (status) { if (g_strcmp0 (clh->res_name, "fcitx") == 0) { ret = TRUE; } g_clear_pointer (&clh->res_name, XFree); g_clear_pointer (&clh->res_class, XFree); } XFree (clh); if (!ret) { return FALSE; } ret = FALSE; XTextProperty text; status = XGetWMName(xdpy, xid, &text); if (status) { gint count; gchar **list; status = XmbTextPropertyToTextList (xdpy, &text, &list, &count); if (status == Success && count > 0) { gint i; for (i = 0; i < count; i++) { if (g_strcmp0 (list[i], "Fcitx Input Window") == 0) { ret = TRUE; break; } } XFreeStringList (list); XFree (text.value); } } gdk_x11_display_error_trap_pop_ignored (filter->display); return ret; } static void unshape_window (CsGdkEventFilter *filter) { g_return_if_fail (CS_IS_GDK_EVENT_FILTER (filter)); gdk_window_shape_combine_region (gtk_widget_get_window (GTK_WIDGET (filter->managed_window)), NULL, 0, 0); } static void restack (CsGdkEventFilter *filter, Window event_window, const gchar *event_type) { g_autofree gchar *net_wm_name = NULL; gdk_x11_display_error_trap_push (filter->display); net_wm_name = cs_screen_get_net_wm_name (event_window); if (g_strcmp0 (net_wm_name, "event-grabber-window") == 0) { g_debug ("(Ignoring %s from CsEventGrabber window)", event_type); gdk_x11_display_error_trap_pop_ignored (filter->display); return; } // Screensaver windows get re-made but we want to pick up new ones // so the backup locker can stay in place always. if (filter->pretty_xid != event_window) { if (g_strcmp0 (net_wm_name, "cinnamon-screensaver-window") == 0) { g_debug ("New screensaver window found: 0x%lx (replaces 0x%lx)", event_window, filter->pretty_xid); filter->pretty_xid = event_window; g_signal_emit (filter, signals[SCREENSAVER_WINDOW_CHANGED], 0, event_window); } } if (filter->we_are_backup_window) { if (event_window != filter->pretty_xid) { g_debug ("BackupWindow received %s from window '%s' (0x%lx), raising ourselves.", event_type, net_wm_name, event_window); XRaiseWindow(GDK_DISPLAY_XDISPLAY (filter->display), filter->my_xid); } } else { g_debug ("Screensaver received %s from window '%s' (0x%lx), raising ourselves.", event_type, net_wm_name, event_window); XRaiseWindow(GDK_DISPLAY_XDISPLAY (filter->display), filter->my_xid); } XFlush (GDK_DISPLAY_XDISPLAY (filter->display)); gdk_x11_display_error_trap_pop_ignored (filter->display); } static GdkFilterReturn cs_gdk_event_filter_xevent (CsGdkEventFilter *filter, GdkXEvent *xevent) { XEvent *ev; ev = xevent; /* MapNotify is used to tell us when new windows are mapped. ConfigureNofify is used to tell us when windows are raised. */ switch (ev->xany.type) { case MapNotify: { XMapEvent *xme = &ev->xmap; if (ignore_fcitx_input_window (filter, xme->window) && filter->we_are_backup_window) { g_debug ("Ignoring MapNotify for fcitx window (we're the backup-locker)."); break; } // Ignore my own events. if (xme->window == filter->my_xid) { break; } restack (filter, xme->window, "MapNotify"); break; } case ConfigureNotify: { XConfigureEvent *xce = &ev->xconfigure; // If the reported window is the root window, and we're the backup window (we have a pretty // xid) then signal to resize to the root window (screen). if (xce->window == GDK_ROOT_WINDOW ()) { // Screen size may have changed, tell the fallback g_debug ("ConfigureNotify from root window (0x%lx), screen size may have changed. %s", xce->window, filter->we_are_backup_window ? "(we're the backup-locker)" : ""); // The screensaver doesn't need to know, it will get notified by CsScreen. if (filter->we_are_backup_window) { g_signal_emit (filter, signals[XSCREEN_SIZE], 0); } break; } if (ignore_fcitx_input_window (filter, xce->window) && filter->we_are_backup_window) { g_debug ("Ignoring ConfigureNotify for fcitx window (we're the backup-locker)."); break; } // Ignore my own events if (xce->window == filter->my_xid) { break; } restack (filter, xce->window, "ConfigureNotify"); break; } default: { #ifdef HAVE_SHAPE_EXT if (ev->xany.type == (filter->shape_event_base + ShapeNotify)) { g_debug ("ShapeNotify event. %s", filter->we_are_backup_window ? "(we're the backup-locker)" : ""); unshape_window (filter); } #endif } } return GDK_FILTER_CONTINUE; } static void select_popup_events (CsGdkEventFilter *filter) { XWindowAttributes attr; unsigned long events; gdk_x11_display_error_trap_push (filter->display); memset (&attr, 0, sizeof (attr)); XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (filter->display), GDK_ROOT_WINDOW (), &attr); events = SubstructureNotifyMask | attr.your_event_mask; XSelectInput (GDK_DISPLAY_XDISPLAY (filter->display), GDK_ROOT_WINDOW (), events); gdk_x11_display_error_trap_pop_ignored (filter->display); } static void select_shape_events (CsGdkEventFilter *filter) { #ifdef HAVE_SHAPE_EXT unsigned long events; int shape_error_base; gdk_x11_display_error_trap_push (filter->display); if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (filter->display), &filter->shape_event_base, &shape_error_base)) { events = ShapeNotifyMask; XShapeSelectInput (GDK_DISPLAY_XDISPLAY (filter->display), GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (filter->managed_window))), events); } gdk_x11_display_error_trap_pop_ignored (filter->display); #endif } static GdkFilterReturn xevent_filter (GdkXEvent *xevent, GdkEvent *event, CsGdkEventFilter *filter) { return cs_gdk_event_filter_xevent (filter, xevent); } static void cs_gdk_event_filter_init (CsGdkEventFilter *filter) { filter->shape_event_base = 0; filter->managed_window = NULL; filter->pretty_xid = 0; filter->my_xid = 0; } static void cs_gdk_event_filter_finalize (GObject *object) { CsGdkEventFilter *filter; g_return_if_fail (object != NULL); g_return_if_fail (CS_IS_GDK_EVENT_FILTER (object)); filter = CS_GDK_EVENT_FILTER (object); cs_gdk_event_filter_stop (filter); g_object_unref (filter->managed_window); G_OBJECT_CLASS (cs_gdk_event_filter_parent_class)->finalize (object); } static void cs_gdk_event_filter_class_init (CsGdkEventFilterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cs_gdk_event_filter_finalize; signals[XSCREEN_SIZE] = g_signal_new ("xscreen-size", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[SCREENSAVER_WINDOW_CHANGED] = g_signal_new ("screensaver-window-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ULONG); } static void muted_log_handler (const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer data) { /* Intentionally empty to discard message */ } void cs_gdk_event_filter_start (CsGdkEventFilter *filter, gboolean fractional_scaling, gboolean debug) { select_popup_events (filter); select_shape_events (filter); if (debug) { g_log_set_handler ("Cvc", G_LOG_LEVEL_DEBUG, muted_log_handler, NULL); g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); } filter->my_xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (filter->managed_window))); g_debug ("Starting event filter for %s - 0x%lx", filter->we_are_backup_window ? "backup-locker." : "screensaver.", filter->my_xid); gdk_window_add_filter (NULL, (GdkFilterFunc) xevent_filter, filter); if (filter->we_are_backup_window) { restack (filter, filter->pretty_xid, NULL); } else { restack (filter, 0, NULL); } } void cs_gdk_event_filter_stop (CsGdkEventFilter *filter) { gdk_window_remove_filter (NULL, (GdkFilterFunc) xevent_filter, filter); } CsGdkEventFilter * cs_gdk_event_filter_new (GtkWidget *managed_window, gulong pretty_xid) { CsGdkEventFilter *filter; filter = g_object_new (CS_TYPE_GDK_EVENT_FILTER, NULL); filter->display = gdk_display_get_default (); filter->managed_window = g_object_ref (managed_window); filter->pretty_xid = pretty_xid; filter->we_are_backup_window = filter->pretty_xid != 0; return filter; } cinnamon-screensaver-6.2.0/libcscreensaver/cs-screen-x11.c0000664000175000017500000006464614632071776022427 0ustar fabiofabio/* * CsScreen: An introspectable C class that establishes an event * trap for the screensaver. It watches for any X events that could result * in other windows showing up over our Stage, and ensures the Stage stays on * top. This will only ever be other override-redirect (non-managed) X windows, * such as native Firefox or Chrome notification popups. * */ #include "config.h" #include "cs-screen.h" #include #include #include #ifdef HAVE_SOLARIS_XINERAMA #include #endif #ifdef HAVE_XFREE_XINERAMA #include #endif #ifdef HAVE_RANDR #include #endif enum { SCREEN_MONITORS_CHANGED, SCREEN_SIZE_CHANGED, COMPOSITED_CHANGED, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (CsScreen, cs_screen, G_TYPE_OBJECT) static gboolean debug_mode = FALSE; #define DEBUG(...) if (debug_mode) g_printerr (__VA_ARGS__) #define cs_XFree(p) do { if ((p)) XFree ((p)); } while (0) #define PRIMARY_MONITOR 0 static gboolean cs_rectangle_equal (const GdkRectangle *src1, const GdkRectangle *src2) { return ((src1->x == src2->x) && (src1->y == src2->y) && (src1->width == src2->width) && (src1->height == src2->height)); } /* The list of monitors reported by the windowing system might include * mirrored monitors with identical bounds. Since mirrored monitors * shouldn't be treated as separate monitors for most purposes, we * filter them out here. (We ignore the possibility of partially * overlapping monitors because they are rare and it's hard to come * up with any sensible interpretation.) */ static void filter_mirrored_monitors (CsScreen *screen) { int i, j; /* Currently always true and simplifies things */ g_assert (screen->primary_monitor_index == 0); for (i = 1; i < screen->n_monitor_infos; i++) { /* In case we've filtered previous monitors */ screen->monitor_infos[i].number = i; for (j = 0; j < i; j++) { if (cs_rectangle_equal (&screen->monitor_infos[i].rect, &screen->monitor_infos[j].rect)) { memmove (&screen->monitor_infos[i], &screen->monitor_infos[i + 1], (screen->n_monitor_infos - i - 1) * sizeof (CsMonitorInfo)); screen->n_monitor_infos--; i--; continue; } } } } #ifdef HAVE_RANDR static CsMonitorInfo * find_monitor_with_rect (CsScreen *screen, int x, int y, int w, int h) { CsMonitorInfo *info; int i; for (i = 0; i < screen->n_monitor_infos; i++) { info = &screen->monitor_infos[i]; if (x == info->rect.x && y == info->rect.y && w == info->rect.width && h == info->rect.height) { return info; } } return NULL; } /* In the case of multiple outputs of a single crtc (mirroring), we consider one of the * outputs the "main". This is the one we consider "owning" the windows, so if * the mirroring is changed to a dual monitor setup then the windows are moved to the * crtc that now has that main output. If one of the outputs is the primary that is * always the main, otherwise we just use the first. */ static XID find_main_output_for_crtc (XRRScreenResources *resources, XRRCrtcInfo *crtc, Display *xdisplay, XID xroot) { XRROutputInfo *output; RROutput primary_output; int i; XID res; primary_output = XRRGetOutputPrimary (xdisplay, xroot); res = None; for (i = 0; i < crtc->noutput; i++) { output = XRRGetOutputInfo (xdisplay, resources, crtc->outputs[i]); if (output->connection != RR_Disconnected && (res == None || crtc->outputs[i] == primary_output)) { res = crtc->outputs[i]; } XRRFreeOutputInfo (output); } return res; } #endif static void apply_scale_factor (CsMonitorInfo *infos, gint n_infos, gint factor) { gint i; for (i = 0; i < n_infos; i++) { infos[i].rect.x /= factor; infos[i].rect.y /= factor; infos[i].rect.width /= factor; infos[i].rect.height /= factor; DEBUG ("Scale factor of %d applied. Monitor %d is %d,%d %d x %d\n", factor, infos[i].number, infos[i].rect.x, infos[i].rect.y, infos[i].rect.width, infos[i].rect.height); } } #define MONITOR_WIDTH_THRESHOLD 1200 #define MONITOR_HEIGHT_THRESHOLD 1000 static gboolean get_low_res_mode (CsScreen *screen, CsMonitorInfo *infos, gint n_infos) { gint i; gint smallest_width, smallest_height; smallest_width = smallest_height = G_MAXINT; for (i = 0; i < n_infos; i++) { smallest_width = MIN (infos[i].rect.width, smallest_width); smallest_height = MIN (infos[i].rect.height, smallest_height); } screen->smallest_width = smallest_width; screen->smallest_height = smallest_height; if (smallest_width < MONITOR_WIDTH_THRESHOLD || smallest_height < MONITOR_HEIGHT_THRESHOLD) { DEBUG ("Narrowest monitor width after scaling (%dx%d) is below threshold of %dx%d, applying low-res mode\n", smallest_width, smallest_height, MONITOR_WIDTH_THRESHOLD, MONITOR_HEIGHT_THRESHOLD); return TRUE; } return FALSE; } static void reload_monitor_infos (CsScreen *screen) { GdkDisplay *gdk_display; Display *xdisplay; Window xroot; gdk_display = gdk_screen_get_display (screen->gdk_screen); xdisplay = gdk_x11_display_get_xdisplay (gdk_display); xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen->gdk_screen)); /* Any previous screen->monitor_infos is freed by the caller */ screen->monitor_infos = NULL; screen->n_monitor_infos = 0; /* Xinerama doesn't have a concept of primary monitor, however XRandR * does. However, the XRandR xinerama compat code always sorts the * primary output first, so we rely on that here. We could use the * native XRandR calls instead of xinerama, but that would be * slightly problematic for _NET_WM_FULLSCREEN_MONITORS support, as * that is defined in terms of xinerama monitor indexes. * So, since we don't need anything in xrandr except the primary * we can keep using xinerama and use the first monitor as the * primary. */ screen->primary_monitor_index = PRIMARY_MONITOR; #ifdef HAVE_XFREE_XINERAMA if (screen->n_monitor_infos == 0 && XineramaIsActive (xdisplay)) { XineramaScreenInfo *infos; int n_infos; int i; n_infos = 0; infos = XineramaQueryScreens (xdisplay, &n_infos); DEBUG ("Found %d Xinerama screens on display %s\n", n_infos, gdk_display_get_name (gdk_display)); if (n_infos > 0) { screen->monitor_infos = g_new0 (CsMonitorInfo, n_infos); screen->n_monitor_infos = n_infos; i = 0; while (i < n_infos) { screen->monitor_infos[i].number = infos[i].screen_number; screen->monitor_infos[i].rect.x = infos[i].x_org; screen->monitor_infos[i].rect.y = infos[i].y_org; screen->monitor_infos[i].rect.width = infos[i].width; screen->monitor_infos[i].rect.height = infos[i].height; DEBUG ("Monitor %d is %d,%d %d x %d\n", screen->monitor_infos[i].number, screen->monitor_infos[i].rect.x, screen->monitor_infos[i].rect.y, screen->monitor_infos[i].rect.width, screen->monitor_infos[i].rect.height); ++i; } } cs_XFree (infos); #ifdef HAVE_RANDR { XRRScreenResources *resources; resources = XRRGetScreenResourcesCurrent (xdisplay, xroot); if (resources) { for (i = 0; i < resources->ncrtc; i++) { XRRCrtcInfo *crtc; CsMonitorInfo *info; crtc = XRRGetCrtcInfo (xdisplay, resources, resources->crtcs[i]); info = find_monitor_with_rect (screen, crtc->x, crtc->y, (int)crtc->width, (int)crtc->height); if (info) { info->output = find_main_output_for_crtc (resources, crtc, xdisplay, xroot); } XRRFreeCrtcInfo (crtc); } XRRFreeScreenResources (resources); } } #endif } else if (screen->n_monitor_infos > 0) { DEBUG ("No XFree86 Xinerama extension or XFree86 Xinerama inactive on display %s\n", gdk_display_get_name (gdk_display)); } #else DEBUG ("Muffin compiled without XFree86 Xinerama support\n"); #endif /* HAVE_XFREE_XINERAMA */ #ifdef HAVE_SOLARIS_XINERAMA /* This code from GDK, Copyright (C) 2002 Sun Microsystems */ if (screen->n_monitor_infos == 0 && XineramaGetState (xdisplay, gdk_screen_get_number (screen->gdk_screen))) { XRectangle monitors[MAXFRAMEBUFFERS]; unsigned char hints[16]; int result; int n_monitors; int i; n_monitors = 0; result = XineramaGetInfo (xdisplay, gdk_screen_get_number (screen->gdk_screen), monitors, hints, &n_monitors); /* Yes I know it should be Success but the current implementation * returns the num of monitor */ if (result > 0) { g_assert (n_monitors > 0); screen->monitor_infos = g_new0 (CsMonitorInfo, n_monitors); screen->n_monitor_infos = n_monitors; i = 0; while (i < n_monitors) { screen->monitor_infos[i].number = i; screen->monitor_infos[i].rect.x = monitors[i].x; screen->monitor_infos[i].rect.y = monitors[i].y; screen->monitor_infos[i].rect.width = monitors[i].width; screen->monitor_infos[i].rect.height = monitors[i].height; DEBUG ("Monitor %d is %d,%d %d x %d\n", screen->monitor_infos[i].number, screen->monitor_infos[i].rect.x, screen->monitor_infos[i].rect.y, screen->monitor_infos[i].rect.width, screen->monitor_infos[i].rect.height); ++i; } } } else if (screen->n_monitor_infos == 0) { DEBUG ("No Solaris Xinerama extension or Solaris Xinerama inactive on display %s\n", gdk_display_get_name (gdk_display)); } #else DEBUG ("Cinnamon Screensaver compiled without Solaris Xinerama support\n"); #endif /* HAVE_SOLARIS_XINERAMA */ /* If no Xinerama, fill in the single screen info so * we can use the field unconditionally */ if (screen->n_monitor_infos == 0) { DEBUG ("No Xinerama screens, using default screen info\n"); screen->monitor_infos = g_new0 (CsMonitorInfo, 1); screen->n_monitor_infos = 1; screen->monitor_infos[0].number = 0; screen->monitor_infos[0].rect = screen->rect; } filter_mirrored_monitors (screen); screen->monitor_infos[screen->primary_monitor_index].is_primary = TRUE; apply_scale_factor (screen->monitor_infos, screen->n_monitor_infos, gdk_screen_get_monitor_scale_factor (screen->gdk_screen, PRIMARY_MONITOR)); screen->low_res = get_low_res_mode (screen, screen->monitor_infos, screen->n_monitor_infos); g_assert (screen->n_monitor_infos > 0); g_assert (screen->monitor_infos != NULL); } static void reload_screen_info (CsScreen *screen) { screen->rect.x = screen->rect.y = 0; screen->rect.width = gdk_screen_get_width (screen->gdk_screen); screen->rect.height = gdk_screen_get_height (screen->gdk_screen); } static gboolean is_full_change (CsScreen *screen) { // Check to see if the union of monitor rects is the same size as the screen GdkRectangle total_monitors; gint i; gboolean same; for (i = 0; i < screen->n_monitor_infos; i++) { CsMonitorInfo info = screen->monitor_infos[i]; gdk_rectangle_union (&total_monitors, &info.rect, &total_monitors); } same = gdk_rectangle_equal (&total_monitors, &screen->rect); g_printerr ("Screen rect (%d,%d-%dx%d) and %d monitor rects (%d,%d-%dx%d) %s\n", screen->rect.x, screen->rect.y, screen->rect.width, screen->rect.height, screen->n_monitor_infos, total_monitors.x, total_monitors.y, total_monitors.width, total_monitors.height, same ? "add up, sending change notification" : "DO NOT add up, skipping change notification"); return same; } static void on_monitors_changed (GdkScreen *gdk_screen, gpointer user_data) { CsScreen *screen; CsMonitorInfo *old_monitor_infos; screen = CS_SCREEN (user_data); DEBUG ("CsScreen received 'monitors-changed' signal from GdkScreen %ld\n", g_get_monotonic_time () / 1000); gdk_flush (); old_monitor_infos = screen->monitor_infos; reload_monitor_infos (screen); g_free (old_monitor_infos); reload_screen_info (screen); if (is_full_change (screen)) { g_signal_emit (screen, signals[SCREEN_MONITORS_CHANGED], 0); } } static void on_screen_changed (GdkScreen *gdk_screen, gpointer user_data) { CsScreen *screen; CsMonitorInfo *old_monitor_infos; screen = CS_SCREEN (user_data); DEBUG ("CsScreen received 'size-changed' signal from GdkScreen %ld\n", g_get_monotonic_time () / 1000); gdk_flush (); old_monitor_infos = screen->monitor_infos; reload_monitor_infos (screen); g_free (old_monitor_infos); reload_screen_info (screen); if (is_full_change (screen)) { g_signal_emit (screen, signals[SCREEN_SIZE_CHANGED], 0); } } static void on_composited_changed (GdkScreen *gdk_screen, gpointer user_data) { CsScreen *screen; screen = CS_SCREEN (user_data); DEBUG ("CsScreen received 'composited-changed' signal from GdkScreen\n"); g_signal_emit (screen, signals[COMPOSITED_CHANGED], 0); } static void cs_screen_init (CsScreen *screen) { screen->gdk_screen = gdk_screen_get_default (); screen->monitors_changed_id = g_signal_connect (screen->gdk_screen, "monitors-changed", G_CALLBACK (on_monitors_changed), screen); screen->screen_size_changed_id = g_signal_connect (screen->gdk_screen, "size-changed", G_CALLBACK (on_screen_changed), screen); screen->composited_changed_id = g_signal_connect (screen->gdk_screen, "composited-changed", G_CALLBACK (on_composited_changed), screen); reload_screen_info (screen); reload_monitor_infos (screen); } static void cs_screen_finalize (GObject *object) { CsScreen *screen; g_return_if_fail (object != NULL); g_return_if_fail (CS_IS_SCREEN (object)); screen = CS_SCREEN (object); if (screen->monitor_infos) { g_free (screen->monitor_infos); } DEBUG ("CsScreen finalize\n"); G_OBJECT_CLASS (cs_screen_parent_class)->finalize (object); } static void cs_screen_dispose (GObject *object) { CsScreen *screen; g_return_if_fail (object != NULL); g_return_if_fail (CS_IS_SCREEN (object)); screen = CS_SCREEN (object); if (screen->monitors_changed_id > 0) { g_signal_handler_disconnect (screen->gdk_screen, screen->monitors_changed_id); screen->monitors_changed_id = 0; } if (screen->screen_size_changed_id > 0) { g_signal_handler_disconnect (screen->gdk_screen, screen->screen_size_changed_id); screen->screen_size_changed_id = 0; } if (screen->composited_changed_id > 0) { g_signal_handler_disconnect (screen->gdk_screen, screen->composited_changed_id); screen->composited_changed_id = 0; } DEBUG ("CsScreen dispose\n"); G_OBJECT_CLASS (cs_screen_parent_class)->dispose (object); } static void cs_screen_class_init (CsScreenClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cs_screen_finalize; object_class->dispose = cs_screen_dispose; signals[SCREEN_MONITORS_CHANGED] = g_signal_new ("monitors-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[SCREEN_SIZE_CHANGED] = g_signal_new ("size-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[COMPOSITED_CHANGED] = g_signal_new ("composited-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } CsScreen * cs_screen_new (gboolean debug) { GObject *result; debug_mode = debug; result = g_object_new (CS_TYPE_SCREEN, NULL); return CS_SCREEN (result); } /** * cs_screen_get_monitor_geometry: * @screen: a #CsScreen * @monitor: the monitor number * @geometry: (out): location to store the monitor geometry * * Stores the location and size of the indicated monitor in @geometry. */ void cs_screen_get_monitor_geometry (CsScreen *screen, gint monitor, GdkRectangle *geometry) { g_return_if_fail (CS_IS_SCREEN (screen)); g_return_if_fail (monitor >= 0 && monitor < screen->n_monitor_infos); g_return_if_fail (geometry != NULL); geometry->x = screen->monitor_infos[monitor].rect.x; geometry->y = screen->monitor_infos[monitor].rect.y; geometry->width = screen->monitor_infos[monitor].rect.width; geometry->height = screen->monitor_infos[monitor].rect.height; } /** * cs_screen_get_screen_geometry: * @screen: a #CsScreen * @geometry: (out): location to store the screen geometry * * Stores the location and size of the screen in @geometry. */ void cs_screen_get_screen_geometry (CsScreen *screen, GdkRectangle *geometry) { g_return_if_fail (CS_IS_SCREEN (screen)); g_return_if_fail (geometry != NULL); geometry->x = screen->rect.x; geometry->y = screen->rect.y; geometry->width = screen->rect.width; geometry->height = screen->rect.height; } /** * cs_screen_get_primary_monitor: * @screen: a #CsScreen * * Gets the index of the primary monitor on this @screen. * * Return value: a monitor index */ gint cs_screen_get_primary_monitor (CsScreen *screen) { g_return_val_if_fail (CS_IS_SCREEN (screen), 0); return screen->primary_monitor_index; } /** * cs_screen_get_n_monitors: * @screen: a #CsScreen * * Gets the number of monitors that are joined together to form @screen. * * Return value: the number of monitors */ gint cs_screen_get_n_monitors (CsScreen *screen) { g_return_val_if_fail (CS_IS_SCREEN (screen), 0); return screen->n_monitor_infos; } /** * cs_screen_get_mouse_monitor: * @screen: a #CsScreen * * Gets the index of the monitor that the mouse pointer currently * occupies. * * Return value: the monitor index for the pointer */ gint cs_screen_get_mouse_monitor (CsScreen *screen) { GdkDisplay *gdk_display; Window xroot, root_return, child_return; int root_x_return, root_y_return; int win_x_return, win_y_return; unsigned int mask_return; gint scale_factor; gint i; gint ret = 0; g_return_val_if_fail (CS_IS_SCREEN (screen), 0); gdk_display = gdk_screen_get_display (screen->gdk_screen); xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen->gdk_screen)); gdk_error_trap_push (); XQueryPointer (gdk_x11_display_get_xdisplay (gdk_display), xroot, &root_return, &child_return, &root_x_return, &root_y_return, &win_x_return, &win_y_return, &mask_return); gdk_error_trap_pop_ignored (); scale_factor = gdk_screen_get_monitor_scale_factor (screen->gdk_screen, 0); root_x_return /= scale_factor; root_y_return /= scale_factor; for (i = 0; i < screen->n_monitor_infos; i++) { GdkRectangle iter = screen->monitor_infos[i].rect; if (root_x_return >= iter.x && root_x_return <= iter.x + iter.width && root_y_return >= iter.y && root_y_return <= iter.y + iter.height) { ret = i; break; } } return ret; } /** * cs_screen_get_low_res_mode: * @screen: a #CsScreen * * Gets whether or not one of our monitors falls below the low res threshold (1200 wide). * This lets us display certain things at smaller sizes to prevent truncating of images, etc. * * Returns: Whether or not to use low res mode. */ gboolean cs_screen_get_low_res_mode (CsScreen *screen) { g_return_val_if_fail (CS_IS_SCREEN (screen), FALSE); return screen->low_res; } /** * cs_screen_get_smallest_monitor_sizes: * @screen: a #CsScreen * @width: (out): width of the smallest monitor * @height: (out): height of the smallest monitor * * Gets whether or not one of our monitors falls below the low res threshold (1200 wide). * This lets us display certain things at smaller sizes to prevent truncating of images, etc. */ void cs_screen_get_smallest_monitor_sizes (CsScreen *screen, gint *width, gint *height) { g_return_if_fail (CS_IS_SCREEN (screen)); if (width != NULL) { *width = screen->smallest_width; } if (height != NULL) { *height = screen->smallest_height; } } /** * cs_screen_center_pointer_in_primary_monitor: * @screen: The #CsScreen * * Warps the mouse pointer to the center in x, and half again below center * in y, of the primary monitor. This is used during waking to have the * unlock dialog appear on the primary monitor (at least, initially). */ void cs_screen_place_pointer_in_primary_monitor (CsScreen *screen) { GdkDisplay *display; GdkRectangle rect; GdkSeat *seat; GdkDevice *pointer; g_return_if_fail (CS_IS_SCREEN (screen)); cs_screen_get_monitor_geometry (screen, screen->primary_monitor_index, &rect); display = gdk_screen_get_display (screen->gdk_screen); seat = gdk_display_get_default_seat (display); pointer = gdk_seat_get_pointer (seat); gdk_device_warp (pointer, screen->gdk_screen, rect.x + (rect.width * .5), rect.y + (rect.height * .75)); } /** * cs_screen_set_net_wm_name: * @window: The #GdkWindow to set the property on * @name: The name. * * Sets _NET_WM_NAME to name on window */ void cs_screen_set_net_wm_name (GdkWindow *window, const gchar *name) { GdkDisplay *display = gdk_display_get_default (); Window xwindow = gdk_x11_window_get_xid (window); XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"), gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, PropModeReplace, (guchar *)name, strlen (name)); XFlush(GDK_DISPLAY_XDISPLAY (display)); } /** * cs_screen_get_net_wm_name: * @xwindow: The Window (XID) to get the property from. * * Gets the NET_WM_NAME of xwindow * * returns: (transfer full): The value of NET_WM_NAME. */ gchar * cs_screen_get_net_wm_name (gulong xwindow) { GdkDisplay *display = gdk_display_get_default (); Atom net_wm_name_atom; Atom type; int format; unsigned long nitems, after; unsigned char *data = NULL; gchar *name = NULL; net_wm_name_atom = XInternAtom(GDK_DISPLAY_XDISPLAY (display), "_NET_WM_NAME", False); XGetWindowProperty(GDK_DISPLAY_XDISPLAY (display), xwindow, net_wm_name_atom, 0, 256, False, AnyPropertyType, &type, &format, &nitems, &after, &data); if (data) { name = g_strdup((char *) data); XFree(data); } return name; } /** * cs_screen_reset_screensaver: * * Resets the screensaver idle timer. If called when the screensaver is active * it will stop it. * */ void cs_screen_reset_screensaver (void) { XResetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); } /** * cs_screen_get_global_scale: * * Retrieves the global scale factor from the GdkScreen. * */ gint cs_screen_get_global_scale (void) { GdkScreen *gdkscreen; GValue value = G_VALUE_INIT; gint window_scale = 1; gdkscreen = gdk_screen_get_default (); g_value_init (&value, G_TYPE_INT); if (gdk_screen_get_setting (gdkscreen, "gdk-window-scaling-factor", &value)) { window_scale = g_value_get_int (&value); } return window_scale; }cinnamon-screensaver-6.2.0/libcscreensaver/org.cinnamon.Muffin.DisplayConfig.xml0000664000175000017500000000145114632071776027042 0ustar fabiofabio cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.Accounts.xml0000664000175000017500000000210114632071776025345 0ustar fabiofabio The username to look up Object path of user Finds a user by its username. if no user with the given username exists cinnamon-screensaver-6.2.0/libcscreensaver/cs-auth.h0000664000175000017500000000414214632071776021470 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2006 William Jon McCann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * */ #ifndef __CS_AUTH_H #define __CS_AUTH_H #include G_BEGIN_DECLS typedef enum { CS_AUTH_MESSAGE_PROMPT_ECHO_ON, CS_AUTH_MESSAGE_PROMPT_ECHO_OFF, CS_AUTH_MESSAGE_ERROR_MSG, CS_AUTH_MESSAGE_TEXT_INFO } CsAuthMessageStyle; typedef enum { CS_AUTH_ERROR_GENERAL, CS_AUTH_ERROR_AUTH_ERROR, CS_AUTH_ERROR_USER_UNKNOWN, CS_AUTH_ERROR_AUTH_DENIED } CsAuthError; #define PAM_SERVICE_NAME "cinnamon-screensaver" typedef gboolean (* CsAuthMessageFunc) (CsAuthMessageStyle style, const char *msg, char **response, gpointer data); #define CS_AUTH_ERROR cs_auth_error_quark () GQuark cs_auth_error_quark (void); void cs_auth_set_verbose (gboolean verbose); gboolean cs_auth_get_verbose (void); gboolean cs_auth_priv_init (void); gboolean cs_auth_init (void); gboolean cs_auth_verify_user (const char *username, const char *display, CsAuthMessageFunc func, gpointer data, GError **error); G_END_DECLS #endif /* __CS_AUTH_H */ cinnamon-screensaver-6.2.0/libcscreensaver/cs-auth-pam.c0000664000175000017500000005753514632071776022254 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2006 William Jon McCann * Copyright (C) 2006 Ray Strode * Copyright (C) 2003 Bill Nottingham * Copyright (c) 1993-2003 Jamie Zawinski * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * */ #include "config.h" #include #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "cs-auth.h" #include "subprocs.h" /* Some time between Red Hat 4.2 and 7.0, the words were transposed in the various PAM_x_CRED macro names. Yay! */ #ifndef PAM_REFRESH_CRED # define PAM_REFRESH_CRED PAM_CRED_REFRESH #endif #ifdef HAVE_PAM_FAIL_DELAY /* We handle delays ourself.*/ /* Don't set this to 0 (Linux bug workaround.) */ # define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1) #else /* !HAVE_PAM_FAIL_DELAY */ # define PAM_NO_DELAY(pamh) /* */ #endif /* !HAVE_PAM_FAIL_DELAY */ /* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args. On some other Linux systems with some other version of PAM (e.g., whichever Debian release comes with a 2.2.5 kernel) it takes one arg. I can't tell which is more "recent" or "correct" behavior, so configure figures out which is in use for us. Shoot me! */ #ifdef PAM_STRERROR_TWO_ARGS # define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status)) #else /* !PAM_STRERROR_TWO_ARGS */ # define PAM_STRERROR(pamh, status) pam_strerror((status)) #endif /* !PAM_STRERROR_TWO_ARGS */ static GMainLoop *auth_loop = NULL; static gboolean verbose_enabled = FALSE; static pam_handle_t *pam_handle = NULL; static gboolean did_we_ask_for_password = FALSE; #define DEBUG(...) if (verbose_enabled) g_printerr (__VA_ARGS__) struct pam_closure { const char *username; CsAuthMessageFunc cb_func; gpointer cb_data; int signal_fd; int result; }; typedef struct { struct pam_closure *closure; CsAuthMessageStyle style; const char *msg; char **resp; gboolean should_interrupt_stack; } GsAuthMessageHandlerData; static GCond *message_handled_condition; static GMutex *message_handler_mutex; GQuark cs_auth_error_quark (void) { static GQuark quark = 0; if (! quark) { quark = g_quark_from_static_string ("cs_auth_error"); } return quark; } void cs_auth_set_verbose (gboolean enabled) { verbose_enabled = enabled; } gboolean cs_auth_get_verbose (void) { return verbose_enabled; } static CsAuthMessageStyle pam_style_to_cs_style (int pam_style) { CsAuthMessageStyle style; switch (pam_style) { case PAM_PROMPT_ECHO_ON: style = CS_AUTH_MESSAGE_PROMPT_ECHO_ON; break; case PAM_PROMPT_ECHO_OFF: style = CS_AUTH_MESSAGE_PROMPT_ECHO_OFF; break; case PAM_ERROR_MSG: style = CS_AUTH_MESSAGE_ERROR_MSG; break; case PAM_TEXT_INFO: style = CS_AUTH_MESSAGE_TEXT_INFO; break; default: g_assert_not_reached (); break; } return style; } static gboolean auth_message_handler (CsAuthMessageStyle style, const char *msg, char **response, gpointer data) { gboolean ret; ret = TRUE; *response = NULL; switch (style) { case CS_AUTH_MESSAGE_PROMPT_ECHO_ON: break; case CS_AUTH_MESSAGE_PROMPT_ECHO_OFF: if (msg != NULL && g_str_has_prefix (msg, _("Password:"))) { did_we_ask_for_password = TRUE; } break; case CS_AUTH_MESSAGE_ERROR_MSG: break; case CS_AUTH_MESSAGE_TEXT_INFO: break; default: g_assert_not_reached (); } return ret; } static gboolean cs_auth_queued_message_handler (GsAuthMessageHandlerData *data) { gboolean res; if (cs_auth_get_verbose ()) { DEBUG ("Waiting for lock\n"); } g_mutex_lock (message_handler_mutex); if (cs_auth_get_verbose ()) { DEBUG ("Waiting for response\n"); } res = data->closure->cb_func (data->style, data->msg, data->resp, data->closure->cb_data); data->should_interrupt_stack = res == FALSE; g_printerr ("should interrupt: %d\n", data->should_interrupt_stack); g_cond_signal (message_handled_condition); g_mutex_unlock (message_handler_mutex); if (cs_auth_get_verbose ()) { DEBUG ("Got response\n"); } return FALSE; } static gboolean cs_auth_run_message_handler (struct pam_closure *c, CsAuthMessageStyle style, const char *msg, char **resp) { GsAuthMessageHandlerData data; data.closure = c; data.style = style; data.msg = msg; data.resp = resp; data.should_interrupt_stack = TRUE; g_mutex_lock (message_handler_mutex); /* Queue the callback in the gui (the main) thread */ g_idle_add ((GSourceFunc) cs_auth_queued_message_handler, &data); if (cs_auth_get_verbose ()) { DEBUG ("cs-auth-pam (pid %i): Waiting for respose to message style %d: '%s'\n", getpid (), style, msg); } /* Wait for the response */ g_cond_wait (message_handled_condition, message_handler_mutex); g_mutex_unlock (message_handler_mutex); if (cs_auth_get_verbose ()) { DEBUG ("cs-auth-pam (pid %i): Got respose to message style %d: interrupt:%d\n", getpid (), style, data.should_interrupt_stack); } return data.should_interrupt_stack == FALSE; } static int pam_conversation (int nmsgs, const struct pam_message **msg, struct pam_response **resp, void *closure) { int replies = 0; struct pam_response *reply = NULL; struct pam_closure *c = (struct pam_closure *) closure; gboolean res; int ret; reply = (struct pam_response *) calloc (nmsgs, sizeof (*reply)); if (reply == NULL) { return PAM_CONV_ERR; } res = TRUE; ret = PAM_SUCCESS; for (replies = 0; replies < nmsgs && ret == PAM_SUCCESS; replies++) { CsAuthMessageStyle style; char *utf8_msg; style = pam_style_to_cs_style (msg [replies]->msg_style); utf8_msg = g_locale_to_utf8 (msg [replies]->msg, -1, NULL, NULL, NULL); /* if we couldn't convert text from locale then * assume utf-8 and hope for the best */ if (utf8_msg == NULL) { char *p; char *q; utf8_msg = g_strdup (msg [replies]->msg); p = utf8_msg; while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q)) { *q = '?'; p = q + 1; } } /* handle message locally first */ auth_message_handler (style, utf8_msg, &reply [replies].resp, NULL); if (c->cb_func != NULL) { if (cs_auth_get_verbose ()) { DEBUG ("Handling message style %d: '%s'\n", style, utf8_msg); } /* blocks until the gui responds */ res = cs_auth_run_message_handler (c, style, utf8_msg, &reply [replies].resp); if (cs_auth_get_verbose ()) { DEBUG ("Msg handler returned %d\n", res); } /* If the handler returns FALSE - interrupt the PAM stack */ if (res) { reply [replies].resp_retcode = PAM_SUCCESS; } else { int i; for (i = 0; i <= replies; i++) { free (reply [i].resp); } free (reply); reply = NULL; ret = PAM_CONV_ERR; } } g_free (utf8_msg); } *resp = reply; return ret; } static gboolean close_pam_handle (int status) { if (pam_handle != NULL) { int status2; status2 = pam_end (pam_handle, status); pam_handle = NULL; if (cs_auth_get_verbose ()) { DEBUG (" pam_end (...) ==> %d (%s)\n", status2, (status2 == PAM_SUCCESS ? "Success" : "Failure")); } } if (message_handled_condition != NULL) { g_cond_free (message_handled_condition); message_handled_condition = NULL; } if (message_handler_mutex != NULL) { g_mutex_free (message_handler_mutex); message_handler_mutex = NULL; } return TRUE; } static gboolean create_pam_handle (const char *username, const char *display, struct pam_conv *conv, int *status_code) { int status; const char *service = PAM_SERVICE_NAME; char *disp; gboolean ret; if (pam_handle != NULL) { g_warning ("create_pam_handle: Stale pam handle around, cleaning up\n"); close_pam_handle (PAM_SUCCESS); } /* init things */ pam_handle = NULL; status = -1; disp = NULL; ret = TRUE; /* Initialize a PAM session for the user */ if ((status = pam_start (service, username, conv, &pam_handle)) != PAM_SUCCESS) { pam_handle = NULL; g_warning (_("Unable to establish service %s: %s\n"), service, PAM_STRERROR (NULL, status)); if (status_code != NULL) { *status_code = status; } ret = FALSE; goto out; } if (cs_auth_get_verbose ()) { DEBUG ("cs-auth-pam (pid %i): pam_start (\"%s\", \"%s\", ...) ==> %d (%s)\n", getpid (), service, username, status, PAM_STRERROR (pam_handle, status)); } disp = g_strdup (display); if (disp == NULL) { disp = g_strdup (":0.0"); } if ((status = pam_set_item (pam_handle, PAM_TTY, disp)) != PAM_SUCCESS) { g_warning (_("Can't set PAM_TTY=%s"), display); if (status_code != NULL) { *status_code = status; } ret = FALSE; goto out; } ret = TRUE; message_handled_condition = g_cond_new (); message_handler_mutex = g_mutex_new (); out: if (status_code != NULL) { *status_code = status; } g_free (disp); return ret; } static void set_pam_error (GError **error, int status) { if (status == PAM_AUTH_ERR || status == PAM_USER_UNKNOWN) { char *msg; if (did_we_ask_for_password) { msg = g_strdup (_("Incorrect password.")); } else { msg = g_strdup (_("Authentication failed.")); } g_set_error (error, CS_AUTH_ERROR, CS_AUTH_ERROR_AUTH_ERROR, "%s", msg); g_free (msg); } else if (status == PAM_PERM_DENIED) { g_set_error (error, CS_AUTH_ERROR, CS_AUTH_ERROR_AUTH_DENIED, "%s", _("Not permitted to gain access at this time.")); } else if (status == PAM_ACCT_EXPIRED) { g_set_error (error, CS_AUTH_ERROR, CS_AUTH_ERROR_AUTH_DENIED, "%s", _("No longer permitted to access the system.")); } } static gpointer cs_auth_thread_func (gpointer auth_operation_fd_ptr) { static const int flags = 0; int status; int status2; struct timespec timeout; sigset_t set; const void *p; int auth_operation_fd = GPOINTER_TO_INT(auth_operation_fd_ptr); timeout.tv_sec = 0; timeout.tv_nsec = 1; set = block_sigchld (); status = pam_authenticate (pam_handle, flags); sigtimedwait (&set, NULL, &timeout); unblock_sigchld (); if (cs_auth_get_verbose ()) { DEBUG (" pam_authenticate (...) ==> %d (%s)\n", status, PAM_STRERROR (pam_handle, status)); } if (status != PAM_SUCCESS) { goto done; } if ((status = pam_get_item (pam_handle, PAM_USER, &p)) != PAM_SUCCESS) { /* is not really an auth problem, but it will pretty much look as such, it shouldn't really happen */ goto done; } /* We don't actually care if the account modules fail or succeed, * but we need to run them anyway because certain pam modules * depend on side effects of the account modules getting run. */ status2 = pam_acct_mgmt (pam_handle, 0); if (cs_auth_get_verbose ()) { DEBUG ("pam_acct_mgmt (...) ==> %d (%s)\n", status2, PAM_STRERROR (pam_handle, status2)); } /* FIXME: should we handle these? */ switch (status2) { case PAM_SUCCESS: break; case PAM_NEW_AUTHTOK_REQD: break; case PAM_AUTHINFO_UNAVAIL: break; case PAM_ACCT_EXPIRED: break; case PAM_PERM_DENIED: break; default : break; } /* Each time we successfully authenticate, refresh credentials, for Kerberos/AFS/DCE/etc. If this fails, just ignore that failure and blunder along; it shouldn't matter. Note: this used to be PAM_REFRESH_CRED instead of PAM_REINITIALIZE_CRED, but Jason Heiss says that the Linux PAM library ignores that one, and only refreshes credentials when using PAM_REINITIALIZE_CRED. */ status2 = pam_setcred (pam_handle, PAM_REINITIALIZE_CRED); if (cs_auth_get_verbose ()) { DEBUG (" pam_setcred (...) ==> %d (%s)\n", status2, PAM_STRERROR (pam_handle, status2)); } done: /* we're done, close the fd and wake up the main * loop */ close (auth_operation_fd); return GINT_TO_POINTER(status); } static gboolean cs_auth_loop_quit (GIOChannel *source, GIOCondition condition, gboolean *thread_done) { *thread_done = TRUE; g_main_loop_quit (auth_loop); return FALSE; } static gboolean cs_auth_pam_verify_user (pam_handle_t *handle, int *status) { GThread *auth_thread; GIOChannel *channel; guint watch_id; int auth_operation_fds[2]; int auth_status; gboolean thread_done; channel = NULL; watch_id = 0; auth_status = PAM_AUTH_ERR; /* This pipe gives us a set of fds we can hook into * the event loop to be notified when our helper thread * is ready to be reaped. */ if (pipe (auth_operation_fds) < 0) { goto out; } if (fcntl (auth_operation_fds[0], F_SETFD, FD_CLOEXEC) < 0) { close (auth_operation_fds[0]); close (auth_operation_fds[1]); goto out; } if (fcntl (auth_operation_fds[1], F_SETFD, FD_CLOEXEC) < 0) { close (auth_operation_fds[0]); close (auth_operation_fds[1]); goto out; } channel = g_io_channel_unix_new (auth_operation_fds[0]); /* we use a recursive main loop to process ui events * while we wait on a thread to handle the blocking parts * of pam authentication. */ thread_done = FALSE; watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP, (GIOFunc) cs_auth_loop_quit, &thread_done); auth_thread = g_thread_new ("cs-auth-verify-user", (GThreadFunc) cs_auth_thread_func, GINT_TO_POINTER (auth_operation_fds[1])); if (auth_thread == NULL) { goto out; } auth_loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (auth_loop); /* if the event loop was quit before the thread is done then we can't * reap the thread without blocking on it finishing. The * thread may not ever finish though if the pam module is blocking. * * The only time the event loop is going to stop when the thread isn't * done, however, is if the dialog quits early (from, e.g., "cancel"), * so we can just exit. An alternative option would be to switch to * using pthreads directly and calling pthread_cancel. */ if (!thread_done) { raise (SIGTERM); } auth_status = GPOINTER_TO_INT (g_thread_join (auth_thread)); out: if (watch_id != 0 && !thread_done) { g_source_remove (watch_id); watch_id = 0; } if (channel != NULL) { g_io_channel_unref (channel); } if (status) { *status = auth_status; } return auth_status == PAM_SUCCESS; } /** * cs_auth_verify_user: * @username: user name * @display: display string * @func: (scope async): the auth function callback * @data: (closure func): data for func * @error: Return location for error or %NULL. * * Starts a PAM thread for user authentication. * * Returns: Whether or not the user was authenticated successfully */ gboolean cs_auth_verify_user (const char *username, const char *display, CsAuthMessageFunc func, gpointer data, GError **error) { int status = -1; struct pam_conv conv; struct pam_closure c; struct passwd *pwent; pwent = getpwnam (username); if (pwent == NULL) { return FALSE; } c.username = username; c.cb_func = func; c.cb_data = data; conv.conv = &pam_conversation; conv.appdata_ptr = (void *) &c; /* Initialize PAM. */ create_pam_handle (username, display, &conv, &status); if (status != PAM_SUCCESS) { goto done; } pam_set_item (pam_handle, PAM_USER_PROMPT, _("Username:")); did_we_ask_for_password = FALSE; if (! cs_auth_pam_verify_user (pam_handle, &status)) { goto done; } done: if (status != PAM_SUCCESS) { set_pam_error (error, status); } close_pam_handle (status); return (status == PAM_SUCCESS ? TRUE : FALSE); } gboolean cs_auth_init (void) { return TRUE; } gboolean cs_auth_priv_init (void) { /* We have nothing to do at init-time. However, we might as well do some error checking. If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock" does not exist, warn that PAM probably isn't going to work. This is a priv-init instead of a non-priv init in case the directory is unreadable or something (don't know if that actually happens.) */ const char dir [] = "/etc/pam.d"; const char file [] = "/etc/pam.d/" PAM_SERVICE_NAME; const char file2 [] = "/etc/pam.conf"; struct stat st; if (g_stat (dir, &st) == 0 && st.st_mode & S_IFDIR) { if (g_stat (file, &st) != 0) { g_warning ("%s does not exist.\n" "Authentication via PAM is unlikely to work.", file); } } else if (g_stat (file2, &st) == 0) { FILE *f = g_fopen (file2, "r"); if (f) { gboolean ok = FALSE; char buf[255]; while (fgets (buf, sizeof(buf), f)) { if (strstr (buf, PAM_SERVICE_NAME)) { ok = TRUE; break; } } fclose (f); if (!ok) { g_warning ("%s does not list the `%s' service.\n" "Authentication via PAM is unlikely to work.", file2, PAM_SERVICE_NAME); } } /* else warn about file2 existing but being unreadable? */ } else { g_warning ("Neither %s nor %s exist.\n" "Authentication via PAM is unlikely to work.", file2, file); } /* Return true anyway, just in case. */ return TRUE; } cinnamon-screensaver-6.2.0/libcscreensaver/subprocs.h0000664000175000017500000000215114632071776021762 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * subprocs.c --- choosing, spawning, and killing screenhacks. * * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef __GS_SUBPROCS_H #define __GS_SUBPROCS_H #include G_BEGIN_DECLS void unblock_sigchld (void); #ifdef HAVE_SIGACTION sigset_t #else /* !HAVE_SIGACTION */ int #endif /* !HAVE_SIGACTION */ block_sigchld (void); int signal_pid (int pid, int signal); void await_dying_children (int pid, gboolean debug); G_END_DECLS #endif /* __GS_SUBPROCS_H */ cinnamon-screensaver-6.2.0/libcscreensaver/setuid.c0000664000175000017500000002043114632071776021413 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * setuid.c --- management of runtime privileges. * * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #include "config.h" #ifdef USE_SETRES #define _GNU_SOURCE #endif /* USE_SETRES */ #include #include #include #include #include #include /* for getpwnam() and struct passwd */ #include /* for getgrgid() and struct group */ #include "setuid.h" static char * uid_gid_string (uid_t uid, gid_t gid) { static char *buf; struct passwd *p = NULL; struct group *g = NULL; p = getpwuid (uid); g = getgrgid (gid); buf = g_strdup_printf ("%s/%s (%ld/%ld)", (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); return buf; } static gboolean set_ids_by_number (uid_t uid, gid_t gid, char **message_ret) { int uid_errno = 0; int gid_errno = 0; int sgs_errno = 0; struct passwd *p = getpwuid (uid); struct group *g = getgrgid (gid); if (message_ret) *message_ret = NULL; /* Rumor has it that some implementations of of setuid() do nothing when called with -1; therefore, if the "nobody" user has a uid of -1, then that would be Really Bad. Rumor further has it that such systems really ought to be using -2 for "nobody", since that works. So, if we get a uid (or gid, for good measure) of -1, switch to -2 instead. Note that this must be done after we've looked up the user/group names with getpwuid(-1) and/or getgrgid(-1). */ if (gid == (gid_t) -1) gid = (gid_t) -2; if (uid == (uid_t) -1) uid = (uid_t) -2; #ifndef USE_SETRES errno = 0; if (setgroups (1, &gid) < 0) sgs_errno = errno ? errno : -1; errno = 0; if (setgid (gid) != 0) gid_errno = errno ? errno : -1; errno = 0; if (setuid (uid) != 0) uid_errno = errno ? errno : -1; #else /* !USE_SETRES */ errno = 0; if (setresgid (gid, gid, gid) != 0) gid_errno = errno ? errno : -1; errno = 0; if (setresuid (uid, uid, uid) != 0) uid_errno = errno ? errno : -1; #endif /* USE_SETRES */ if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0) { static char *reason; reason = g_strdup_printf ("changed uid/gid to %s/%s (%ld/%ld).", (p && p->pw_name ? p->pw_name : "???"), (g && g->gr_name ? g->gr_name : "???"), (long) uid, (long) gid); if (message_ret) *message_ret = g_strdup (reason); g_free (reason); return TRUE; } else { char *reason = NULL; if (sgs_errno) { reason = g_strdup_printf ("couldn't setgroups to %s (%ld)", (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (sgs_errno == -1) fprintf (stderr, "%s: unknown error\n", reason); else { errno = sgs_errno; perror (reason); } g_free (reason); reason = NULL; } if (gid_errno) { reason = g_strdup_printf ("couldn't set gid to %s (%ld)", (g && g->gr_name ? g->gr_name : "???"), (long) gid); if (gid_errno == -1) fprintf (stderr, "%s: unknown error\n", reason); else { errno = gid_errno; perror (reason); } g_free (reason); reason = NULL; } if (uid_errno) { reason = g_strdup_printf ("couldn't set uid to %s (%ld)", (p && p->pw_name ? p->pw_name : "???"), (long) uid); if (uid_errno == -1) fprintf (stderr, "%s: unknown error\n", reason); else { errno = uid_errno; perror (reason); } g_free (reason); reason = NULL; } return FALSE; } return FALSE; } /* If we've been run as setuid or setgid to someone else (most likely root) turn off the extra permissions so that random user-specified programs don't get special privileges. (On some systems it is necessary to install this program as setuid root in order to read the passwd file to implement lock-mode.) *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ /* Returns TRUE if OK to lock, FALSE otherwise */ gboolean hack_uid (char **nolock_reason, char **orig_uid, char **uid_message) { char *reason; gboolean ret; ret = TRUE; reason = NULL; if (nolock_reason != NULL) { *nolock_reason = NULL; } if (orig_uid != NULL) { *orig_uid = NULL; } if (uid_message != NULL) { *uid_message = NULL; } /* Discard privileges, and set the effective user/group ids to the real user/group ids. That is, give up our "chmod +s" rights. */ { uid_t euid = geteuid (); gid_t egid = getegid (); uid_t uid = getuid (); gid_t gid = getgid (); if (orig_uid != NULL) { *orig_uid = uid_gid_string (euid, egid); } if (uid != euid || gid != egid) { #ifndef USE_SETRES if (! set_ids_by_number (uid, gid, uid_message)) { #else /* !USE_SETRES */ if (! set_ids_by_number (euid == 0 ? uid : euid, egid == 0 ? gid : egid, uid_message)) { #endif /* USE_SETRES */ reason = g_strdup ("unable to discard privileges."); ret = FALSE; goto out; } } } /* Locking can't work when running as root, because we have no way of knowing what the user id of the logged in user is (so we don't know whose password to prompt for.) *** WARNING: DO NOT DISABLE THIS CODE! If you do so, you will open a security hole. See the sections of the xscreensaver manual titled "LOCKING AND ROOT LOGINS", and "USING XDM". */ if (getuid () == (uid_t) 0) { reason = g_strdup ("running as root"); ret = FALSE; goto out; } out: if (nolock_reason != NULL) { *nolock_reason = g_strdup (reason); } g_free (reason); return ret; } cinnamon-screensaver-6.2.0/libcscreensaver/setuid.h0000664000175000017500000000153314632071776021422 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifndef __GS_SETUID_H #define __GS_SETUID_H #include G_BEGIN_DECLS gboolean hack_uid (char **nolock_reason, char **orig_uid, char **uid_message); G_END_DECLS #endif /* __GS_SETUID_H */ cinnamon-screensaver-6.2.0/libcscreensaver/test-passwd.c0000664000175000017500000001636214632071776022404 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2005-2006 William Jon McCann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Authors: William Jon McCann * */ #include "config.h" #include #include #include #include #include #include #include #include #include "cs-auth.h" #include "setuid.h" /* Initializations that potentially take place as a privileged user: If the executable is setuid root, then these initializations are run as root, before discarding privileges. */ static gboolean privileged_initialization (void) { gboolean ret; char *nolock_reason; char *orig_uid; char *uid_message; #ifndef NO_LOCKING /* before hack_uid () for proper permissions */ cs_auth_priv_init (); #endif /* NO_LOCKING */ ret = hack_uid (&nolock_reason, &orig_uid, &uid_message); if (nolock_reason) { g_warning ("Locking disabled: %s", nolock_reason); } if (uid_message && cs_auth_get_verbose ()) { g_print ("Modified UID: %s", uid_message); } g_free (nolock_reason); g_free (orig_uid); g_free (uid_message); return ret; } /* Figure out what locking mechanisms are supported. */ static gboolean lock_initialization (char **nolock_reason) { if (nolock_reason) { *nolock_reason = NULL; } #ifdef NO_LOCKING if (nolock_reason) { *nolock_reason = g_strdup ("not compiled with locking support"); } return FALSE; #else /* !NO_LOCKING */ /* Finish initializing locking, now that we're out of privileged code. */ if (! cs_auth_init ()) { if (nolock_reason) { *nolock_reason = g_strdup ("error getting password"); } return FALSE; } /* If locking is currently enabled, but the environment indicates that we have been launched as MDM's "Background" program, then disable locking just in case. */ if (getenv ("RUNNING_UNDER_MDM")) { if (nolock_reason) { *nolock_reason = g_strdup ("running under MDM"); } return FALSE; } /* If the server is XDarwin (MacOS X) then disable locking. (X grabs only affect X programs, so you can use Command-Tab to bring any other Mac program to the front, e.g., Terminal.) */ { gboolean macos = FALSE; #ifdef __APPLE__ /* Disable locking if *running* on Apple hardware, since we have no reliable way to determine whether the server is running on MacOS. Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware" but I'm not really sure about that. */ macos = TRUE; #endif if (macos) { if (nolock_reason) { *nolock_reason = g_strdup ("Cannot lock securely on MacOS X"); } return FALSE; } } #endif /* NO_LOCKING */ return TRUE; } static char * request_password (const char *prompt) { char buf [255]; char *pass; char *password; struct termios ts0; struct termios ts1; tcgetattr (fileno (stdin), &ts0); ts1 = ts0; ts1.c_lflag &= ~ECHO; printf ("%s", prompt); if (tcsetattr (fileno (stdin), TCSAFLUSH, &ts1) != 0) { fprintf (stderr, "Could not set terminal attributes\n"); exit (1); } pass = fgets (buf, sizeof (buf) - 1, stdin); tcsetattr (fileno (stdin), TCSANOW, &ts0); if (!pass || !*pass) { exit (0); } if (pass [strlen (pass) - 1] == '\n') { pass [strlen (pass) - 1] = 0; } password = g_strdup (pass); memset (pass, '\b', strlen (pass)); return password; } static gboolean auth_message_handler (CsAuthMessageStyle style, const char *msg, char **response, gpointer data) { gboolean ret; g_message ("Got message style %d: '%s'", style, msg); ret = TRUE; switch (style) { case CS_AUTH_MESSAGE_PROMPT_ECHO_ON: break; case CS_AUTH_MESSAGE_PROMPT_ECHO_OFF: { char *password; password = request_password (msg); *response = password; } break; case CS_AUTH_MESSAGE_ERROR_MSG: break; case CS_AUTH_MESSAGE_TEXT_INFO: break; default: g_assert_not_reached (); } return ret; } int main (int argc, char **argv) { GError *error = NULL; gboolean verbose = TRUE; char *nolock_reason = NULL; #ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, "/usr/share/locale"); # ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); # endif textdomain (GETTEXT_PACKAGE); #endif cs_auth_set_verbose (verbose); if (! privileged_initialization ()) { exit (1); } if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error)) { fprintf (stderr, "%s", error->message); g_error_free (error); exit (1); } if (! lock_initialization (&nolock_reason)) { if (nolock_reason) { g_warning ("Screen locking disabled: %s", nolock_reason); g_free (nolock_reason); } exit (1); } again: error = NULL; if (cs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, NULL, &error)) { printf ("Correct!\n"); } else { if (error != NULL) { fprintf (stderr, "ERROR: %s\n", error->message); g_error_free (error); } printf ("Incorrect\n"); goto again; } return 0; } cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.UPower.Device.xml0000664000175000017500000002301314632071776026212 0ustar fabiofabio ]> Objects implementing this interface are usually discovered through the org.freedesktop.UPower interface on the /org/freedesktop/UPower object on the D-Bus system bus service with the well-known name org.freedesktop.UPower using the EnumerateDevices method. $ dbus-send --print-reply \ --system \ --dest=org.freedesktop.UPower \ /org/freedesktop/UPower/devices/battery_BAT0 \ org.freedesktop.DBus.Properties.GetAll \ string:org.freedesktop.UPower.Device method return sender=:1.386 -> dest=:1.477 reply_serial=2 array [ dict entry( string "native-path" variant string "/sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/PNP0C0A:00/power_supply/BAT0" ) dict entry( string "vendor" variant string "SONY" ) dict entry( string "model" variant string "42T4568" ) dict entry( string "serial" variant string "4179" ) dict entry( string "update-time" variant uint64 1226417875 ) dict entry( string "type" variant uint 2 ) dict entry( string "power-supply" variant boolean true ) dict entry( string "has-history" variant boolean true ) dict entry( string "has-statistics" variant boolean true ) dict entry( string "online" variant boolean false ) dict entry( string "energy" variant double 72.85 ) dict entry( string "energy-empty" variant double 0 ) dict entry( string "energy-full" variant double 74.55 ) dict entry( string "energy-full-design" variant double 74.88 ) dict entry( string "energy-rate" variant double 0 ) dict entry( string "voltage" variant double 16.415 ) dict entry( string "time-to-empty" variant int64 0 ) dict entry( string "time-to-full" variant int64 0 ) dict entry( string "percentage" variant double 97.7197 ) dict entry( string "is-present" variant boolean true ) dict entry( string "state" variant uint 3 ) dict entry( string "is-rechargeable" variant boolean true ) dict entry( string "capacity" variant double 100 ) dict entry( string "technology" variant uint 1 ) ] Unless otherwise noted, an empty string or the value 0 in a property on this interface means not set. Type of power source. 0Unknown 1Line Power 2Battery 3Ups 4Monitor 5Mouse 6Keyboard 7Pda 8Phone The amount of energy left in the power source expressed as a percentage between 0 and 100. Typically this is the same as (energy - energy-empty) / (energy-full - energy-empty). However, some primitive power sources are capable of only reporting percentages and in this case the energy-* properties will be unset while this property is set. This property is only valid if the property type has the value "battery". Whether power is currently being provided through line power. This property is only valid if the property type has the value "line-power". The battery power state. 0Unknown 1Charging 2Discharging 3Empty 4Fully charged 5Pending charge 6Pending discharge This property is only valid if the property type has the value "battery". Warning level of the battery: 0Unknown 1None 2Discharging (only for UPSes) 3Low 4Critical 5Action An icon name, following the Icon Naming Specification cinnamon-screensaver-6.2.0/libcscreensaver/org.freedesktop.Accounts.User.xml0000664000175000017500000000172414632071776026274 0ustar fabiofabio The users real name. The filename of a png file containing the users icon. The users home directory. cinnamon-screensaver-6.2.0/cinnamon-screensaver.pot0000664000175000017500000000746614632071776021461 0ustar fabiofabio# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-12-02 21:29+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: src/cinnamon-screensaver-command.py:41 msgid "Causes the screensaver to exit gracefully" msgstr "" #: src/cinnamon-screensaver-command.py:43 msgid "Query the state of the screensaver" msgstr "" #: src/cinnamon-screensaver-command.py:45 msgid "Query the length of time the screensaver has been active" msgstr "" #: src/cinnamon-screensaver-command.py:47 msgid "Tells the running screensaver process to lock the screen immediately" msgstr "" #: src/cinnamon-screensaver-command.py:49 msgid "Turn the screensaver on (blank the screen)" msgstr "" #: src/cinnamon-screensaver-command.py:51 msgid "If the screensaver is active then deactivate it (un-blank the screen)" msgstr "" #: src/cinnamon-screensaver-command.py:53 msgid "Version of this application" msgstr "" #: src/cinnamon-screensaver-command.py:55 msgid "Message to be displayed in lock screen" msgstr "" #: src/cinnamon-screensaver-command.py:105 msgid "The screensaver is active\n" msgstr "" #: src/cinnamon-screensaver-command.py:107 msgid "The screensaver is inactive\n" msgstr "" #: src/cinnamon-screensaver-command.py:111 msgid "The screensaver is not currently active.\n" msgstr "" #: src/cinnamon-screensaver-command.py:113 #, python-format msgid "The screensaver has been active for %d second.\n" msgid_plural "The screensaver has been active for %d seconds.\n" msgstr[0] "" msgstr[1] "" #: src/manager.py:315 msgid "Cinnamon Screensaver has experienced an error" msgstr "" #: src/manager.py:317 msgid "" "The 'cs-backup-locker' process terminated before the screensaver did. Please " "report this issue and try to describe any actions you may have performed " "prior to this occurring." msgstr "" #: src/passwordEntry.py:23 src/unlock.py:216 msgid "Please enter your password..." msgstr "" #: src/unlock.py:96 msgid "Unlock" msgstr "" #: src/unlock.py:107 msgid "Switch User" msgstr "" #: src/unlock.py:189 msgid "Incorrect password" msgstr "" #: src/unlock.py:206 msgid "Checking..." msgstr "" #: src/unlock.py:250 msgid "You have the Caps Lock key on." msgstr "" #. This is the first line of text for the backup-locker, explaining how to switch to tty #. and run 'cinnamon-unlock-desktop' command. This appears if the screensaver crashes. #: backup-locker/cs-backup-locker.c:255 msgid "Something went wrong with the screensaver." msgstr "" #. (continued) This is a subtitle #: backup-locker/cs-backup-locker.c:265 msgid "We'll help you get your desktop back" msgstr "" #. (new section) Bulleted list of steps to take to unlock the desktop; #: backup-locker/cs-backup-locker.c:276 #, c-format msgid "Switch to a console using ." msgstr "" #. (list continued) #: backup-locker/cs-backup-locker.c:278 msgid "Log in by typing your user name followed by your password." msgstr "" #. (list continued) #: backup-locker/cs-backup-locker.c:280 msgid "At the prompt, type 'cinnamon-unlock-desktop' and press Enter." msgstr "" #. (list continued) #: backup-locker/cs-backup-locker.c:282 #, c-format msgid "Switch back to your unlocked desktop using ." msgstr "" #. (end section) Final words after the list of steps #: backup-locker/cs-backup-locker.c:287 msgid "If you can reproduce this behavior, please file a report here:" msgstr "" cinnamon-screensaver-6.2.0/HACKING0000664000175000017500000000111714632071776015567 0ustar fabiofabioHACKING ======= Cinnamon-Screensaver is normally build using dpkg. If using Linux Mint, Ubuntu, or any other member of the Debian family, install the `devscripts` package with `sudo apt-get install devscripts`. Other dependencies that may be needed to get it to build: `sudo apt install dh-make dh-python gobject-introspection meson build-essential libgtk-3-dev libxdo-dev libgirepository1.0-dev` Once you have devscripts, run `debuild -us -uc` to build cinnamon-screensaver (or any other cinnamon package) and `sudo dpkg -i ../cinnamon-screensaver_{version}_{arch}.deb` to install. cinnamon-screensaver-6.2.0/NEWS0000664000175000017500000013141514632071776015304 0ustar fabiofabio============== Version 3.4.1 ============== - Drop num lock warning - Translation updates (Greek, Marathi, Hindi, Aragonese) ============== Version 3.4.0 ============== - Translations updates (crh) ============== Version 3.3.92 ============== - Fix fade when explicitly locking screen - Translations updates (ko) ============== Version 3.2.2 ============== - Fix --with-systemd=auto configure option - Stop using deprecated GTK+ API - Fix misleading debug messages referring to ConsoleKit - Fix a compile warning when compiled with systemd - Use correct signature for GetActiveTime D-Bus API - Translation updates (Xhosa) - Properly turn off caps lock warning ============== Version 3.2.1 ============== - Optionally use systemd instead of ConsoleKit - Always lock when asked - Fade out quickly on explicit locks - Allow arbitrary long idle times - Fix background scaling on multi-monitor setups - Implement OpenBSD authentication - Translation updates (Burmese, Czech, Norwegian bokmÃ¥l) ============== Version 3.2.0 ============== - Translation updates (Gujarati, Telugu, Assamese, Lithuanian, Irish) =============== Version 3.1.92 =============== - Use maintainer mode by default - Fix the clock to actually update every minute - Translation updates (Japanese) =============== Version 3.1.91 =============== - Use GnomeWallClock to keep informed about time and timezone changes =============== Version 3.1.90 =============== - Properly set background to black when cancelling unlock - Drop the .pc file ============== Version 3.1.5 ============== - Add back keyboard layout indicator - Detect clock skew when resuming or ntp runs and compensate - Fix crash in user switcher - Drop libgnome-menu dependency - Translation updates (Finnish, Thai, Polish, Turkish, Czech, Esperanto, Belarusian, Catalan (Valencian), Persian) ============== Version 3.0.0 ============== - Remove inihibit APIs from the documentation - Translation updates (Russian, Bulgarian, Vietnamese, Punjabi, Uighur, Brazilian Portuguese, Latvian, Simplified Chinese, Traditional Chinese, Basque, Japanese, Danish, Tamil, Czech, Dutch, Catalan, Bengali) ================ Version 2.91.92 ================ - Store time values in seconds instead of minutes - Don't monitor session settings - Ensure the initial state of the clock is correct - Remove dbus activation - Remove daemonization - Request shell exit overview when locking - Translation updates ((Polish, Vietnamese, Brazilian Portuguese, Arabic, Dutch, Romanian, Africaans, Hungarian, Estonian, Slovenian, Portuguese, Hebrew, Norwegian bokmÃ¥l, British English, German, Ukrainian, Swedish, Catalan, Serbian, Korean, French, Indonesian, Assamese) ================ Version 2.91.91 ================ - Follow the users clock format preference - Port to GDBus - Build cleanups - Remove --poke option from the commandline tool (didn't work anyway) - Remove unused delay and timeout preferences - Remove inhibit interface (inhibit the session instead) - Remove theme and job support - Update style to match gnome-shell - Use accountsservice to get face - Translation updates (Korean, Simplified Chinese, Norwegian bokmÃ¥l, Latvian Oriya, Ukrainian, Russian) =============== Version 2.91.90 =============== - Use username in top panel if real name is empty - Various settings fixes - Translation updates =============== Version 2.91.4 =============== - Fix build against recent GTK+ - GNOME 3 style updates - Translation updates =============== Version 2.91.3 =============== - Fix build against recent GTK+ - Use GIO to launch gdmflexiserver - Translation updates =============== Version 2.91.2 =============== - Fix build against recent GTK+ - Port to GSettings =============== Version 2.91.1 =============== - Fix build against recent GTK+ and gnome-desktop - Port background handling to gsettings - Translation updates =============== Version 2.91.0 =============== - Switch to GTK+ 3.0 - Fix crash on systems that lack vidmode and randr extensions - Add session service activation file - Use better unicode symbols in strings - Drop "Leave a message" feature - Drop unlock dialog theme support - Drop support for non-PAM based authentication - Improve aesthetics of unlock dialog - Monitor hotplug fixes - Auto-activate screensaver when needed if not running - Translation updates =============== Version 2.30.0 =============== Translation updates =============== Version 2.29.92 =============== Fixed bugs: - Build fix for systems with x11 < 1.3 =============== Version 2.29.91 =============== Fixed bugs: - #609789 - CVE-2010-0422, handle monitor hotplugging more securely - Fix screen dirt in floaters screensaver =============== Version 2.29.90 =============== Fixed bugs: - #609337, CVE-2010-0414, handle monitor removal more securely =============== Version 2.29.1 =============== Fixed bugs: - #597874, Use xrandr 1.2 gamma fading - RH bug #546656, Fix crash in popsquares - #598476, Don't crash when lock dialog dies during shake - #593616, Fix multi-head issues - #600488, Remove session inhibitors if the originator falls of the bus =============== Version 2.28.0 =============== Fixed bugs: - #444927, get idle time from xorg - #463010, Port cinnamon-screensaver from libglade to gtkbuilder - #564061, Install .desktop file - #570402, screensaver crash if number of screens changes - #570941, String not marked for translation cinnamon-screensaver-command.c - #574896, [Leave message] Cancel button should clean clipboard buffe - #576463, can't unlock screen after upgrade until restarting gnome- screensaver - #581995, Fade out doesn't properly fall back to gamma number if X server only supports 0-size ramp - #586174, Encoding key in desktop files is deprecated - #589728, cinnamon-screensaver: implicit declaration g_printf - #590776, screensaver is never activated - #591193, Update/Create .gitignore - #591194, Add Comment and Icon in cinnamon-screensaver.desktop - #594082, cinnamon-screensaver ignores input when fading to black Translators: - Khaled Hosny (ar) - Amitakhya Phukan (as) - Alexander Shopov (bg) - Runa Bhattacharjee (bn_IN) - Jamil Ahmed (bn) - Denis ARNAUD (br) - Carles Ferrando (ca@valencia) - Petr Kovar (cz) - Ask H. Larsen (da) - Mario Blättermann, Christian Kirbach (de) - Philip Withnall (en_GB) - Jorge González (es) - Ivar Smolin, Mattias Põldaru (et) - Inaki Larranaga Murgoitio (eu) - Tommi Vainikainen (fi) - Claude Paroz (fr) - Seán de Búrca (ga) - Antón Méixome (gl) - Sweta Kothari (gu) - Yaron Shahrabani (he) - Rajesh Ranjan (hi) - Gabor Kelemen (hu) - Luca Ferretti (it) - Shankar Prasad (kn) - Changwoo Ryu (ko) - Gintautas Miliauskas (lt) - Rajesh Ranjan (mai) - Ani (ml) - Sandeep Shedmake (mr) - Kjartan Maraas (nb) - Niels-Christoph Fiedler (nds) - Manoj Kumar Giri (or) - A S Alam (pa) - Tomasz Dominikowski (pl) - Rodrigo L. M. Flores, Krix Apolinário (pt_BR) - Duarte Loreto (pt) - Lucian Adrian Grijincu (ro) - Matej UrbanÄiÄ (sl) - MiloÅ¡ Popović (sr@latin) - MiloÅ¡ Popović (sr) - Daniel Nylander (sv) - ifelix (ta) - krishnababu k (te) - Theppitak Karoonboonyanan (th) - Maxim V. Dziumanenko (uk) - Chao-Hsiung Liao (zh_HK) - Chao-Hsiung Liao (zh_TW) =============== Version 2.27.0 =============== - Add an autostart file for cinnamon-screensaver (#585485) Updated Translations: Telugu, Catalan, zh_CN, Crimean Tatar =============== Version 2.26.1 =============== - Fixed #572955, audit GError usage (William Jon McCann) - Fixed #572956, use g_strerror (William Jon McCann) - Fixed #573495, logout_command setting may be overwritten with keyboard_command value (William Jon McCann) - Fixed #577739, cinnamon-screensaver-gl-helper calls gdk_x11_visual_get_xvisual with NULL visual (William Jon McCann) - Fixed #578669, Screenserver (William Jon McCann) Translations: - Updated crh: ReÅŸat SABIQ - Updated ja: Takeshi AIHANA - Updated kn: Shankar Prasad =============== Version 2.26.0 =============== - Remove power notice signal - Rely on GNOME session idle detection - Proxy g-s inhibitors over to the session - Read status message from session - Accept a full path for theme (Matthias Clasen) =============== Version 2.25.2 =============== - Fix pixmap leak (Matthias Clasen) - Fix dialog border width =============== Version 2.25.1 =============== - Add bevel around dialog - Fixed #444927, get idle time from xorg - Fixed #492949, screensavers-personal-slideshow should not show hidden directories (Jörg) - Fixed #530561, "Random" preview is always the last saver (Maxim Ermilov) - Fixed #561855, Calls wrong slideshow binary if there is one installed in $PATH (Frederic Crozat) Translations: None =============== Version 2.24.1 =============== - Fixed #555701, libgnome-desktop:gnome_bg_create_pixmap() leaks pixmaps and X clients (Scott Remnant) - Fixed #451498, Preview of "Blank screen" is not consistent (Neil Patel) - Fixed #552119, Add GTK and GNOME to screensaver desktop (William Jon McCann) - Fixed #552523, Screensaver suddenly stopped working (William Jon McCann) - Fixed #555254, cinnamon-screensaver-dialog NULL pointer crash (William Jon McCann) - Fixed #555491, cinnamon-screensaver should release mouse/keyboard when failed to grab both of them (Joey Yu Zheng) - Fixed #560456, Cleaning up GTK Includes in cinnamon-screensaver (Maxim Ermilov) Translations: - Updated ar: Djihed Afifi - Updated ast: Andre Klapper, Astur - Updated be@latin: Ihar HraÄyÅ¡ka, Ihar Hrachyshka - Updated pt_BR: Leonardo Ferreira Fontenelle, Vladimir Melo =============== Version 2.24.0 =============== - Updated LINGUAS, sr@latin.po, sr@Latn: Arangel Angov - Updated ar: Djihed Afifi, Khaled Hosny - Updated bg: Alexander Shopov - Updated bn_IN: Runa Bhattacharjee - Updated ca: Gil Forcada - Updated da: Kenneth Nielsen - Updated de: Hendrik Richter - Updated dz: Pema Geyleg, Dawa pemo - Updated en_GB: Philip Withnall, David Lodge - Updated et: Ivar Smolin - Updated fr: Claude Paroz - Updated ga: Seán de Búrca - Updated hr: Robert Sedak, Ante Karamatić - Updated hu: Gabor Kelemen - Updated it: Luca Ferretti - Updated kn: Shankar Prasad - Updated ko: Changwoo Ryu - Updated mk: Arangel Angov, Clytie Siddall - Updated ml: Praveen Arimbrathodiyil, à´ªàµà´°à´µàµ€à´£àµâ€ à´…à´°à´¿à´®àµà´ªàµà´°à´¤àµà´¤àµŠà´Ÿà´¿à´¯à´¿à´²àµâ€ - Updated mr: Sandeep Shedmake - Updated nl: Wouter Bolsterlee - Updated pl: Wadim Dziedzic, wadim dziedzic - Updated pt_BR: Vladimir Melo - Updated ro: Mugurel Tudor, MiÈ™u Moldovan - Updated sr.po, sr@latin: Goran Rakić - Updated ta: I. Felix - Updated tr: Baris Cicek =============== Version 2.23.90 =============== - use standard icon names (Matthias Clasen) - Paint widget background color as face background color. - Add/remove windows as necessary in response to RANDR 1.2 events - Honor the disable_user_switching lockdown key. - Fixed #547013, The dialog incorrectly tell that the numlock is on (William Jon McCann) Translations: - Updated LINGUAS, sr@latin.po, sr@Latn: Goran Rakic - Updated ar: Khaled Hosny - Updated cs: Petr Kovar - Updated eu: Inaki Larranaga Murgoitio, Iñaki Larrañaga Murgoitio - Updated fi: Ilkka Tuohela, Timo Jyrinki - Updated gu: Daniel Nylander, Sweta Kothari - Updated he: Yair Hershkovitz , Yaniv Abir - Updated ja: Takeshi AIHANA - Updated lt: Gintautas Miliauskas - Updated pt: Duarte Loreto - Updated pt_BR: Vladimir Melo - Updated sv: Daniel Nylander Help Manual Translations: None =============== Version 2.23.3 =============== * Fix build issue (Wouter Bolsterlee) * Add a simple frame around user icon * Add a new child widget to use for themes to draw on =============== Version 2.23.2 =============== * Fix gamma lost during fade * Show default desktop background behind lock dialog * Add reporting of inhibitors to --query command =============== Version 2.22.2 =============== * Add a 160 char limit to the message text box. =============== Version 2.22.1 =============== * Fix for bug 525785 – CVE-2008-0887 =============== Version 2.22.0 =============== Translators - Updated oc: Yannig Marchegay - Updated ca: Gil Forcada - Updated eu: Inaki Larranaga Murgoitio, Djihed Afifi - Updated as: Amitakhya Phukan - Updated ar: Artur Flinta, Djihed Afifi - Updated cs: Petr Kovar, Clytie Siddall - Updated et: Priit Laes - Updated bn_IN: Runa Bhattacharjee - Updated nl: Wouter Bolsterlee - Updated pt: Duarte Loreto - Updated nb: Kjartan Maraas - Updated tr: Baris Cicek - Updated ne: Pawan Chitrakar - Updated lt: Gintautas Miliauskas - Updated th: Theppitak Karoonboonyanan - Updated zh_TW: Taiwan - Updated en_GB: Philip Withnall - Updated be: Alexander Nyakhaychyk - Updated fr: Claude Paroz - Updated zh_HK: Hong Kong - Updated pt_BR: Leonardo Ferreira Fontenelle - Updated de: Andre Klapper - Updated hu: Gabor Kelemen - Updated fi: bug #518255, Ilkka Tuohela - Updated da: Kenneth Nielsen - Updated ja: Takeshi AIHANA - Updated he: Yair Hershkovitz - Updated be@latin: Ihar Hrachyshka - Updated ko: Changwoo Ryu - Updated sv: Daniel Nylander - Updated mk: Arangel Angov, Ignacio Casal Quinteiro , Clytie Siddall - Updated sk: Marcel Telka - Updated pl: Artur Flinta - Updated uk: Maxim Dziumanenko - Updated mr: Rahul Bhalerao, mr =============== Version 2.21.6 =============== * New theme transfer progress dialog. (John Millikin) * Migrate gnome-vfs to GIO (John Millikin) * Set default action after cancelling note. =============== Version 2.20.0 =============== cinnamon-screensaver * Fixed #442853, Unlock dialog shows Switch User button when /apps/cinnamon-screensaver/user_switch_enabled is false (William Jon McCann) Translators * Updated el: Nikos Charonitakis * Updated vi: Clytie Siddall * Updated ca: Gil Forcada * Updated it: Luca Ferretti * Updated ar: Djihed Afifi * Updated et: Priit Laes * Updated ru: Nickolay V. Shmyrev * Updated nl: Wouter Bolsterlee * Updated pt: Duarte Loreto * Updated nb: Kjartan Maraas * Updated tr: Baris Cicek * Updated lt: Gintautas Miliauskas * Updated ro: Mugurel Tudor * Updated ta: I. Felix * Updated fr: Stéphane Raimbault * Updated da: Kenneth Nielsen * Updated hu: Gabor Kelemen * Updated ja: Takeshi AIHANA * Updated ml: Ani Peter * Updated mk: Jovan Naumovski + * Updated sk: Andre Klapper * Updated si: Danishka Navin * Updated uk: Maxim Dziumanenko =============== Version 2.19.7 =============== cinnamon-screensaver * Fixed #437225, Solaris needs additional libs linked with to build (William Jon McCann) * Fixed #463754, remove last dep on libgnome (Jani Monoses) * Initialize XWindowAttributes (Rodrigo Moya) * Improvements to slideshow (Frederic Crozat) Translators * Updated gu: Ankit Patel * Updated be@latin: Ihar Hrachyshka * Updated pt_BR: Leonardo Ferreira Fontenelle * Updated ml: Ani Peter * Updated de: Hendrik Richter * Updated ko: Changwoo Ryu * Updated sv: Daniel Nylander * Updated es: Jorge Gonzalez * Updated th: Theppitak Karoonboonyanan * Updated fi: Ilkka Tuohela * Updated eu: Inaki Larranaga Murgoitio * Updated et: Priit Laes * Updated te: Sunil Mohan Adapa * Updated ja: Takeshi AIHANA * Updated pl: Artur Flinta =============== Version 2.19.6 =============== cinnamon-screensaver * Fixed #327602, cinnamon-screensaver never propagates the environment to its helper dialog (William Jon McCann) * Fixed #456705, useless message on stdout (Bill Nottingham) * Fixed #461028, console-kit ActiveChanged (VC switch) does not trigger unlock request (Ian Jackson) * Fixed #461722, Add support to disable slideshow randomization (Frederic Crozat) * Fixed #461814, Allow to specify background color for image slideshow (Frederic Crozat) * Fixed #461824, remove libexif dependency (Frederic Crozat) Translators * Updated mn: Djihed Afifi * Updated nl: Wouter Bolsterlee * Updated be@latin: Ihar Hrachyshka * Updated vi: Nguyễn Thái Ngá»Âc Duy, Clytie Siddall * Updated nb: Kjartan Maraas * Updated de: Andre Klapper * Updated sv: Daniel Nylander * Updated mk: Daniel Nylander * Updated hu: Gabor Kelemen * Updated lv: Daniel Nylander, Djihed Afifi * Updated si: I. Felix, si * Updated en_GB: David Lodge * Updated dz: Pema Geyleg * Updated pl: Artur Flinta * Updated th: Theppitak Karoonboonyanan * Updated fi: Ilkka Tuohela * Updated et: Priit Laes * Updated ja: Takeshi AIHANA * Updated es: Jorge Gonzalez * Updated ta: I. Felix, I Felix =============== Version 2.19.5 =============== cinnamon-screensaver * Fixed #331515, No translation for themes in command line help (William Jon McCann) * Fixed #354663, lock dialog not visible when using nvidia clone (William Jon McCann) * Fixed #434712, cinnamon-screensaver crashes without dbus system bus (William Jon McCann) * Fixed #436818, replace gnome_help_display (William Jon McCann) * Fixed #445443, Screensaver deactivation is not correct (William Jon McCann) Translators * Updated be@latin: Ihar Hrachyshka * Updated vi: Nguyễn Thái Ngá»Âc Duy, Clytie Siddall * Updated nb: Kjartan Maraas * Updated sv: Daniel Nylander * Updated lv: Daniel Nylander * Updated si: I. Felix, si * Updated en_GB: David Lodge * Updated dz: Pema Geyleg * Updated es: Jorge Gonzalez * Updated th: Theppitak Karoonboonyanan * Updated et: Priit Laes * Updated pl: Artur Flinta * Updated ta: I Felix =============== Version 2.19.1 =============== cinnamon-screensaver * Fixed #384509, cinnamon-screensaver could allow users to leave notes when screen is locked (William Jon McCann) * Fixed #411252, Use xdg-user-dirs to find pictures directory (William Jon McCann) * Fixed #428199, Make fast-user-switching work with gdm configurations involving multiple X servers (William Jon McCann) =============== Version 2.18.1 =============== cinnamon-screensaver * Fixed #424165, power management button should reflect g-p-m status (William Jon McCann) * Fixed #425550, cinnamon-screensaver should probably require mouse grab to lock (Ray Strode) Translators * Updated zh_CN: Funda Wang * Updated ca: Josep Puigdemont i Casamajó * Updated da: Peter Bach * Updated lv: Raivis Dejus * Updated sl: Matic Zgur * Updated eu: Inaki Larranaga Murgoitio * Updated gl: Ignacio Casal Quinteiro =============== Version 2.18.0 =============== cinnamon-screensaver * Fixed #412486, cinnamon-screensaver doesn't work with xnest (William Lachance) * Fixed #412492, cinnamon-screensaver won't build * Fixed #417323, build from trunk fails; undefined reference to pango symbols (Elijah Newren) * Fixed #417538, unapproved change broke theme management (William Jon McCann) Translators * Updated ru: Nickolay V. Shmyrev * Updated gu: Ankit Patel * Updated zh_HK.po, zh_TW: Abel Cheung * Updated it: Luca Ferretti * Updated mk: Jovan Naumovski + * Updated lt: Gintautas Miliauskas * Updated ar: Djihed Afifi * Updated dz: Pema Geyleg * Updated pl: Artur Flinta * Updated he: Yair Hershkovitz =============== Version 2.17.8 =============== cinnamon-screensaver * Fixed #400579, cinnamon-screensaver fails to compile against openpam (Ray Strode) * Fixed #407524, Freeze on unlock screen (William Jon McCann) * Fixed #407964, direct fast user switching (William Jon McCann) * Fixed #411393, 'switch user' button needs to turn inactive on selection (William Jon McCann) * Set ConsoleKit idle-hint state Translators * Updated bg: Alexander Shopov * Updated pt: Duarte Loreto * Updated ko: Changwoo Ryu * Updated hu: Gabor Kelemen * Updated lt: Gintautas Miliauskas * Updated uk: Maxim Dziumanenko * Updated fi: Ilkka Tuohela * Updated ja: Takeshi AIHANA * Updated pl: Artur Flinta =============== Version 2.17.7 =============== cinnamon-screensaver * #402183, use gdm socket command to start a login procedure when available (Matthias Clasen) * #403936, Specifically block shaping the window (William Jon McCann) * Sync FUSA code with upstream Translators * Updated fr: Stéphane Raimbault * Updated nl: Wouter Bolsterlee * Updated ca: Josep Puigdemont i Casamajó * Updated pt_BR: Leonardo Ferreira Fontenelle * Updated da: Lasse Bang Mikkelsen =============== Version 2.17.6 =============== cinnamon-screensaver * Categorize preferences for the control center (Denis Washington) Translators * Updated nb: Kjartan Maraas * Updated ca: Josep Puigdemont i Casamajó =============== Version 2.17.5 =============== cinnamon-screensaver * Fix some PAM threading issues * Fixed #387572, auth hangs when password is expired * Fixed #392780, Pass DBUS_SESSION_BUS_ADDRESS into hack environment Translators * Updated lv: Raivis Dejus * Updated et: Priit Laes * Updated ar: Djihed Afifi * Updated en_GB: British * Updated vi: Clytie Siddall =============== Version 2.17.4 =============== cinnamon-screensaver * Fix non-ramp gamma fading * Add script to help debugging * Be sure to initialize threading Themes * (slideshow) Check for empty file list Translators * Khaled Hosny (ar) * Francisco Javier F. Serrador (es) * Kjartan Maraas (nb) * Theppitak Karoonboonyanan (th) =============== Version 2.17.3 =============== cinnamon-screensaver * Convert PAM messages to UTF-8 (Ray Strode) * Make PAM authentication async (Ray Strode) Translators * Ivar Smolin (et) * Daniel Nylander (sv) =============== Version 2.17.2 =============== cinnamon-screensaver * Remove the pointer polling * Add a keyboard layout indicator (Sergey Udaltsov) * Only notice mouse motion of 10% of screen width (Thomas Andersen) * Add SessionPowerManagementIdleChanged D-Bus signal * Add GetInhibitors D-Bus method cinnamon-screensaver-preferences * Add a button to launch the power management preferences cinnamon-screensaver-command * Add option to how long screensaver was active (Patrick McLean) Translators * Francisco Javier F. Serrador (es) * Ivar Smolin (et) * Ilkka Tuohela (fi) * Ankit Patel (gu) * Jovan Naumovski (mk) * Kjartan Maraas (nb) =============== Version 2.17.1 =============== cinnamon-screensaver * Don't poll in dialog cinnamon-screensaver-preferences * Don't show expander column Translators * Gil Forcada (ca) * Jakub Friedl (cs) * Ivar Smolin (et) * Ignacio Casal Quinteiro (gl) =============== Version 2.16.1 =============== cinnamon-screensaver * Set the correct value of DISPLAY for the gl helper * Don't queue keys that may cause focus navigation (Bruno Boaventura) cinnamon-screensaver-preferences * Don't explicitly set icon sizes in preview bar (Dimitur Kirov) Translators * David Lodge (en_GB) * Ivar Smolin (et) * Rajesh Ranjan (hi) * Luca Ferretti (it) * Satoru SATOH (ja) * Leonardo Ferreira Fontenelle (pt_BR) =============== Version 2.16.0 =============== Translators * Kostas Papadimas (el) * Iñaki Larrañaga Murgoitio (eu) * Meelad Zakaria (fa) * Robert-André Mauchin (fr) * Rajesh Ranjan (hi) * Gabor Kelemen (hu) * Gintautas Miliauskas (lt) * Raivis Dejus (lv) * Jovan Naumovski (mk) * Subhransu Behera (or) * Duarte Loreto (pt) * Matic %GŽ%@gur (sl) * Theppitak Karoonboonyanan (th) * Deniz Koçak (tr) * Ching-Hung Lin (zh_HK) * Ching-Hung Lin (zh_TW) =============== Version 2.15.7 =============== Translators * Rostislav \"zbrox\" Raykov (bg) * Gil Forcada (ca) * Hendrik Richter (de) * Ivar Smolin (et) * Ilkka Tuohela (fi) * Ankit Patel (gu) * Gabor Kelemen (hu) * Satoru SATOH (ja) * Vladimer Sichinava (ka) * Changwoo Ryu (ko) * rizoye-xerzi (ku) * Ani Peter (ml) * Badral (mn) * Wouter Bolsterlee (nl) * Subhransu Behera (or) * Leonid Kanter (ru) * Daniel Nylander (sv) * Maxim V. Dziumanenko (uk) * Clytie Siddall (vi) =============== Version 2.15.6 =============== cinnamon-screensaver * Support drag-n-drop of themes to preferences dialog * Add support for embedding a keyboard in window * More memory usage reduction * Only allow a small number of queued key events Translators * Francisco Javier F. Serrador (es) * Ivar Smolin (et) * Ilkka Tuohela (fi) * Christophe Merlet (RedFox) (fr) * Ankit Patel (gu) * ahmad riza h nst (id) * %GŽ%@ygimantas Beru%GÄÂ%@ka (lt) * Jovan Naumovski (mk) * Kjartan Maraas (nb) * Matic %GŽ%@gur (sl) * Theppitak Karoonboonyanan (th) =============== Version 2.15.5 =============== cinnamon-screensaver * Turn off themes when screen is not visible * Make the unlock dialog show on correct window under Xinerama * Prevent theme flickering from watchdog timer * Listen for HAL coffee key events * Ignore brightness key events cinnamon-screensaver-preferences * Populate themes list in an idle after startup Translators * Mahay Alam Khan (bn_IN) * Gil Forcada (ca) * Jakub Friedl (cs) * Hendrik Richter (de) * Kostas Papadimas (el) * Francisco Javier F. Serrador (es) * Priit Laes (et) * Ankit Patel (gu) * Rajesh Ranjan (hi) * Changwoo Ryu (ko) * Fano Rajaonarisoa (mg) * Daniel Nylander (sv) * Theppitak Karoonboonyanan (th) =============== Version 2.15.4 =============== cinnamon-screensaver * Override gtk+ theme background style * Use helper command to get best GL visual * Add a blocking inhibit option to the command-line tool * Add short options to the command-line tool * Misc memory reduction Translators * Rostislav \"zbrox\" Raykov (bg) * Mahay Alam Khan (bn_IN) * Jakub Friedl (cs) * Hendrik Richter (de) * Francisco Javier F. Serrador (es) * Ilkka Tuohela (fi) * Robert-André Mauchin (fr) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Yaniv Abir (he) * Rajesh Ranjan (hi) * Raivis Dejus (lv) * Jovan Naumovski (mk) * Kjartan Maraas (nb) * Shyam Krishna Bal (ne) * Daniel Nylander (sv) * Felix (ta) * Theppitak Karoonboonyanan (th) =============== Version 2.15.3 =============== cinnamon-screensaver * Fully support PAM driving the unlock dialog * Only support one auth backend configured at build time * Don't try to unlock using root's password * DBUS API changes based on XDG feedback * Add docbook documentation (Richard Hughes) * Use replacable tokens in the lock dialog theme glade file * Set lock flag before activating (Rodrigo Moya) * Fix support for RUNNING_UNDER_GDM environment variable * Increase minimum distance the mouse must move to detect motion * Add theme engine profiling support * Switch user when double clicking in treeview * Don't reset throttle when deactivating * Try to grab keyboard before doing idle notice fade-out * Make activation wait for fade to complete. cinnamon-screensaver-preferences * Print a warning that locking is disabled when running as root Themes * (slideshow) Performance improvements (L. David Baron) Translators * Jakub Friedl (cs) * Francisco Javier F. Serrador (es) * Ankit Patel (gu) * Kjartan Maraas (nb) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) =============== Version 2.15.2 =============== cinnamon-screensaver * Sync with FUSA code (fixes GDM configuration) * Make cancel close the unlock dialog * Make button presses and scroll events request an unlock * Send DBUS signals before/after authentication is requested * Various fixes for async fade out * Fix potential crasher when poked while fading * Support short username in themed dialog (Matthias Clasen) Themes * (popsquares) Fix crash when window is NULL (Joe Marcus Clarke) Translators * Francisco Javier F. Serrador (es) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Kjartan Maraas (nb) * Chao-Hsiung Liao (zh_HK) * Chao-Hsiung Liao (zh_TW) =============== Version 2.15.1 =============== cinnamon-screensaver * Add support for theming the lock dialog * Use OpenGL to get the best visual and use it * Shorten mouse poll interval * New DBUS API for throttle and inhibit * Support themes in all XDG dirs (including $HOME) * Make caps-lock warning more accurate cinnamon-screensaver-preferences * Add fullscreen preview * Update wording Translators * Priit Laes (et) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Clytie Siddall (vi) =============== Version 2.14.1 =============== cinnamon-screensaver * Detect button press activity (Ray Strode) * Make operation after system suspend (Ray Strode) * Cygwin build fix * Use GTimer for portability * Obtain new Kerberos credentials when unlocking screen Themes * (floaters) Use the entire screen at start Translators * Lukas Novotny (cs) * Ivar Smolin (et) * Benoît Dejean (fr) * Wouter Bolsterlee (nl) * Andrzej PolatyÃ…ski (pl) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) =============== Version 2.14.0 =============== cinnamon-screensaver * Removed unused GConf keys from schemas * Don't queue keypad enter keypresses * Use black circle as invisible character in password entry Translators * Rostislav \"zbrox\" Raykov (bg) * Mahay Alam Khan (bn) * Gil Forcada (ca) * Petr TomeÅ¡ (cs) * Rhys Jones (cy) * David Nielsen (da) * Frank Arnold (de) * Ivar Smolin (et) * Rajesh Ranjan (hi) * Luca Ferretti (it) * Young-Ho Cha (ko) * Kjartan Maraas (nb) * Kjartan Maraas (no) * Raphael Higino (pt_BR) * Duarte Loreto (pt) * Laurent Dhima (sq) * Daniel Nylander (sv) * Maxim V. Dziumanenko (uk) =============== Version 2.13.92 =============== cinnamon-screensaver * Fix problems when some other program has keyboard grabbed * Add a pkg-config file for out of tree themes * Use a black bullet character instead of * for password entry * Capitalize DBUS method names that weren't already cinnamon-screensaver-preferences * Track gconf key changes for themes Translators * Kostas Papadimas (el) * Francisco Javier F. Serrador (es) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Gabor Kelemen (hu) * Luca Ferretti (it) * Satoru SATOH (ja) * Vladimer SIchinava (ka) * Hyun-Jin Moon (ko) * Žygimantas BeruÄka (lt) * Kjartan Maraas (nb) * Tino Meinen (nl) * Kjartan Maraas (no) * Raphael Higino (pt_BR) * Leonid Kanter (ru) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) =============== Version 2.13.91 =============== cinnamon-screensaver * Maintain a pointer grab unless the unlock dialog is up * Convert the real and user names to UTF8 * Use G_GUINT32_FORMAT for communicating XID * Start themes after grabbing window * Handle broken grabs * Don't abort when fade isn't available * Display host name in unlock dialog cinnamon-screensaver-preferences * Add help * Don't make heading bold or indent under it Themes * (floaters) More performance work (Ray Strode) Translators * Gil Forcada (ca) * Lukas Novotny (cs) * Žygimantas BeruÄka (lt) * Ãivind Hoel (nb) =============== Version 2.13.90 =============== cinnamon-screensaver * Ignore our windows when watching for map and configure events * Clear window after showing to prevent flashing * Don't start child processes in home directory themes * (floaters) Cap update rate (Ray Strode) Translators * Rostislav \"zbrox\" Raykov (bg) * Hendrik Brandt (de) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Tino Meinen (nl) * Theppitak Karoonboonyanan (th) * Chao-Hsiung Liao (zh_HK) * Chao-Hsiung Liao (zh_TW) ============== Version 2.13.5 ============== cinnamon-screensaver * Move DPMS functionality to gnome-power-manager * Rename the DBUS interface from screensaver to ScreenSaver * Disable the X.org grab smasher * Don't do the slow fade if we are inhibited * Fix screen flashing after idle activation cinnamon-screensaver-preferences * Rework dialog for setting session idle timeout * Remove unused help button Translators * Rostislav \"zbrox\" Raykov (bg) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Ilkka Tuohela (fi) * Ignacio Casal Quinteiro (gl) * Takeshi AIHANA (ja) * Tino Meinen (nl) * Clytie Siddall (vi) * Abel Cheung (zh_HK) * Abel Cheung (zh_TW) ============== Version 0.0.24 ============== cinnamon-screensaver * Add proper session idle reporting * Clear all children of toplevel windows Translators * Josep Puigdemont i Casamajó (ca) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Ilkka Tuohela (fi) * Ankit Patel (gu) * Žygimantas BeruÄka (lt) * Tino Meinen (nl) * áûþñþôðý Ã. áÑõôþÑÃµÃ²Ã¸Ñ (sr) * Theppitak Karoonboonyanan (th) * Clytie Siddall (vi) ============== Version 0.0.23 ============== cinnamon-screensaver * Fix idle warning cancellation * Changes to D-Bus API * Add --debug command line option * Build fix when DPMS disabled themes * (floaters) New theme engine (Ray Strode et al) Translators * Francisco Javier F. Serrador (es) * Ignacio Casal Quinteiro (gl) * Ankit Patel (gu) * Takeshi AIHANA (ja) * Theppitak Karoonboonyanan (th) ============== Version 0.0.22 ============== cinnamon-screensaver * Don't mess up event mask for root window. Translators * Miloslav Trmac (cs) * Gabor Kelemen (hu) * Kjartan Maraas (nb) * Kjartan Maraas (no) ============== Version 0.0.21 ============== cinnamon-screensaver * Try to prevent popups over screensaver window * Use an interruptable slow fade before idle activation Translators * Rostislav \"zbrox\" Raykov (bg) * Ilkka Tuohela (fi) * Åygimantas BeruÄka (lt) * Amanpreet Singh Alam (pa) * Marcel Telka (sk) * Theppitak Karoonboonyanan (th) ============== Version 0.0.20 ============== cinnamon-screensaver * Fix blanking when running under Xnest ============== Version 0.0.19 ============== cinnamon-screensaver * Allow trying password after auth failure * Allow more space for user list * Make unlock dialog centered vertically * Use OverrideRedirect windows * Sync with latest FUSA version * Add test for window/dialog subsystem * Fix leaking of signal handlers * Misc correctness fixes Translators * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Davy Defaud (fr) * Takeshi AIHANA (ja) * Åygimantas Beruka (lt) ============== Version 0.0.18 ============== cinnamon-screensaver * UI tweaks for unlock dialog * Fix Intel compiler warnings (Kjartan Maraas) * Do more error checking of GConf input * Check for command parsing errors * Fix leaks * Redesign the idle watcher and fix leaks * Solaris build fixes (Damien Carbery) cinnamon-screensaver-preferences * Make lock toggle work again Translators * Miloslav Trmac (cs) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Davy Defaud (fr) * Gabor Kelemen (hu) * Takeshi AIHANA (ja) * Tino Meinen (nl) ============== Version 0.0.17 ============== cinnamon-screensaver * Restart idle watch if can't grab keyboard * Make timeout in preferences work again * Daemonize process by default * Add option --no-daemon * Don't use any icon if face image is not found Translators * Adam Weinberger (en_CA) * Kjartan Maraas (nb) * Kjartan Maraas (no) ============== Version 0.0.16 ============== cinnamon-screensaver * Shake the dialog when authentication fails * Maintain nicer aspect ratio on unlock dialog * Only notice motion of 10 pixels or more * Periodically raise windows * Don't emit duplicate response signals from dialog * Make dialog buttons insensitive when input isn't allowed * Add a gconf setting for the logout command themes * (slideshow) Skip thumbnail images Translators * Rostislav \"zbrox\" Raykov (bg) * Adam Weinberger (en_CA) * Francisco Javier F. Serrador (es) * Takeshi AIHANA (ja) * Tino Meinen (nl) * Funda Wang (zh_CN) ============== Version 0.0.15 ============== cinnamon-screensaver * First version with working power management * Add Gamma fade out * Don't forward first enter or space to unlock dialog * Make poke command work when screen is blanked * Add getIdleTime dbus method * Translation fixes themes * (slideshow) Optionally use EXIF info to rotate images (torkel@acc.umu.se) Translators * Rostislav \"zbrox\" Raykov (bg) * Miloslav Trmac (cs) * Adam Weinberger (en_CA) * Žygimantas BeruÄka (lt) * Kjartan Maraas (nb) * Tino Meinen (nl) * Kjartan Maraas (no) ============== Version 0.0.14 ============== cinnamon-screensaver * Don't drop keypresses before unlock dialog appears * Use .desktop files and menu spec to find themes * Simplify unlock dialog * Make user switching a gconf option (Matthias Clasen) * Make user switching disabled by default * Fix a crash due to a missing error handler cinnamon-screensaver-preferences * Use new themes API that will theme list to be modified by distro/sysadmin/user. Translators * Ilkka Tuohela (fi) * Luca Ferretti (it) * Takeshi AIHANA (ja) * Tino Meinen (nl) * Clytie Siddall (vi) ============== Version 0.0.13 ============== cinnamon-screensaver * Fix problem when starting GNOME from KDM * Only look for theme files that end in .xml * Look for xscreensaver themes in a few more places ============== Version 0.0.12 ============== cinnamon-screensaver * Fix crasher caused by destroying NULL hash table * Don't activate after last inhibitor is removed Translators * Adam Weinberger (en_CA) ============== Version 0.0.11 ============== cinnamon-screensaver * Make unlock dialog show more quickly * Support the gconf cycle timeout key * Add system name to unlock dialog cinnamon-screensaver-preferences * Add random theme (Rodrigo) Translators * Francisco Javier F. Serrador (es) * Tino Meinen (nl) * Clytie Siddall (vi) ============== Version 0.0.10 ============== cinnamon-screensaver * Fix hang on system resume * Actually use the gconf lock key * Add configure option to look for xscreensaver hacks (Rodrigo) cinnamon-screensaver-preferences * Add checkbox to enable/disable locking (Rodrigo) * Make type ahead find work correctly * HIG fixes (Dennis Cranston) Translators * Adam Weinberger (en_CA) * Eric Maeker (fr) * Takeshi AIHANA (ja) * Clytie Siddall (vi) ============= Version 0.0.9 ============= cinnamon-screensaver * Added slideshow theme engine * Display username if real name is unknown (Rodrigo) themes * Added personal slideshow theme * Added cosmos slideshow theme Translators * Rostislav \"zbrox\" Raykov (bg) * Tommi Vainikainen (fi) * Maxim V. Dziumanenko (uk) ============= Version 0.0.8 ============= cinnamon-screensaver * Add Dbus API for inhibiting the idle activation * Integrate fast-user-switching directly into the unlock dialog. * Restart unlock dialog timer after every keystroke * Fix leaks * Fix xauth problems for some displays Translators * Adam Weinberger (en_CA) * Takeshi AIHANA (ja) * Tino Meinen (nl) * Abel Cheung (zh_TW) ============= Version 0.0.7 ============= cinnamon-screensaver * Fix authentication on SuSE/Novell * Automatically find the SuSE/Novell gdm.conf file * Don't require the gdm.conf file to exist at build time * Actually ship the translations * Fix crash in popsquares * Reset throttle value after deactivation Translators * Hendrik Richter (de) * Adam Weinberger (en_CA) * Takeshi AIHANA (ja) * Christian Rose (sv) ============= Version 0.0.6 ============= cinnamon-screensaver * Added Multi-head/Xinerama support * Added screensaver throttle capability * Depend on dbus >= 0.3 * Use property based dbus methods/signals * Fix Solaris build * Install own PAM configuration instead of relying on xscreensaver * Add ability to disable 'New Login' button cinnamon-screensaver-preferences * Support drag and drop of theme configuration files * Display a nicer form of the activation time value Translators * Frank Arnold (de) * Adam Weinberger (en_CA) * James Ogley (en_GB) * Francisco Javier F. Serrador (es) * Raphael Higino (pt_BR) * Christian Rose (sv) * Funda Wang (zh_CN) ============= Version 0.0.5 ============= cinnamon-screensaver * Support user themes in USER_DATA_DIR/cinnamon-screensaver/themes/ * Send activation/deactivation signals on session message bus * Added DPMS support * Added xscreensaver compatible XML configuration for themes * Add optional logout button after a specified amount of time * Fix leaking of file descriptors cinnamon-screensaver-preferences * Added configuration applet * Use screensaver icon themes * Added version of popsquares that follows GNOME theme cinnamon-screensaver-6.2.0/COPYING.LIB0000664000175000017500000006130214632071776016242 0ustar fabiofabio GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street - Suite 500, Boston, MA, 02110-1335, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA, 02110-1335, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cinnamon-screensaver-6.2.0/backup-locker/0000775000175000017500000000000014632071776017322 5ustar fabiofabiocinnamon-screensaver-6.2.0/backup-locker/meson.build0000664000175000017500000000131014632071776021457 0ustar fabiofabiobackup_locker_data = configuration_data() backup_locker_data.set_quoted('GETTEXT_PACKAGE', 'cinnamon-screensaver') backup_locker_data.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir'))) backup_locker_data.set_quoted('VERSION', meson.project_version()) bl_config = configure_file(output : 'config.h', configuration : backup_locker_data ) bl_sources = [ 'cs-backup-locker.c', ] backup_locker = executable('cs-backup-locker', bl_sources, include_directories: inc, dependencies: [x11, gtk, glib], link_with: libcscreensaver, install_dir: libexecdir, install: true ) install_data('cinnamon-unlock-desktop', install_dir: bindir, install_mode: 'rwxr-xr-x' ) cinnamon-screensaver-6.2.0/backup-locker/cs-backup-locker.c0000664000175000017500000004747014632071776022627 0ustar fabiofabio#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static gboolean debug = FALSE; static guint term_tty = 0; static guint session_tty = 0; static GMutex pretty_xid_mutex; static GCancellable *window_monitor_cancellable = NULL; static guint sigterm_src_id; #define BACKUP_TYPE_WINDOW (backup_window_get_type ()) G_DECLARE_FINAL_TYPE (BackupWindow, backup_window, BACKUP, WINDOW, GtkWindow) static void setup_window_monitor (BackupWindow *window, gulong xid); static void quit (BackupWindow *window); struct _BackupWindow { GtkWindow parent_instance; GtkWidget *fixed; GtkWidget *info_box; CsGdkEventFilter *event_filter; CsEventGrabber *grabber; gulong pretty_xid; guint activate_idle_id; gboolean should_grab; }; G_DEFINE_TYPE (BackupWindow, backup_window, GTK_TYPE_WINDOW) static void position_info_box (BackupWindow *window) { GdkDisplay *display = gdk_display_get_default (); GdkMonitor *monitor = gdk_display_get_primary_monitor (display); GdkRectangle rect; GtkRequisition natural_size; gtk_widget_get_preferred_size (window->info_box, NULL, &natural_size); if (natural_size.width == 0 || natural_size.height == 0) { return; } gdk_monitor_get_workarea (monitor, &rect); g_debug ("Positioning info box (%dx%d) to primary monitor (%d+%d+%dx%d)", natural_size.width, natural_size.height, rect.x, rect.y, rect.width, rect.height); gtk_fixed_move (GTK_FIXED (window->fixed), window->info_box, rect.x + (rect.width / 2) - (natural_size.width / 2), rect.y + (rect.height / 2) - (natural_size.height / 2)); } static void root_window_size_changed (CsGdkEventFilter *filter, gpointer user_data) { BackupWindow *window = BACKUP_WINDOW (user_data); GdkWindow *gdk_win; Display *xdisplay; gint w, h, screen_num; gdk_win = gtk_widget_get_window (GTK_WIDGET (window)); xdisplay = GDK_DISPLAY_XDISPLAY (gdk_window_get_display (gdk_win)); screen_num = DefaultScreen (xdisplay); w = DisplayWidth (xdisplay, screen_num); h = DisplayHeight (xdisplay, screen_num); gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)), 0, 0, w, h); position_info_box (window); gtk_widget_queue_resize (GTK_WIDGET (window)); } static void set_active_background (BackupWindow *window, gboolean active) { GtkStyleContext *context; context = gtk_widget_get_style_context (GTK_WIDGET (window)); if (active) { gtk_style_context_remove_class (context, "backup-dormant"); gtk_style_context_add_class (context, "backup-active"); } else { gtk_style_context_remove_class (context, "backup-active"); gtk_style_context_add_class (context, "backup-dormant"); } gtk_widget_queue_draw (GTK_WIDGET (window)); } static void backup_window_show (GtkWidget *widget) { g_return_if_fail (BACKUP_IS_WINDOW (widget)); if (GTK_WIDGET_CLASS (backup_window_parent_class)->show) { GTK_WIDGET_CLASS (backup_window_parent_class)->show (widget); } } static void window_grab_broken (gpointer data); static gboolean activate_backup_window_cb (BackupWindow *window) { g_debug ("Grabbing input"); if (window->should_grab) { if (cs_event_grabber_grab_root (window->grabber, FALSE)) { guint32 user_time; cs_event_grabber_move_to_window (window->grabber, gtk_widget_get_window (GTK_WIDGET (window)), gtk_widget_get_screen (GTK_WIDGET (window)), FALSE); g_signal_connect_swapped (window, "grab-broken-event", G_CALLBACK (window_grab_broken), window); set_active_background (window, TRUE); user_time = gdk_x11_display_get_user_time (gtk_widget_get_display (GTK_WIDGET (window))); gdk_x11_window_set_user_time (gtk_widget_get_window (GTK_WIDGET (window)), user_time); gtk_widget_show (window->info_box); position_info_box (window); } else { return G_SOURCE_CONTINUE; } } window->activate_idle_id = 0; return G_SOURCE_REMOVE; } static void activate_backup_window (BackupWindow *window) { g_clear_handle_id (&window->activate_idle_id, g_source_remove); window->activate_idle_id = g_idle_add ((GSourceFunc) activate_backup_window_cb, window); } static void backup_window_ungrab (BackupWindow *window) { cs_event_grabber_release (window->grabber); window->should_grab = FALSE; } static void window_grab_broken (gpointer data) { BackupWindow *window = BACKUP_WINDOW (data); g_signal_handlers_disconnect_by_func (window, window_grab_broken, window); if (window->should_grab) { g_debug ("Grab broken, retrying"); activate_backup_window (window); } } static gboolean update_for_compositing (gpointer data) { BackupWindow *window = BACKUP_WINDOW (data); GdkVisual *visual; visual = gdk_screen_get_rgba_visual (gdk_screen_get_default ()); if (!visual) { g_critical ("Can't get RGBA visual to paint backup window"); return FALSE; } if (visual != NULL && gdk_screen_is_composited (gdk_screen_get_default ())) { gtk_widget_hide (GTK_WIDGET (window)); gtk_widget_unrealize (GTK_WIDGET (window)); gtk_widget_set_visual (GTK_WIDGET (window), visual); gtk_widget_realize (GTK_WIDGET (window)); gtk_widget_show (GTK_WIDGET (window)); } g_debug ("update for compositing\n"); if (window->should_grab) { activate_backup_window (window); } return TRUE; } static void on_composited_changed (gpointer data) { g_debug ("Received composited-changed"); g_return_if_fail (BACKUP_IS_WINDOW (data)); BackupWindow *window = BACKUP_WINDOW (data); if (!update_for_compositing (window)) { g_critical ("Error realizing backup-locker window - exiting"); quit(window); } } static void screensaver_window_changed (CsGdkEventFilter *filter, Window xwindow, BackupWindow *window) { backup_window_ungrab (window); set_active_background (window, FALSE); setup_window_monitor (window, xwindow); } static void backup_window_realize (GtkWidget *widget) { if (GTK_WIDGET_CLASS (backup_window_parent_class)->realize) { GTK_WIDGET_CLASS (backup_window_parent_class)->realize (widget); } BackupWindow *window = BACKUP_WINDOW (widget); cs_screen_set_net_wm_name (gtk_widget_get_window (widget), "backup-locker"); root_window_size_changed (window->event_filter, (gpointer) widget); cs_gdk_event_filter_stop (window->event_filter); cs_gdk_event_filter_start (window->event_filter, FALSE, debug); } static void backup_window_init (BackupWindow *window) { GtkWidget *box; GtkWidget *widget; PangoAttrList *attrs; gtk_widget_set_events (GTK_WIDGET (window), gtk_widget_get_events (GTK_WIDGET (window)) | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); window->fixed = gtk_fixed_new (); gtk_container_add (GTK_CONTAINER (window), window->fixed); box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_widget_set_valign (box, GTK_ALIGN_CENTER); widget = gtk_image_new_from_icon_name ("csr-backup-locker-icon", GTK_ICON_SIZE_DIALOG); gtk_image_set_pixel_size (GTK_IMAGE (widget), 100); gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 6); // This is the first line of text for the backup-locker, explaining how to switch to tty // and run 'cinnamon-unlock-desktop' command. This appears if the screensaver crashes. widget = gtk_label_new (_("Something went wrong with the screensaver.")); attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_size_new (20 * PANGO_SCALE)); pango_attr_list_insert (attrs, pango_attr_foreground_new (65535, 65535, 65535)); gtk_label_set_attributes (GTK_LABEL (widget), attrs); pango_attr_list_unref (attrs); gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 6); // (continued) This is a subtitle widget = gtk_label_new (_("We'll help you get your desktop back")); attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_size_new (12 * PANGO_SCALE)); pango_attr_list_insert (attrs, pango_attr_foreground_new (65535, 65535, 65535)); gtk_label_set_attributes (GTK_LABEL (widget), attrs); pango_attr_list_unref (attrs); gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 6); const gchar *steps[] = { // (new section) Bulleted list of steps to take to unlock the desktop; N_("Switch to a console using ."), // (list continued) N_("Log in by typing your user name followed by your password."), // (list continued) N_("At the prompt, type 'cinnamon-unlock-desktop' and press Enter."), // (list continued) N_("Switch back to your unlocked desktop using .") }; const gchar *bug_report[] = { // (end section) Final words after the list of steps N_("If you can reproduce this behavior, please file a report here:"), // (end section continued) "https://github.com/linuxmint/cinnamon-screensaver" }; GString *str = g_string_new (NULL); gchar *tmp0 = NULL; gchar *tmp1 = NULL; tmp0 = g_strdup_printf (_(steps[0]), term_tty); tmp1 = g_strdup_printf ("• %s\n", tmp0); g_string_append (str, tmp1); g_free (tmp0); g_free (tmp1); tmp1 = g_strdup_printf ("• %s\n", _(steps[1])); g_string_append (str, tmp1); g_free (tmp1); tmp1 = g_strdup_printf ("• %s\n", _(steps[2])); g_string_append (str, tmp1); g_free (tmp1); tmp0 = g_strdup_printf (_(steps[3]), session_tty); tmp1 = g_strdup_printf ("• %s\n", tmp0); g_string_append (str, tmp1); g_free (tmp0); g_free (tmp1); g_string_append (str, "\n"); for (int i = 0; i < G_N_ELEMENTS (bug_report); i++) { gchar *line = g_strdup_printf ("%s\n", _(bug_report[i])); g_string_append (str, line); g_free (line); } widget = gtk_label_new (str->str); g_string_free (str, TRUE); attrs = pango_attr_list_new (); pango_attr_list_insert (attrs, pango_attr_size_new (10 * PANGO_SCALE)); pango_attr_list_insert (attrs, pango_attr_foreground_new (65535, 65535, 65535)); gtk_label_set_attributes (GTK_LABEL (widget), attrs); pango_attr_list_unref (attrs); gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE); gtk_widget_set_halign (widget, GTK_ALIGN_CENTER); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 6); g_signal_connect_swapped (gdk_screen_get_default (), "composited-changed", G_CALLBACK (on_composited_changed), window); gtk_widget_show_all (box); gtk_widget_set_no_show_all (box, TRUE); gtk_widget_hide (box); window->info_box = box; g_signal_connect_swapped (window->info_box, "realize", G_CALLBACK (position_info_box), window); gtk_fixed_put (GTK_FIXED (window->fixed), window->info_box, 0, 0); gtk_widget_show (window->fixed); window->grabber = cs_event_grabber_new (debug); } static void backup_window_finalize (GObject *object) { BackupWindow *window; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_WINDOW (object)); window = BACKUP_WINDOW (object); backup_window_ungrab (window); g_object_unref (window->event_filter); g_object_unref (window->grabber); G_OBJECT_CLASS (backup_window_parent_class)->finalize (object); } static void backup_window_class_init (BackupWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = backup_window_finalize; widget_class->show = backup_window_show; widget_class->realize = backup_window_realize; } static GtkWidget * backup_window_new (gulong pretty_xid) { BackupWindow *window; GtkStyleContext *context; GtkCssProvider *provider; GObject *result; result = g_object_new (BACKUP_TYPE_WINDOW, "type", GTK_WINDOW_POPUP, NULL); window = BACKUP_WINDOW (result); context = gtk_widget_get_style_context (GTK_WIDGET (window)); gtk_style_context_remove_class (context, "background"); provider = gtk_css_provider_new (); gtk_css_provider_load_from_data (provider, ".backup-dormant { background-color: transparent; }" ".backup-active { background-color: black; }", -1, NULL); gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); set_active_background (window, FALSE); window->event_filter = cs_gdk_event_filter_new (GTK_WIDGET (window), pretty_xid); g_signal_connect (window->event_filter, "xscreen-size", G_CALLBACK (root_window_size_changed), window); g_signal_connect (window->event_filter, "screensaver-window-changed", G_CALLBACK (screensaver_window_changed), window); window->pretty_xid = pretty_xid; window->should_grab = FALSE; if (!update_for_compositing (window)) { return NULL; } setup_window_monitor (BACKUP_WINDOW (window), window->pretty_xid); return GTK_WIDGET (result); } static void window_monitor_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GSubprocess *xprop_proc; GError *error; gulong xid = GDK_POINTER_TO_XID (task_data); gchar *xid_str = g_strdup_printf ("0x%lx", xid); error = NULL; xprop_proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE, &error, "xprop", "-spy", "-id", (const gchar *) xid_str, NULL); g_free (xid_str); if (xprop_proc == NULL) { g_critical ("unable to monitor screensaver window: %s", error->message); } else { g_debug ("Monitoring screensaver window (0x%lx)", xid); g_subprocess_wait (xprop_proc, cancellable, &error); if (error != NULL && error->code != G_IO_ERROR_CANCELLED) { g_critical ("problem with screensaver window monitor: %s", error->message); } } g_clear_error (&error); g_task_return_boolean (task, TRUE); } static void screensaver_window_gone (GObject *source, GAsyncResult *result, gpointer user_data) { BackupWindow *window = BACKUP_WINDOW (user_data); GCancellable *task_cancellable = g_task_get_cancellable (G_TASK (result)); gulong xid = GDK_POINTER_TO_XID (g_task_get_task_data (G_TASK (result))); g_task_propagate_boolean (G_TASK (result), NULL); // The normal screensaver window is gone - either thru a crash or normal unlocking. // The main process will kill us, or the user will have to. Either way, grab everything. if (!g_cancellable_is_cancelled (task_cancellable)) { g_mutex_lock (&pretty_xid_mutex); g_debug ("Screensaver window gone: 0x%lx", xid); if (xid == window->pretty_xid) { window->should_grab = TRUE; window->pretty_xid = 0; activate_backup_window (window); } else { g_debug ("Already have new screensaver window, not activating ourselves: 0x%lx", window->pretty_xid); } g_mutex_unlock (&pretty_xid_mutex); } g_clear_object (&task_cancellable); } static void setup_window_monitor (BackupWindow *window, gulong xid) { GTask *task; g_debug ("Beginning to monitor screensaver window 0x%lx", xid); g_mutex_lock (&pretty_xid_mutex); window->should_grab = FALSE; window->pretty_xid = xid; window_monitor_cancellable = g_cancellable_new (); task = g_task_new (NULL, window_monitor_cancellable, screensaver_window_gone, window); g_task_set_return_on_cancel (task, TRUE); g_task_set_task_data (task, GDK_XID_TO_POINTER (xid), NULL); g_task_run_in_thread (task, window_monitor_thread); g_object_unref (task); g_mutex_unlock (&pretty_xid_mutex); } static void quit (BackupWindow *window) { g_clear_handle_id (&sigterm_src_id, g_source_remove); g_cancellable_cancel (window_monitor_cancellable); gtk_widget_destroy (GTK_WIDGET (window)); gtk_main_quit (); } static gboolean sigterm_received (gpointer data) { g_debug("SIGTERM received, cleaning up."); g_return_val_if_fail (BACKUP_IS_WINDOW (data), G_SOURCE_REMOVE); quit (BACKUP_WINDOW (data)); return G_SOURCE_REMOVE; } int main (int argc, char **argv) { GtkWidget *window; GError *error; static gboolean show_version = FALSE; gchar *xid_str, *term_tty_str, *session_tty_str; const GOptionEntry entries [] = { { "xid", 0, 0, G_OPTION_ARG_STRING, &xid_str, "Window ID to monitor", NULL }, { "term", 0, 0, G_OPTION_ARG_STRING, &term_tty_str, "Terminal tty number", NULL }, { "session", 0, 0, G_OPTION_ARG_STRING, &session_tty_str, "Session tty number", NULL }, { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, "Version of this application", NULL }, { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Enable debugging code", NULL }, { NULL } }; bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error)) { if (error) { g_warning ("%s", error->message); g_error_free (error); } else { g_warning ("Unable to initialize GTK+"); } exit (1); } if (debug) { g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); } if (show_version) { g_print ("%s %s\n", g_get_prgname (), VERSION); exit (1); } g_debug ("backup-locker: initializing"); gulong xid = term_tty = session_tty = 0; xid = strtoul (xid_str, NULL, 0); term_tty = strtoul (term_tty_str, NULL, 0); session_tty = strtoul (session_tty_str, NULL, 0); if (xid == 0) { g_warning ("Couldn't parse screensaver XID argument"); exit(1); } window = backup_window_new (xid); if (window == NULL) { g_critical ("No backup window"); exit(1); } sigterm_src_id = g_unix_signal_add (SIGTERM, (GSourceFunc) sigterm_received, window); gtk_main (); g_debug ("backup-locker: exit"); return 0; } cinnamon-screensaver-6.2.0/backup-locker/cinnamon-unlock-desktop0000664000175000017500000000007714632071776024013 0ustar fabiofabio#!/bin/sh killall cs-backup-locker killall cinnamon-screensavercinnamon-screensaver-6.2.0/doc/0000775000175000017500000000000014632071776015345 5ustar fabiofabiocinnamon-screensaver-6.2.0/doc/dbus-interface.html0000664000175000017500000003724614632071776021142 0ustar fabiofabioCinnamon Screensaver 1.8.0 Documentation

Cinnamon Screensaver 1.8.0 Documentation

William Jon McCann


            
          

Version 1.8.0


Chapter 1. DBUS Interface

This API is currently unstable and is likely to change in the future.

Introduction

Cinnamon Screensaver exposes a DBUS API for programs to obtain information about the screensaver state and to interact with the screensaver in limited ways.

The following constants are used to uniquely refer to the CinnamonScreensaver object when making DBUS method calls:

DBUS Service:org.cinnamon.ScreenSaver
DBUS Object Path:/org.cinnamon.ScreenSaver
DBUS Interface:org.cinnamon.ScreenSaver

Methods

These are the DBUS methods.

Lock

Request that the screen be locked.

DirectionTypeDescription
instringthe away message

Cycle

Request that the screen saver theme be restarted and, if applicable, switch to the next one in the list.

SimulateUserActivity

Simulate user activity. If the screensaver is activated this will attempt to deactivate and authentication will be requested if necessary. If the screensaver is not activated then the idle timers will be reset.

Throttle

Request that running themes while the screensaver is active be blocked until UnThrottle is called or the calling process exits.

DirectionTypeDescription
instringthe application name, e.g. "gnome-power-manager"
instringthe localized reason to inhibit, e.g. "on battery power"
outunsigned integerthe cookie

A cookie is a random, unique, non-zero UINT32 used to identify the throttle request.

UnThrottle

Cancel a previous call to Throttle() identified by the cookie.

DirectionTypeDescription
inunsigned integerthe cookie

SetActive

Request a change in the state of the screensaver. Set to TRUE to request that the screensaver activate. Active means that the screensaver has blanked the screen and may run a graphical theme. This does not necessary mean that the screen is locked.

DirectionTypeDescription
inbooleanTRUE to request activation, FALSE to request deactivation

GetActive

Returns the value of the current state of activity. See SetActive().

DirectionTypeDescription
outbooleanActivation state

GetActiveTime

Returns the number of seconds that the screensaver has been active. Returns zero if the screensaver is not active.

DirectionTypeDescription
outunsigned integerActive time in seconds

GetSessionIdle

Returns the value of the current state of session idleness.

DirectionTypeDescription
outbooleanIf the session is idle

GetSessionIdleTime

Returns the number of seconds that the session has been idle. Returns zero if the session is not idle.

DirectionTypeDescription
outunsigned integerIdle time in seconds

Signals

These are the DBUS signals.

ActiveChanged

See method GetActive().

DirectionTypeDescription
outbooleanReturns the value of the current state of activity.

SessionIdleChanged

See method GetActive().

DirectionTypeDescription
outbooleanReturns the value of the current state of activity.

AuthenticationRequestBegin

Emitted before an authentication request

AuthenticationRequestEnd

Emitted after an authentication request

Examples

You can get the number of seconds the screensaver has been active by running the following:

dbus-send --session \
          --dest=org.cinnamon.ScreenSaver \
          --type=method_call \
          --print-reply \
          --reply-timeout=20000 \
          /org/cinnamon/ScreenSaver \
          org.cinnamon.ScreenSaver.GetSessionIdleTime
    

You can activate the screensaver like so:

dbus-send --session \
          --dest=org.cinnamon.ScreenSaver \
          --type=method_call \
          --print-reply \
          --reply-timeout=20000 \
          /org/cinnamon/ScreenSaver \
          org.cinnamon.ScreenSaver.SetActive \
          boolean:true
    

You can monitor screensaver changes:

dbus-monitor --session \
          "type='signal',interface='org.cinnamon.ScreenSaver'"
    

Or watch for a specific screensaver signal:

dbus-monitor --session \
          "type='signal',interface='org.cinnamon.ScreenSaver',member='SessionIdleChanged'"
    
cinnamon-screensaver-6.2.0/meson_options.txt0000664000175000017500000000114714632071776020240 0ustar fabiofabiooption('setres', type : 'boolean', value : false, description: 'Use setresuid/setresgid in the setuid.c helper') option('locking', type : 'boolean', value : true, description: 'Compile in support for locking the display') option('xinerama', type : 'boolean', value : true, description: 'Use of the Xinerama extension') option('pam-prefix', type : 'string', value : '', description: 'specify where pam files go') option('use-debian-pam', type : 'boolean', value : false, description: 'use the debian pam file') option('deprecated-warnings', type : 'boolean', value : false, description: 'show deprecated warnings') cinnamon-screensaver-6.2.0/src/0000775000175000017500000000000014632071776015367 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/cinnamon-screensaver.css0000664000175000017500000001315014632071776022221 0ustar fabiofabio.csstage { } .csstage .unlockbox { color: #eeeeee; font-size: 20px; text-shadow: 1px 1px alpha(black, 0.8); } .csstage .clock { color: #eeeeee; text-shadow: 1px 1px alpha(black, 0.8); } .csstage .toppanel { border-color: alpha(white, .2); border-style: solid; color: white; background-color: transparent; background-image: linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)); } .csstage .audiopanel { border-width: 0 1px 1px 0; border-radius: 0 0 3px 0; background-color: rgba(255, 255, 255, .05); } .csstage .infopanel { border-width: 0 0 1px 1px; border-radius: 0 0 0 3px; background-color: rgba(255, 255, 255, .05); } .csstage .notificationwidget, .csstage .powerwidget { font-size: 12px; font-weight: bold; color: white; background-color: transparent; background-image: none; text-shadow: 1px 1px alpha(black, 0.8); padding: 6px; } .csstage .auth-message { font-size: 15px; color: red; } .csstage .caps-message { font-size: 15px; color: orange; } .csstage .framedimage { border-radius: 4px; border: 4px solid; background-color: alpha(grey, .25); background-clip: border-box; border-color: @theme_selected_bg_color; box-shadow: 1px 1px alpha(black, 0.8); } .csstage .passwordentry { font-size: 15px; box-shadow: none; border-color: alpha(white, .2); border-style: solid; color: white; background-image: none; background-color: transparent; border-image: none; border-width: 1px; box-shadow: none; } .csstage .passwordentry progress { margin: 2px; padding: 0; border-radius: 0; border-width: 0 0 2px; border-color: alpha(@theme_selected_bg_color, .7); } .csstage .passwordentry progress:focus { background-color: transparent; } .csstage .passwordentry image.left { padding-right: 10px; } /* Use :backdrop for alt-text keyboard layout */ .csstage .passwordentry:backdrop { font-family: monospace; font-size: 14px; color: @theme_selected_bg_color; } .csstage .transparentbutton { -gtk-icon-style: requested; border-color: alpha(white, .2); border-image: none; box-shadow: none; background-image: none; background-color: transparent; border-radius: 20px; -gtk-outline-radius: 20px; outline-color: transparent; color: alpha(white, .7); border-width: 1px; padding: 4px; } .csstage .transparentbutton image { color: alpha(white, .7); } .csstage .transparentbutton:disabled image { color: #333333; } .csstage .passwordentry:focus, .csstage .transparentbutton:focus { background-color: alpha(grey, .3); border-color: @theme_selected_bg_color; box-shadow: 1px 1px alpha(white, 0.1); } .csstage .passwordentry:hover, .csstage .transparentbutton:hover { border-color: alpha(white, .2); background-color: alpha(grey, .2); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .passwordentry:hover:focus, .csstage .transparentbutton:hover:focus { border-color: @theme_selected_bg_color; background-color: alpha(grey, .4); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .passwordentry:active, .csstage .transparentbutton:active { border-color: @theme_selected_bg_color; background-color: alpha(grey, .6); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .passwordentry:active:focus, .csstage .transparentbutton:active:focus { border-color: @theme_selected_bg_color; background-color: alpha(grey, .9); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .volumeslider { min-height: 24px; min-width: 100px; background-color: rgba(255, 255, 255, .1); color: @theme_selected_bg_color; padding: 3px 0px 3px 0px; } .csstage scale.volumeslider slider { min-height: 24px; min-width: 4px; margin: -9px; } .csstage .volumeslider:disabled { background-color: alpha(white, .5); } .csstage .trackname { font-family: ubuntu; font-size: 14px; text-shadow: 1px 1px alpha(black, 0.8); background-image: none; background-color: transparent; } .csstage .albumartist { font-family: ubuntu; font-size: 10px; text-shadow: 1px 1px alpha(black, 0.8); background-image: none; background-color: transparent; } .csstage GtkViewport { background-color: transparent; background-image: none; } .csstage .osk-button { -gtk-icon-style: requested; border-color: alpha(white, .2); border-image: none; box-shadow: none; background-image: none; background-color: transparent; border-radius: 4px; -gtk-outline-radius: 4px; outline-color: transparent; color: alpha(white, .7); border-width: 1px; padding: 4px; } .csstage .osk-button image { color: alpha(white, .7); } .csstage .osk-button:disabled image { color: #333333; } .csstage .osk-button:focus { background-color: alpha(grey, .3); border-color: @theme_selected_bg_color; box-shadow: 1px 1px alpha(white, 0.1); } .csstage .osk-button:hover { border-color: alpha(white, .2); background-color: alpha(grey, .2); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .osk-button:hover:focus { border-color: @theme_selected_bg_color; background-color: alpha(grey, .4); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .osk-button:active { border-color: @theme_selected_bg_color; background-color: alpha(grey, .5); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .osk-button:active:focus { border-color: @theme_selected_bg_color; background-color: alpha(grey, .55); box-shadow: 1px 1px alpha(white, 0.1); } .csstage .osk-popover { background-color: black; } cinnamon-screensaver-6.2.0/src/constants.py0000664000175000017500000000123214632071776017753 0ustar fabiofabio#!/usr/bin/python3 # Idle time in seconds before the unlock dialog will disappear and we go back to sleep. UNLOCK_TIMEOUT = 30 # Time in ms to wait before releasing the keyboard and mouse grabs # after an idle-activation is canceled. GRAB_RELEASE_TIMEOUT = 1 * 1000 # Used by powerWidget - the level a battery must be below before the battery icon widget in the infopanel # will show even when asleep (active but not awake.) BATTERY_CRITICAL_PERCENT = 20 # Cinnamon Screensaver SS_SERVICE = "org.cinnamon.ScreenSaver" SS_PATH = "/org/cinnamon/ScreenSaver" SS_INTERFACE = "org.cinnamon.ScreenSaver" cinnamon-screensaver-6.2.0/src/meson.build0000664000175000017500000000312014632071776017525 0ustar fabiofabiosubdir('dbusdepot') subdir('pamhelper') subdir('util') subdir('widgets') # will not subdir the binfiles directory as it only contains symlinked scripts config_py = configure_file( output: 'config.py', input: 'config.py.in', # meson 0.49 #configuration: { # 'prefix': prefix, # 'datadir': datadir, # 'pkgdatadir': pkgdatadir, # 'libexecdir': libexecdir, # 'libdir': libdir, # 'PACKAGE': meson.project_name(), # 'VERSION': meson.project_version(), # 'GETTEXT_PACKAGE': meson.project_name() #} configuration: misc_conf ) app_py = [ '__init__.py', 'albumArt.py', 'audioPanel.py', 'baseWindow.py', 'clock.py', config_py, 'constants.py', 'floating.py', 'infoPanel.py', 'manager.py', 'monitorView.py', 'osk.py', 'passwordEntry.py', 'playerControl.py', 'service.py', 'singletons.py', 'stage.py', 'status.py', 'unlock.py', 'volumeControl.py', ] app_css = [ 'cinnamon-screensaver.css', ] install_data(app_py + app_css, install_dir: pkgdatadir ) app_scripts = [ ['cinnamon-screensaver-main.py', 'cinnamon-screensaver'], ['cinnamon-screensaver-command.py', 'cinnamon-screensaver-command'] ] foreach script : app_scripts prefix_info = configuration_data() prefix_info.set('install_dir', pkgdatadir) prefix_info.set('target', script[0]) bin_file = configure_file( input : 'binfile.in', output: script[1], configuration: prefix_info, install_dir: get_option('bindir'), install_mode: 'rwxr-xr-x' ) install_data( script[0], install_dir: pkgdatadir, install_mode: 'rwxr-xr-x' ) endforeach cinnamon-screensaver-6.2.0/src/infoPanel.py0000664000175000017500000000606214632071776017660 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk import status from util import utils, trackers, settings from baseWindow import BaseWindow from widgets.notificationWidget import NotificationWidget from widgets.powerWidget import PowerWidget class InfoPanel(BaseWindow): """ Upper right corner panel - contains the notification counter and any battery indicator(s) - this panel will generally show if it has anything relevant to say, regardless of our Awake state. """ def __init__(self): super(InfoPanel, self).__init__() self.monitor_index = status.screen.get_primary_monitor() self.update_geometry() if not settings.get_show_info_panel(): self.disabled = True return self.show_power = False self.show_notifications = False self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.box.set_halign(Gtk.Align.FILL) self.box.get_style_context().add_class("toppanel") self.box.get_style_context().add_class("infopanel") self.add(self.box) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.pack_start(hbox, False, False, 6) self.notification_widget = NotificationWidget() self.notification_widget.set_no_show_all(True) hbox.pack_start(self.notification_widget, True, True, 2) self.notification_widget.connect("notification", self.on_notification_received) self.power_widget = PowerWidget() self.power_widget.set_no_show_all(True) self.power_widget.connect("power-state-changed",self.on_power_state_changed) hbox.pack_start(self.power_widget, True, True, 2) self.show_all() def refresh_power_state(self): if self.disabled: return self.power_widget.refresh() def on_notification_received(self, obj): self.update_visibility() def on_power_state_changed(self, obj): self.update_visibility() def update_visibility(self): """ Determines whether or not to show the panel, depending on: - Whether the power widget should show (are we on battery?) - Whether the notification widget should show (are there any?) The panel will show if either of its child indicators has useful info. """ if self.disabled: return do_show = False battery_critical = False self.show_power = self.power_widget.should_show() if self.show_power: battery_critical = self.power_widget.battery_critical self.show_notifications = self.notification_widget.should_show() # Determine if we want to show all the time or only when status.Awake if status.Awake: if self.show_power or self.show_notifications: do_show = True elif self.show_notifications or battery_critical: do_show = True self.set_visible(do_show) self.power_widget.set_visible(self.show_power) self.notification_widget.set_visible(self.show_notifications) cinnamon-screensaver-6.2.0/src/volumeControl.py0000664000175000017500000001277214632071776020622 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('Cvc', '1.0') from gi.repository import Gtk, Cvc, Gdk from widgets.volumeSlider import VolumeSlider from util import trackers, utils class VolumeControl(Gtk.Box): """ A direct child of the AudioPanel, it provides the volume slider. This shows any time we are Awake, regardless of any media player connections. We use libcvc to interact with the audio subsystem. """ def __init__(self): super(VolumeControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.output = None self.controller = None self.volume_slider = VolumeSlider() self.volume_slider.show_all() self.set_no_show_all(True) self.pack_start(self.volume_slider, False, False, 6) self.initialize_sound_controller() def initialize_sound_controller(self): self.controller = Cvc.MixerControl(name="cinnamon-screensaver") trackers.con_tracker_get().connect(self.controller, "state-changed", self.on_state_changed) trackers.con_tracker_get().connect(self.controller, "default-sink-changed", self.on_state_changed) self.controller.open() self.on_state_changed() def on_state_changed(self, controller=None, state=0): if controller and controller != self.controller: old = self.controller self.controller = controller del old if self.controller.get_state() == Cvc.MixerControlState.READY: new = self.controller.get_default_sink() if new is not None: if self.output and self.output != new: old = self.output self.output = new del old else: self.output = new trackers.con_tracker_get().connect(self.output, "notify::is-muted", self.on_volume_changed) trackers.con_tracker_get().connect(self.output, "notify::volume", self.on_volume_changed) trackers.con_tracker_get().connect(self.volume_slider, "value-changed", self.on_volume_slider_changed) trackers.con_tracker_get().connect(self.volume_slider, "button-press-event", self.on_button_press_event) self.on_volume_changed(None, None) self.show() return self.hide() def on_volume_changed(self, output, pspec): vol = self.output.props.volume muted = self.output.get_is_muted() max_vol = self.controller.get_vol_max_norm() normalized_volume = int(min((vol / max_vol * 100), 100)) self.update_slider(normalized_volume, muted) def update_slider(self, volume, muted): trackers.con_tracker_get().handler_block(self.volume_slider, "value-changed", self.on_volume_slider_changed) self.volume_slider.set_muted(muted) self.volume_slider.set_value(volume) trackers.con_tracker_get().handler_unblock(self.volume_slider, "value-changed", self.on_volume_slider_changed) def on_volume_slider_changed(self, range, data=None): value = self.volume_slider.get_value() max_norm = self.controller.get_vol_max_norm() denormalized_volume = utils.CLAMP((value / 100) * max_norm, 0, max_norm) trackers.con_tracker_get().handler_block(self.output, "notify::volume", self.on_volume_changed) trackers.con_tracker_get().handler_block(self.output, "notify::is-muted", self.on_volume_changed) self.output.set_volume(denormalized_volume) self.output.push_volume() self.output.change_is_muted(False) trackers.con_tracker_get().handler_unblock(self.output, "notify::volume", self.on_volume_changed) trackers.con_tracker_get().handler_unblock(self.output, "notify::is-muted", self.on_volume_changed) def on_button_press_event(self, widget, event): if event.button == 2: self.output.set_is_muted(not self.volume_slider.muted) return Gdk.EVENT_PROPAGATE def on_scroll_event(self, widget, event): success, dx, dy = event.get_scroll_deltas() step = self.volume_slider.get_adjustment().get_step_increment() if dy < 0: self.volume_slider.set_value(self.volume_slider.get_value() + step) elif dy > 0: self.volume_slider.set_value(self.volume_slider.get_value() - step) return Gdk.EVENT_STOP cinnamon-screensaver-6.2.0/src/status.py0000664000175000017500000000240114632071776017261 0ustar fabiofabio#!/usr/bin/python3 # Our global state vars Active = False # Screensaver visible or not - False when it's completely idle. Locked = False # Independent of Active, whether the unlock dialog will show when we become Awake. Awake = False # Whether the unlock dialog is visible or not. # A list of focusable widgets that the user can tab between in the unlock screen. See FocusNavigator. focusWidgets = [] # This is different than the preference that turns off locking - that only prevents idle locking. The # user can still lock explicitly. The function checks for the existence of correct PAM files, # as well as adjusting the UID if this process is started as root. LockEnabled = True UseFallback = True # Enables extra PAM/authentication/notification debugging # TODO: We do a *lot* of logging now, we should just use a debug() function that checks # for debug mode internally, instead of 'if status.Debug' everywhere. Debug = False # Forces the Stage to only cover a single monitor and launch a GtkInspector window. InteractiveDebug = False # If the wallpaper aspect is 'spanned' we will only create one MonitorView and manage it slightly # differently. This is an easy place to keep track of that. This is set in singletons.py. Spanned = False screen = None cinnamon-screensaver-6.2.0/src/clock.py0000664000175000017500000001332614632071776017041 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import CinnamonDesktop, GLib, Gtk, Gio, Pango from util import utils, trackers, settings from baseWindow import BaseWindow from floating import Floating MAX_WIDTH = 320 MAX_WIDTH_LOW_RES = 200 class ClockWidget(Floating, BaseWindow): """ ClockWidget displays the time and away message on the screen. It is a child of the Stage's GtkOverlay, and its placement is controlled by the overlay's child positioning function. When not Awake, it positions itself around all monitors using a timer which randomizes its halign and valign properties as well as its current monitor. """ def __init__(self, away_message=None, initial_monitor=0, low_res=False): super(ClockWidget, self).__init__(initial_monitor) self.get_style_context().add_class("clock") self.set_halign(Gtk.Align.START) self.set_property("margin", 6) self.clock = None self.low_res = low_res if not settings.get_show_clock(): return self.away_message = away_message box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.add(box) box.show() self.label = Gtk.Label() self.label.show() self.label.set_line_wrap(True) self.label.set_alignment(0.5, 0.5) box.pack_start(self.label, True, False, 6) self.msg_label = Gtk.Label() self.msg_label.show() self.msg_label.set_line_wrap(True) self.msg_label.set_alignment(0.5, 0.5) if self.low_res: self.msg_label.set_max_width_chars(50) else: self.msg_label.set_max_width_chars(80) box.pack_start(self.msg_label, True, True, 6) self.clock = CinnamonDesktop.WallClock() self.set_clock_format() trackers.con_tracker_get().connect(self.clock, "notify::clock", self.on_clock_changed) tz = Gio.File.new_for_path(path="/etc/localtime") self.tz_monitor = tz.monitor_file(Gio.FileMonitorFlags.NONE, None) trackers.con_tracker_get().connect(self.tz_monitor, "changed", self.on_tz_changed) trackers.con_tracker_get().connect(self, "destroy", self.on_destroy) self.update_clock() def set_clock_format(self): date_format = "" time_format = "" if settings.get_use_custom_format(): date_format = settings.get_custom_date_format() time_format = settings.get_custom_time_format() else: date_format = self.clock.get_default_date_format() time_format = self.clock.get_default_time_format() # %l is 12-hr hours, but it adds a space to 0-9, which looks bad here. # The '-' modifier tells the GDateTime formatter not to pad the value. time_format = time_format.replace('%l', '%-l') time_font = Pango.FontDescription.from_string(settings.get_time_font()) date_font = Pango.FontDescription.from_string(settings.get_date_font()) if self.low_res: time_size = time_font.get_size() * .66 date_size = date_font.get_size() * .66 time_font.set_size(int(time_size)) date_font.set_size(int(date_size)) time_format = ('%s\n' + \ '%s') \ % (time_font.to_string(), time_format, date_font.to_string(), date_format) self.clock.set_format_string(time_format) def on_clock_changed(self, clock, pspec): self.update_clock() def on_tz_changed(self, monitor, file, other, event): self.update_clock() def update_clock(self): default_message = GLib.markup_escape_text (settings.get_default_away_message(), -1) font_message = Pango.FontDescription.from_string(settings.get_message_font()) if self.low_res: msg_size = font_message.get_size() * .66 font_message.set_size(int(msg_size)) if self.away_message and self.away_message != "": user_name = utils.get_user_display_name() markup = ('' + '{1}' + '\n ~ {2}' + '\n ').format( font_message.to_string(), self.away_message, user_name) else: markup = '%s\n ' %\ (font_message.to_string(), default_message) self.label.set_markup(self.clock.get_clock()) self.msg_label.set_markup(markup) def set_message(self, msg=""): if not self.clock: return self.away_message = msg self.update_clock() def on_destroy(self, data=None): trackers.con_tracker_get().disconnect(self.clock, "notify::clock", self.on_clock_changed) trackers.con_tracker_get().disconnect(self.tz_monitor, "changed", self.on_tz_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_destroy) self.clock = None self.tz_monitor = None cinnamon-screensaver-6.2.0/src/playerControl.py0000664000175000017500000001470314632071776020603 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('Cvc', '1.0') from gi.repository import Gtk import datetime from util import trackers, utils from dbusdepot.mediaPlayerWatcher import PlaybackStatus from widgets.marqueeLabel import MarqueeLabel from widgets.transparentButton import TransparentButton import singletons import status class PlayerControl(Gtk.Box): """ Provides info and controls for any active music or other media player. It is a a direct child of the AudioPanel, and is only shown if there is an active mpris interface we can connect to. """ def __init__(self): super(PlayerControl, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() if self.player: self.build_layout() def build_layout(self): player_status = self.player.get_playback_status() # Player buttons button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button_box.set_homogeneous(True) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 2) vbox.pack_start(button_box, True, True, 0) vbox.set_valign(Gtk.Align.CENTER) self.previous_button = TransparentButton("media-skip-backward-symbolic", Gtk.IconSize.BUTTON) self.previous_button.show() trackers.con_tracker_get().connect(self.previous_button, "clicked", self.on_previous_clicked) button_box.pack_start(self.previous_button, True, True, 2) self.play_pause_button = TransparentButton(self.get_play_pause_icon_name(player_status), Gtk.IconSize.BUTTON) self.play_pause_button.show() trackers.con_tracker_get().connect(self.play_pause_button, "clicked", self.on_play_pause_clicked) button_box.pack_start(self.play_pause_button, True, True, 2) self.next_button = TransparentButton("media-skip-forward-symbolic", Gtk.IconSize.BUTTON) self.next_button.show() trackers.con_tracker_get().connect(self.next_button, "clicked", self.on_next_clicked) button_box.pack_start(self.next_button, True, True, 2) self.update_buttons(player_status) status.focusWidgets = status.focusWidgets + [self.previous_button, self.play_pause_button, self.next_button] # Track info vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.pack_start(vbox, True, True, 6) vbox.set_valign(Gtk.Align.CENTER) self.track_name_label = MarqueeLabel("") self.track_name_label.get_style_context().add_class("trackname") vbox.pack_start(self.track_name_label, True, True, 2) self.album_artist_label = MarqueeLabel("") self.album_artist_label.get_style_context().add_class("albumartist") vbox.pack_end(self.album_artist_label, True, True, 2) self.show_all() trackers.con_tracker_get().connect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_playback_status_changed(self.player, player_status) self.on_metadata_changed(self.player) trackers.con_tracker_get().connect(self, "destroy", self.on_widget_destroy) def on_previous_clicked(self, button, data=None): self.player.go_previous() def on_next_clicked(self, button, data=None): self.player.go_next() def on_play_pause_clicked(self, button, data=None): self.player.play_pause() def get_play_pause_icon_name(self, status): if status == PlaybackStatus.Playing: icon_name = "media-playback-pause-symbolic" else: icon_name = "media-playback-start-symbolic" return icon_name def on_playback_status_changed(self, player, status, data=None): self.update_buttons(status) def on_metadata_changed(self, player): """ Update labels when the player metadata changes """ self.update_labels() def update_labels(self): """ Construct the track and artist-album labels as well as possible. """ self.track_name_label.set_text(self.player.get_track_name()) artist_name = self.player.get_artist_name() album_name = self.player.get_album_name() if artist_name != "" and album_name != "": self.album_artist_label.set_text("%s - %s" % (self.player.get_artist_name(), self.player.get_album_name())) elif artist_name != "": self.album_artist_label.set_text(artist_name) elif album_name != "": self.album_artist_label.set_text(album_name) else: self.album_artist_label.set_text("") def update_buttons(self, status): """ Updates the player buttons based on the current state """ self.play_pause_button.set_sensitive(self.player.get_can_play_pause()) self.next_button.set_sensitive(self.player.get_can_go_next()) self.previous_button.set_sensitive(self.player.get_can_go_previous()) icon_name = self.get_play_pause_icon_name(status) image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON) self.play_pause_button.set_image(image) def on_widget_destroy(self, widget, data=None): trackers.con_tracker_get().disconnect(self.player, "status-changed", self.on_playback_status_changed) trackers.con_tracker_get().disconnect(self, "destroy", self.on_widget_destroy) def should_show(self): """ Checked by the AudioPanel, whether or not this widget should be displayed. """ return self.player is not None cinnamon-screensaver-6.2.0/src/pamhelper/0000775000175000017500000000000014632071776017344 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/pamhelper/meson.build0000664000175000017500000000051114632071776021503 0ustar fabiofabioapp_py = [ 'authClient.py', ] install_data(app_py, install_dir: join_paths(pkgdatadir, 'pamhelper')) executable('cinnamon-screensaver-pam-helper', 'cinnamon-screensaver-pam-helper.c', dependencies: [gio_unix, glib], include_directories: inc, link_with: libcscreensaver, install: true, install_dir: libexecdir ) cinnamon-screensaver-6.2.0/src/pamhelper/authClient.py0000664000175000017500000002061614632071776022023 0ustar fabiofabio#!/usr/bin/python3 import re import os import signal import platform import sys from gi.repository import Gio, GObject, GLib, CScreensaver import config import status from util.utils import DEBUG class AuthClient(GObject.Object): """ The AuthClient manages spawning of our pam helper process, and communication with it. """ __gsignals__ = { 'auth-success': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-failure': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-cancel': (GObject.SignalFlags.RUN_LAST, None, ()), 'auth-busy': (GObject.SignalFlags.RUN_LAST, None, (bool,)), 'auth-prompt': (GObject.SignalFlags.RUN_LAST, None, (str,)), 'auth-info': (GObject.SignalFlags.RUN_LAST, None, (str,)) } def __init__(self): super(AuthClient, self).__init__() self.reset() def initialize(self): if self.initialized: return True DEBUG("authClient: attempting to initialize") self.cancellable = Gio.Cancellable() try: helper_path = None architecture = platform.machine() paths = [config.libexecdir, "/usr/lib", "/usr/lib/cinnamon-screensaver", "/usr/libexec", "/usr/libexec/cinnamon-screensaver"] # On x86 archs, iterate through multiple paths # For instance, on a Mint i686 box, the path is actually /usr/lib/i386-linux-gnu x86archs = ["i386", "i486", "i586", "i686"] if architecture in x86archs: for arch in x86archs: paths += ["/usr/lib/%s" % arch, "/usr/lib/%s-linux-gnu" % arch] elif architecture == "x86_64": paths += ["/usr/lib/x86_64", "/usr/lib/x86_64-linux-gnu", "/usr/lib64"] else: paths += ["/usr/lib/%s" % architecture, "/usr/lib/%s-linux-gnu" % architecture] for path in paths: full_path = os.path.join(path, "cinnamon-screensaver-pam-helper") if os.path.exists(full_path): helper_path = full_path break if helper_path is None: print ("authClient: critical Error: PAM Helper could not be found!") if status.Debug: argv = (helper_path, "--debug", None) self.proc = Gio.Subprocess.new(argv, Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE) else: argv = (helper_path, None) self.proc = Gio.Subprocess.new(argv, Gio.SubprocessFlags.STDIN_PIPE | Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_SILENCE) except GLib.Error as e: print("authClient: error starting cinnamon-screensaver-pam-helper: %s" % e.message) return False self.proc.wait_check_async(self.cancellable, self.on_proc_completed, None) self.out_pipe = self.proc.get_stdout_pipe() self.out_pipe.read_bytes_async(1024, GLib.PRIORITY_DEFAULT, self.cancellable, self.message_from_child) self.in_pipe = self.proc.get_stdin_pipe() self.initialized = True DEBUG("authClient: initialized (helper pid %s)" % self.proc.get_identifier ()) return True def reset(self): self.initialized = False self.cancellable = None self.proc = None self.in_pipe = None self.out_pipe = None def cancel(self): self.end_proc() def end_proc(self): if self.cancellable is None: return self.cancellable.cancel() if self.proc is not None: DEBUG("authClient: cancel requested, killing helper.") self.proc.send_signal(signal.SIGTERM) else: DEBUG("authClient: cancel requested, but no helper process") self.reset() def on_proc_completed(self, proc, res, data=None): DEBUG("authClient: helper process (pid %s) completed..." % proc.get_identifier()) try: ret = proc.wait_check_finish(res) except GLib.Error as e: if status.Debug and e.code != Gio.IOErrorEnum.CANCELLED: print("helper process did not exit cleanly: %s" % e.message) pipe = proc.get_stdin_pipe() if pipe is not None: pipe.clear_pending() try: pipe.close(None) except GLib.Error as e: DEBUG("helper process did not close in_pipe cleanly: %s" % e.message) pipe = proc.get_stdout_pipe() if pipe is not None: pipe.clear_pending() try: pipe.close(None) except GLib.Error as e: DEBUG("helper process did not close out_pipe cleanly: %s" % e.message) # Don't just reset - if another proc has been started we don't want to interfere. if self.proc == proc: self.reset() def message_to_child(self, string): if not self.initialized: return if self.cancellable is None or self.cancellable.is_cancelled(): return DEBUG("authClient: message to child") try: b = GLib.Bytes.new(string.encode()) try: s = self.in_pipe.write_bytes(b) except GLib.Error as e: if e.code != Gio.IOErrorEnum.CANCELLED: print("Error reading message from pam helper") return self.in_pipe.flush(None) except GLib.Error as e: if e.code != Gib.IOErrorEnum.CANCELLED: print("Error writing to pam helper: %s" % e.message) def message_from_child(self, pipe, res): if self.cancellable is None or self.cancellable.is_cancelled(): return terminate = False try: bytes_read = pipe.read_bytes_finish(res) except GLib.Error as e: if e.code != Gio.IOErrorEnum.CANCELLED: print("Error reading message from pam helper: %s" % e.message) return if bytes_read: raw_string = bytes_read.get_data().decode() lines = raw_string.split("\n") for output in lines: DEBUG("Output from pam helper: '%s'" % output) if output: if "CS_PAM_AUTH_FAILURE" in output: self.emit_idle_failure() if "CS_PAM_AUTH_SUCCESS" in output: self.emit_idle_success() terminate = True if "CS_PAM_AUTH_CANCELLED" in output: self.emit_idle_cancel() terminate = True if "CS_PAM_AUTH_BUSY_TRUE" in output: self.emit_idle_busy_state(True) if "CS_PAM_AUTH_BUSY_FALSE" in output: self.emit_idle_busy_state(False) if "CS_PAM_AUTH_SET_PROMPT" in output: prompt = re.search('(?<=CS_PAM_AUTH_SET_PROMPT_)(.*)(?=_)', output).group(0) self.emit_idle_auth_prompt(prompt) if "CS_PAM_AUTH_SET_INFO" in output: info = re.search('(?<=CS_PAM_AUTH_SET_INFO_)(.*)(?=_)', output).group(0) self.emit_auth_info(info) if terminate: self.end_proc() return pipe.read_bytes_async(1024, GLib.PRIORITY_DEFAULT, None, self.message_from_child) def emit_idle_busy_state(self, busy): DEBUG("authClient: idle add auth-busy") GObject.idle_add(self.emit, "auth-busy", busy) def emit_idle_failure(self): DEBUG("authClient: idle add failure") GObject.idle_add(self.emit, "auth-failure") def emit_idle_success(self): DEBUG("authClient: idle add success") GObject.idle_add(self.emit, "auth-success") def emit_idle_cancel(self): DEBUG("authClient: idle add cancel") GObject.idle_add(self.emit, "auth-cancel") def emit_idle_auth_prompt(self, prompt): DEBUG("authClient: idle add auth-prompt") GObject.idle_add(self.emit, "auth-prompt", prompt) def emit_auth_info(self, info): DEBUG("authClient: auth-info") GObject.idle_add(self.emit, "auth-info", info) cinnamon-screensaver-6.2.0/src/pamhelper/cinnamon-screensaver-pam-helper.c0000664000175000017500000003314214632071776025663 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2004-2006 William Jon McCann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Authors: William Jon McCann * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #define MAX_FAILURES 5 #define DEBUG(...) if (debug_mode) g_printerr (__VA_ARGS__) static GMainLoop *ml = NULL; static gboolean debug_mode = FALSE; static guint shutdown_id = 0; static gchar *password_ptr = NULL; static GMutex password_mutex; static GCancellable *stdin_cancellable = NULL; #define CS_PAM_AUTH_FAILURE "CS_PAM_AUTH_FAILURE\n" #define CS_PAM_AUTH_SUCCESS "CS_PAM_AUTH_SUCCESS\n" #define CS_PAM_AUTH_CANCELLED "CS_PAM_AUTH_CANCELLED\n" #define CS_PAM_AUTH_BUSY_TRUE "CS_PAM_AUTH_BUSY_TRUE\n" #define CS_PAM_AUTH_BUSY_FALSE "CS_PAM_AUTH_BUSY_FALSE\n" #define CS_PAM_AUTH_SET_PROMPT_ "CS_PAM_AUTH_SET_PROMPT_" #define CS_PAM_AUTH_SET_INFO_ "CS_PAM_AUTH_SET_INFO_" #define CS_PAM_AUTH_REQUEST_SUBPROCESS_EXIT "CS_PAM_AUTH_REQUEST_SUBPROCESS_EXIT" static void send_cancelled (void); static gboolean shutdown (void) { DEBUG ("cinnamon-screensaver-pam-helper (pid %i): shutting down.\n", getpid ()); g_clear_handle_id (&shutdown_id, g_source_remove); g_clear_object (&stdin_cancellable); g_clear_pointer (&password_ptr, g_free); g_main_loop_quit (ml); return G_SOURCE_REMOVE; } static void send_failure (void) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } g_printf (CS_PAM_AUTH_FAILURE); fflush (stdout); } static void send_success (void) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } g_printf (CS_PAM_AUTH_SUCCESS); fflush (stdout); } static void send_cancelled (void) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } g_printf (CS_PAM_AUTH_CANCELLED); fflush (stdout); } static void send_busy (gboolean busy) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } if (busy) { g_printf (CS_PAM_AUTH_BUSY_TRUE); } else { g_printf (CS_PAM_AUTH_BUSY_FALSE); } fflush (stdout); } static void send_prompt (const gchar *msg) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } g_printf (CS_PAM_AUTH_SET_PROMPT_ "%s_\n", msg); fflush (stdout); } static void send_info (const gchar *msg) { if (g_cancellable_is_cancelled (stdin_cancellable)) { return; } g_printf (CS_PAM_AUTH_SET_INFO_ "%s_\n", msg); fflush (stdout); } static gboolean auth_message_handler (CsAuthMessageStyle style, const char *msg, char **response, gpointer data) { gboolean ret; DEBUG ("cinnamon-screensaver-pam-helper: Got message style %d: '%s'\n", style, msg); ret = TRUE; *response = NULL; switch (style) { case CS_AUTH_MESSAGE_PROMPT_ECHO_ON: DEBUG ("cinnamon-screensaver-pam-helper: CS_AUTH_MESSAGE_PROMPT_ECHO_ON\n"); break; case CS_AUTH_MESSAGE_PROMPT_ECHO_OFF: if (msg != NULL) { send_prompt (msg); send_busy (FALSE); while (password_ptr == NULL && !g_cancellable_is_cancelled (stdin_cancellable)) { g_main_context_iteration (g_main_context_default (), FALSE); usleep (100 * 1000); } g_mutex_lock (&password_mutex); DEBUG ("cinnamon-screensaver-pam-helper: auth_message_handler processing response string\n"); if (password_ptr != NULL) { *response = g_strdup (password_ptr); memset (password_ptr, '\b', strlen (password_ptr)); g_clear_pointer (&password_ptr, g_free); } g_mutex_unlock (&password_mutex); } break; case CS_AUTH_MESSAGE_ERROR_MSG: DEBUG ("CS_AUTH_MESSAGE_ERROR_MSG\n"); break; case CS_AUTH_MESSAGE_TEXT_INFO: DEBUG ("CS_AUTH_MESSAGE_TEXT_INFO\n"); if (msg != NULL) { send_info (msg); } break; default: g_assert_not_reached (); } if (*response == NULL) { DEBUG ("cinnamon-screensaver-pam-helper: Got no response\n"); ret = FALSE; } else { send_busy (TRUE); } /* we may have pending events that should be processed before continuing back into PAM */ while (g_main_context_pending (g_main_context_default ())) { g_main_context_iteration(g_main_context_default (), TRUE); } return ret; } static gboolean do_auth_check (void) { GError *error; gboolean res; error = NULL; res = cs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, NULL, &error); DEBUG ("cinnamon-screensaver-pam-helper: Verify user returned: %s\n", res ? "TRUE" : "FALSE"); if (!res) { if (error != NULL && !g_cancellable_is_cancelled (stdin_cancellable)) { DEBUG ("cinnamon-screensaver-pam-helper: Verify user returned error: %s\n", error->message); g_error_free (error); } } return res; } static gboolean auth_check_idle (gpointer user_data) { gboolean res; gboolean again; static guint loop_counter = 0; again = TRUE; res = do_auth_check (); if (res) { again = FALSE; send_success (); } else { loop_counter++; if (loop_counter < MAX_FAILURES) { send_failure (); DEBUG ("cinnamon-screensaver-pam-helper: Authentication failed, retrying (%u)\n", loop_counter); } else { DEBUG ("cinnamon-screensaver-pam-helper: Authentication failed, quitting (max failures)\n"); again = FALSE; /* Don't quit immediately, but rather request that cinnamon-screensaver * terminates us after it has finished the dialog shake. Time out * after 5 seconds and quit anyway if this doesn't happen though */ send_cancelled (); } } if (again) { return G_SOURCE_CONTINUE; } g_cancellable_cancel (stdin_cancellable); return G_SOURCE_REMOVE; } static void stdin_monitor_task_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GInputStream *stream = G_INPUT_STREAM (g_unix_input_stream_new (STDIN_FILENO, FALSE)); gssize size; GError *error =NULL; while (!g_cancellable_is_cancelled (cancellable)) { guint8 input[255]; // Blocks size = g_input_stream_read (stream, input, 255, cancellable, &error); if (error) { g_cancellable_cancel (cancellable); break; } g_mutex_lock (&password_mutex); if (size > 0) { if (input [size - 1] == '\n') { input [size - 1] = 0; } password_ptr = g_strdup ((gchar *) &input); memset (input, '\b', 255); } g_mutex_unlock (&password_mutex); g_usleep (1000); } g_object_unref (stream); if (error != NULL) { g_task_return_error (task, error); } } static void stdin_monitor_task_finished (GObject *source, GAsyncResult *result, gpointer user_data) { GError *error = NULL; g_task_propagate_boolean (G_TASK (result), &error); if (error != NULL) { if (error->code != G_IO_ERROR_CANCELLED) { g_critical ("cinnamon-screensaver-pam-helper: stdin monitor: Could not read input from cinnamon-screensaver: %s", error->message); } g_error_free (error); } DEBUG ("cinnamon-screensaver-pam-helper: stdin_monitor_task_finished (Cancelled: %d)\n", g_cancellable_is_cancelled (stdin_cancellable)); shutdown (); } static void setup_stdin_monitor (void) { GTask *task; stdin_cancellable = g_cancellable_new (); task = g_task_new (NULL, stdin_cancellable, stdin_monitor_task_finished, NULL); // g_task_set_return_on_cancel (task, TRUE); g_task_run_in_thread (task, stdin_monitor_task_thread); g_object_unref (task); } /* * Copyright (c) 1991-2004 Jamie Zawinski * Copyright (c) 2005 William Jon McCann * * Initializations that potentially take place as a privileged user: If the executable is setuid root, then these initializations are run as root, before discarding privileges. */ static gboolean privileged_initialization (int *argc, char **argv, gboolean verbose) { gboolean ret; char *nolock_reason; char *orig_uid; char *uid_message; #ifndef NO_LOCKING /* before hack_uid () for proper permissions */ cs_auth_priv_init (); #endif /* NO_LOCKING */ ret = hack_uid (&nolock_reason, &orig_uid, &uid_message); if (nolock_reason) { DEBUG ("cinnamon-screensaver-pam-helper: Locking disabled: %s\n", nolock_reason); } if (uid_message && verbose) { g_print ("cinnamon-screensaver-pam-helper: Modified UID: %s", uid_message); } g_free (nolock_reason); g_free (orig_uid); g_free (uid_message); return ret; } /* * Copyright (c) 1991-2004 Jamie Zawinski * Copyright (c) 2005 William Jon McCann * * Figure out what locking mechanisms are supported. */ static gboolean lock_initialization (int *argc, char **argv, char **nolock_reason, gboolean verbose) { if (nolock_reason != NULL) { *nolock_reason = NULL; } #ifdef NO_LOCKING if (nolock_reason != NULL) { *nolock_reason = g_strdup ("not compiled with locking support"); } return FALSE; #else /* !NO_LOCKING */ /* Finish initializing locking, now that we're out of privileged code. */ if (!cs_auth_init ()) { if (nolock_reason != NULL) { *nolock_reason = g_strdup ("error getting password"); } return FALSE; } #endif /* NO_LOCKING */ return TRUE; } static void response_lock_init_failed (void) { /* if we fail to lock then we should drop the dialog */ send_success (); } static gboolean handle_sigterm (gpointer data) { DEBUG ("cinnamon-screensaver-pam-helper (pid %i): SIGTERM, shutting down\n", getpid ()); g_cancellable_cancel (stdin_cancellable); return G_SOURCE_REMOVE; } int main (int argc, char **argv) { GOptionContext *context; GError *error = NULL; char *nolock_reason = NULL; g_unix_signal_add (SIGTERM, (GSourceFunc) handle_sigterm, NULL); bindtextdomain (GETTEXT_PACKAGE, "/usr/share/locale"); if (! privileged_initialization (&argc, argv, debug_mode)) { response_lock_init_failed (); exit (1); } static GOptionEntry entries [] = { { "debug", 0, 0, G_OPTION_ARG_NONE, &debug_mode, N_("Show debugging output"), NULL }, { NULL } }; context = g_option_context_new (N_("\n\nPAM interface for cinnamon-screensaver.")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_critical ("Failed to parse arguments: %s", error->message); g_error_free (error); g_option_context_free (context); exit (1); } g_option_context_free (context); if (! lock_initialization (&argc, argv, &nolock_reason, debug_mode)) { if (nolock_reason != NULL) { DEBUG ("cinnamon-screensaver-pam-helper: Screen locking disabled: %s\n", nolock_reason); g_free (nolock_reason); } response_lock_init_failed (); exit (1); } cs_auth_set_verbose (debug_mode); DEBUG ("cinnamon-screensaver-pam-helper (pid %i): start\n", getpid ()); setup_stdin_monitor (); g_idle_add ((GSourceFunc) auth_check_idle, NULL); ml = g_main_loop_new (NULL, FALSE); g_main_loop_run (ml); DEBUG ("cinnamon-screensaver-pam-helper: exit\n"); return 0; } cinnamon-screensaver-6.2.0/src/cinnamon-screensaver-main.py0000775000175000017500000001326114632071776023011 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('Gtk', '3.0') gi.require_version('GdkX11', '3.0') gi.require_version('CScreensaver', '1.0') from gi.repository import Gtk, Gdk, CScreensaver, Gio import signal import gettext import argparse import os import setproctitle import sys import config import status from util import utils, settings from service import ScreensaverService signal.signal(signal.SIGINT, signal.SIG_DFL) gettext.install("cinnamon-screensaver", "/usr/share/locale") class Main(Gtk.Application): """ This is the main entry point to the program, and it shows up in the process list. We do any theme preparation here as well. We start the ScreensaverService from here. """ def __init__(self): super(Main, self).__init__(application_id="org.cinnamon.ScreenSaver", inactivity_timeout=30000, flags=Gio.ApplicationFlags.IS_SERVICE) # Our service must be set up before we register with the session manager. ScreensaverService() def do_activate(self): pass def do_startup(self): print("Starting screensaver...", flush=True) Gtk.Application.do_startup(self) parser = argparse.ArgumentParser(description='Cinnamon Screensaver') parser.add_argument('--debug', dest='debug', action='store_true', help='Print out some extra debugging info') parser.add_argument('--interactive-debug', dest='interactive', action='store_true', help='If multiple monitors are in use, only cover one monitor, and launch GtkInspector') parser.add_argument('--disable-locking', dest='lock_disabled', action='store_true', help='Disable the lock screen') parser.add_argument('--version', dest='version', action='store_true', help='Display the current version') parser.add_argument('--hold', dest='hold', action='store_true', help="Keep the process running." \ "Normally cinnamon-screensaver will exit after being idle for 30 seconds.") parser.add_argument('--no-fallback', dest='no_fallback', action='store_true', help="Don't spawn a fallback window when locking the screen.") args = parser.parse_args() if settings.get_custom_screensaver() != '': print("custom screensaver selected, exiting cinnamon-screensaver.", flush=True) quit() if args.version: print("cinnamon-screensaver %s" % config.VERSION) quit() status.LockEnabled = not args.lock_disabled status.Debug = args.debug status.InteractiveDebug = args.interactive status.UseFallback = not args.no_fallback # The inactivity-timeout will be ignored until there's been an initial hold. Simply # starting the app and letting it idle will end up with it exiting after 10s no matter # what the timeout. self.hold() if not args.hold: self.release() if status.Debug: print("Debug mode active", flush=True) if args.lock_disabled: print("Locking disabled", flush=True) # This is here mainly to allow the notification watcher to have a valid status.Debug value import singletons Gtk.Settings.get_default().connect("notify::gtk-theme-name", self.on_theme_changed) self.do_style_overrides() def on_theme_changed(self, settings, pspec, data=None): self.do_style_overrides() def do_style_overrides(self): """ Here we try to check for theme support in the current system's gtk theme. We do this by retrieving a string of the current theme's final style sheet, then searching for the .csstage style class. If it's found, we return, otherwise we add our own application-priority provider as a fallback. While we have the theme string, we check for a variable name we can use for the fallback experience, and adjust it in our application stylesheet if necessary before adding it as a provider. """ theme_name = Gtk.Settings.get_default().get_property("gtk-theme-name") provider = Gtk.CssProvider.get_named(theme_name) css = provider.to_string() if ".csstage" not in css: print("Cinnamon Screensaver support not found in current theme - adding some...", flush=True) path = os.path.join(config.pkgdatadir, "cinnamon-screensaver.css") f = open(path, 'r') fallback_css = f.read() f.close() if "@define-color theme_selected_bg_color" in css: pass elif "@define-color selected_bg_color" in css: print("replacing theme_selected_bg_color with selected_bg_color", flush=True) fallback_css = fallback_css.replace("@theme_selected_bg_color", "@selected_bg_color") else: print("replacing theme_selected_bg_color with Adwaita blue", flush=True) fallback_css = fallback_css.replace("@selected_bg_color", "#4a90d9") fallback_prov = Gtk.CssProvider() try: fallback_prov.load_from_data(fallback_css.encode()) Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default(), fallback_prov, 600) Gtk.StyleContext.reset_widgets(Gdk.Screen.get_default()) except Exception as e: print("Could not parse fallback css: %s" % str(e)) if __name__ == "__main__": setproctitle.setproctitle('cinnamon-screensaver') main = Main() main.run() cinnamon-screensaver-6.2.0/src/audioPanel.py0000664000175000017500000000300114632071776020014 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk from baseWindow import BaseWindow from volumeControl import VolumeControl from playerControl import PlayerControl from util import utils, settings import status class AudioPanel(BaseWindow): def __init__(self): """ Upper left panel - only shows when Awake. Will always show the volume slider, and will only show the player controls if there is a controllable mpris player available. """ super(AudioPanel, self).__init__() self.monitor_index = status.screen.get_primary_monitor() self.update_geometry() if not settings.get_allow_media_control(): self.disabled = True return self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.box.set_halign(Gtk.Align.FILL) self.box.get_style_context().add_class("toppanel") self.box.get_style_context().add_class("audiopanel") self.add(self.box) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.box.pack_start(hbox, True, True, 6) self.volume_widget = VolumeControl() hbox.pack_start(self.volume_widget, False, False, 0) self.player_widget = PlayerControl() hbox.pack_start(self.player_widget, False, False, 0) should_show = self.player_widget.should_show() if not self.player_widget.should_show(): self.disabled = True def show_panel(self): if not self.disabled: self.show_all()cinnamon-screensaver-6.2.0/src/service.py0000775000175000017500000001230614632071776017406 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CScreensaver', '1.0') from gi.repository import Gtk, CScreensaver, Gio, GObject import constants as c from manager import ScreensaverManager import status from util.utils import DEBUG class ScreensaverService(GObject.Object): """ This is the dbus service we run ourselves. It is the owner of our ScreensaverManager, and implements the org.cinnamon.Screensaver dbus interface. It is through this service that the screensaver is controlled. """ def __init__(self): super(ScreensaverService, self).__init__() try: self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) except: print("Unable to get session connection, fatal!") exit(1) self.interface = CScreensaver.ScreenSaverSkeleton.new() self.interface.connect("handle-lock", self.handle_lock) self.interface.connect("handle-quit", self.handle_quit) self.interface.connect("handle-set-active", self.handle_set_active) self.interface.connect("handle-get-active", self.handle_get_active) self.interface.connect("handle-get-active-time", self.handle_get_active_time) self.interface.connect("handle-simulate-user-activity", self.handle_simulate_user_activity) self.manager = ScreensaverManager() self.manager.connect("active-changed", self.on_active_changed) """ The stage constructs itself and fades in asynchronously, and most importantly, as an idle callback. This can cause the screensaver to not be fully active when a call to suspend is made. Cinnamon-session calls to lock the screensaver synchronously, and if we don't completely finish construction before returning the dbus call completion, there's a chance the idle callback won't run until after the computer is resumed. We get an active-changed signal whenever the screensaver becomes completely active or inactive, so we'll queue up running iface.complete_lock() until we receive that signal. This allows the screensaver to be fully activated prior to cinnamon-session allowing the suspend/hibernate/whatever process to continue. For reference, this is called in cinnamon-session's csm-manager.c "manager_perhaps_lock" method. """ self.lock_queue = [] self.interface.export(self.bus, c.SS_PATH) def poke_process(self, method_name): if not status.Awake: DEBUG("service: '%s' received, poking application." % method_name) app = Gio.Application.get_default() app.hold() app.release() # Interface handlers def handle_lock(self, iface, inv, msg): """ We want to be able to respond to locking synchronously for security reasons. Things like cinnamon-settings-daemon (power) and cinnamon-session use the cinnamon-screensaver-command helper script to lock the screen during certain events like sleep, hibernate, user switching, etc... If we receive a lock request, we forward it to the manager. It returns True if we were already active (or active and locked.) If so, we can complete the invocation immediately. Otherwise, we queue the invocation, and wait for an "active-changed" signal from the manager, and then complete the invocations (in the same order they were received.) """ self.poke_process("Lock") if self.manager.lock(msg): iface.complete_lock(inv) else: self.lock_queue.append(inv) return True def handle_quit(self, iface, inv): self.manager.unlock() iface.complete_quit(inv) Gio.Application.get_default().quit() return True def handle_set_active(self, iface, inv, active): self.poke_process("SetActive") if active: self.manager.set_active(active) else: self.manager.unlock() iface.complete_set_active(inv) return True def handle_get_active(self, iface, inv): self.poke_process("GetActive") active = self.manager.get_active() iface.complete_get_active(inv, active) return True def handle_get_active_time(self, iface, inv): self.poke_process("GetActiveTime") atime = self.manager.get_active_time() iface.complete_get_active_time(inv, atime) return True def handle_simulate_user_activity(self, iface, inv): self.poke_process("SimulateUserActivity") if self.manager.is_locked(): self.manager.simulate_user_activity() else: DEBUG("Calling XResetScreenSaver") CScreensaver.Screen.reset_screensaver() iface.complete_simulate_user_activity(inv) return True def on_active_changed(self, manager, state, data=None): GObject.idle_add(self.on_active_changed_idle, state) def on_active_changed_idle(self, state): self.lock_queue.reverse() while len(self.lock_queue) > 0: invocation = self.lock_queue.pop() self.interface.complete_lock(invocation) self.lock_queue = [] self.interface.emit_active_changed(state) cinnamon-screensaver-6.2.0/src/cinnamon-screensaver-command.py0000775000175000017500000001221714632071776023503 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CScreensaver', '1.0') from gi.repository import GLib, CScreensaver, Gio import os import sys import signal import argparse import gettext import shlex from enum import IntEnum from subprocess import Popen, DEVNULL import config from util import settings import constants as c signal.signal(signal.SIGINT, signal.SIG_DFL) gettext.install("cinnamon-screensaver", "/usr/share/locale") class Action(IntEnum): EXIT = 1 QUERY = 2 TIME = 3 LOCK = 4 ACTIVATE = 5 DEACTIVATE = 6 VERSION = 7 class ScreensaverCommand: """ This is a standalone executable that provides a simple way of controlling the screensaver via its dbus interface. """ def __init__(self, mainloop): self.mainloop = mainloop self.proxy = None parser = argparse.ArgumentParser(description='Cinnamon Screensaver Command') parser.add_argument('--exit', '-e', dest="action_id", action='store_const', const=Action.EXIT, help=_('Causes the screensaver to exit gracefully')) parser.add_argument('--query', '-q', dest="action_id", action='store_const', const=Action.QUERY, help=_('Query the state of the screensaver')) parser.add_argument('--time', '-t', dest="action_id", action='store_const', const=Action.TIME, help=_('Query the length of time the screensaver has been active')) parser.add_argument('--lock', '-l', dest="action_id", action='store_const', const=Action.LOCK, help=_('Tells the running screensaver process to lock the screen immediately')) parser.add_argument('--activate', '-a', dest="action_id", action='store_const', const=Action.ACTIVATE, help=_('Turn the screensaver on (blank the screen)')) parser.add_argument('--deactivate', '-d', dest="action_id", action='store_const', const=Action.DEACTIVATE, help=_('If the screensaver is active then deactivate it (un-blank the screen)')) parser.add_argument('--version', '-V', dest="action_id", action='store_const', const=Action.VERSION, help=_('Version of this application')) parser.add_argument('--away-message', '-m', dest="message", action='store', default="", help=_('Message to be displayed in lock screen')) args = parser.parse_args() if not args.action_id: parser.print_help() quit() if args.action_id == Action.VERSION: print("cinnamon-screensaver %s" % config.VERSION) quit() self.action_id = args.action_id self.message = args.message custom_saver = settings.get_custom_screensaver() if custom_saver != '': self.handle_custom_saver(custom_saver) quit() CScreensaver.ScreenSaverProxy.new_for_bus(Gio.BusType.SESSION, Gio.DBusProxyFlags.NONE, c.SS_SERVICE, c.SS_PATH, None, self._on_proxy_ready) def handle_custom_saver(self, custom_saver): if self.action_id in [Action.LOCK, Action.ACTIVATE]: try: Popen(shlex.split(custom_saver), stdin=DEVNULL) except OSError as e: print("Error %d running %s: %s" % (e.errno, custom_saver, e.strerror)) else: print("Action not supported with custom screensaver.") def _on_proxy_ready(self, object, result, data=None): try: self.proxy = CScreensaver.ScreenSaverProxy.new_for_bus_finish(result) self.perform_action() except GLib.Error as e: print("Can't connect to screensaver: %d - %s" % (e.code, e.message)) self.mainloop.quit() def perform_action(self): if self.action_id == Action.EXIT: self.proxy.call_quit_sync() elif self.action_id == Action.QUERY: if self.proxy.call_get_active_sync(): print(_("The screensaver is active\n")) else: print(_("The screensaver is inactive\n")) elif self.action_id == Action.TIME: time = self.proxy.call_get_active_time_sync() if time == 0: print(_("The screensaver is not currently active.\n")) else: print(gettext.ngettext ("The screensaver has been active for %d second.\n", "The screensaver has been active for %d seconds.\n", time) % time) elif self.action_id == Action.LOCK: self.proxy.call_lock_sync(self.message) elif self.action_id == Action.ACTIVATE: self.proxy.call_set_active_sync(True) elif self.action_id == Action.DEACTIVATE: self.proxy.call_set_active_sync(False) self.mainloop.quit() if __name__ == "__main__": ml = GLib.MainLoop.new(None, True) main = ScreensaverCommand(ml) ml.run() cinnamon-screensaver-6.2.0/src/floating.py0000664000175000017500000000352614632071776017552 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk import random import status from util import trackers from util import settings POSITIONING_TIMEOUT = 30 ALIGNMENTS = [int(Gtk.Align.START), int(Gtk.Align.END), int(Gtk.Align.CENTER)] class Floating: def __init__(self, initial_monitor=0): super(Floating, self).__init__() self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) self.current_monitor = initial_monitor def start_positioning(self): self.show() if settings.get_allow_floating(): trackers.timer_tracker_get().cancel(str(self) + "positioning") trackers.timer_tracker_get().start_seconds(str(self) + "positioning", POSITIONING_TIMEOUT, self.positioning_callback) def stop_positioning(self): if settings.get_allow_floating(): trackers.timer_tracker_get().cancel(str(self) + "positioning") def positioning_callback(self): current_halign = int(self.get_halign()) horizontal = current_halign current_valign = int(self.get_valign()) vertical = current_valign while horizontal == current_halign: horizontal = ALIGNMENTS[random.randint(0, 2)] while vertical == current_valign: vertical = ALIGNMENTS[random.randint(0, 2)] self.set_halign(Gtk.Align(horizontal)) self.set_valign(Gtk.Align(vertical)) if status.screen.get_n_monitors() > 1: new_monitor = self.current_monitor n = status.screen.get_n_monitors() while new_monitor == self.current_monitor: new_monitor = random.randint(0, n - 1) self.current_monitor = new_monitor self.queue_resize() return True cinnamon-screensaver-6.2.0/src/widgets/0000775000175000017500000000000014632071776017035 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/widgets/meson.build0000664000175000017500000000035314632071776021200 0ustar fabiofabioapp_py = [ '__init__.py', 'framedImage.py', 'marqueeLabel.py', 'notificationWidget.py', 'powerWidget.py', 'transparentButton.py', 'volumeSlider.py', ] install_data(app_py, install_dir: join_paths(pkgdatadir, 'widgets')) cinnamon-screensaver-6.2.0/src/widgets/marqueeLabel.py0000664000175000017500000001143614632071776022013 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, GObject, GLib from util import trackers class _fixedViewport(Gtk.Viewport): """ This is needed by MarqueeLabel to restrict the size of our label, and cause the viewport to actually be functional. Otherwise, the text is trimmed, but no scrolling occurs. """ def __init__(self): super(_fixedViewport, self).__init__() self.set_shadow_type(Gtk.ShadowType.NONE) def do_get_preferred_width(self): return 400, 400 class MarqueeLabel(Gtk.Stack): """ A scrolling label for the PlayerControl - it uses the defined pattern as a mapping between elapsed time and the hadjustment of the _fixedViewport. If the label text is wider than the widget's actual width, we will scroll according to this map over the course of 15 seconds. It roughly translates to: 0.0 - 2.0 seconds: no movement. 2.0 - 10.0 seconds: gradually scroll to max adjustment of the viewport. 10.0 - 12.0 seconds: no movement. 12.0 - 15.0 seconds: scroll back to the starting position. This widget also implements a stack (similar to the one in MonitorView) which provides for a smooth label crossfade when track info changes. """ PATTERN = [( 0.0, 0.0), ( 2.0, 0.0), (10.0, 1.0), (12.0, 1.0), (15.0, 0.0)] LENGTH = len(PATTERN) def __init__(self, text): super(MarqueeLabel, self).__init__() self.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self.set_transition_duration(300) self.tick_id = 0 self.current = self._make_label(text) self.add(self.current) self.set_visible_child(self.current) def _make_label(self, text): vp = _fixedViewport() label = Gtk.Label(text) label.set_halign(Gtk.Align.START) vp.add(label) vp.show_all() return vp def set_text(self, text): if self.current.get_child().get_text() == text: return self.cancel_tick() self.queued = self._make_label(text) self.add(self.queued) self.set_visible_child(self.queued) tmp = self.current self.current = self.queued self.queued = None GObject.idle_add(tmp.destroy) if not self.current.get_realized(): trackers.con_tracker_get().connect(self.current, "realize", self.on_current_realized) else: GObject.idle_add(self._marquee_idle) def on_current_realized(self, widget, data=None): GObject.idle_add(self._marquee_idle) trackers.con_tracker_get().disconnect(widget, "realize", self.on_current_realized) def cancel_tick(self): if self.tick_id > 0: self.remove_tick_callback(self.tick_id) self.tick_id = 0 def _marquee_idle(self): self.hadjust = self.current.get_hadjustment() if (self.hadjust.get_upper() == self.hadjust.get_page_size()) == self.get_allocated_width(): return False self.start_time = self.get_frame_clock().get_frame_time() self.end_time = self.start_time + (self.PATTERN[self.LENGTH - 1][0] * 1000 * 1000) # sec to ms to μs if self.tick_id == 0: self.tick_id = self.add_tick_callback(self._on_marquee_tick) self._marquee_step(self.start_time) return GLib.SOURCE_REMOVE def _on_marquee_tick(self, widget, clock, data=None): now = clock.get_frame_time() self._marquee_step(now) if now >= self.end_time: self.start_time = self.end_time self.end_time += (self.PATTERN[self.LENGTH - 1][0] * 1000 * 1000) # sec to ms to μs return GLib.SOURCE_CONTINUE def interpolate_point(self, now): point = ((now - self.start_time) / 1000 / 1000) i = 0 while i < self.LENGTH: cindex, cval = self.PATTERN[i] if point > cindex: i += 1 continue if point == cindex: return cval pindex, pval = self.PATTERN[i - 1] diff = cval - pval duration = cindex - pindex ratio = diff / duration additive = (point - pindex) * ratio return pval + additive def _marquee_step(self, now): if now < self.end_time: t = self.interpolate_point(now) else: t = self.PATTERN[self.LENGTH - 1][1] new_position = ((self.hadjust.get_upper() - self.hadjust.get_page_size()) * t) self.hadjust.set_value(new_position) self.queue_draw() cinnamon-screensaver-6.2.0/src/widgets/transparentButton.py0000664000175000017500000000070314632071776023144 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk class TransparentButton(Gtk.Button): """ Custom button widget used throughout. """ def __init__(self, name, size): super(TransparentButton, self).__init__() self.get_style_context().add_class("transparentbutton") image = Gtk.Image.new_from_icon_name(name, size) self.set_can_default(True) self.set_can_focus(True) self.set_image(image) cinnamon-screensaver-6.2.0/src/widgets/notificationWidget.py0000664000175000017500000000364114632071776023245 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, GObject import singletons import status from util import trackers class NotificationWidget(Gtk.Frame): """ Notification widget is a child of the InfoPanel - it is only shown if we've received intercepted a non-transient notification that hasn't been sent from a media player. """ __gsignals__ = { "notification": (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): super(NotificationWidget, self).__init__() self.set_shadow_type(Gtk.ShadowType.NONE) self.get_style_context().add_class("notificationwidget") self.set_size_request(50, -1) self.notification_count = 0 box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.add(box) self.label = Gtk.Label.new("0") box.pack_start(self.label, False, False, 4) self.image = Gtk.Image.new_from_icon_name("screensaver-notification-symbolic", Gtk.IconSize.LARGE_TOOLBAR) box.pack_end(self.image, False, False, 4) box.show_all() self.notification_watcher = singletons.NotificationWatcher trackers.con_tracker_get().connect(self.notification_watcher, "notification-received", self.on_notification_received) def on_notification_received(self, proxy, sender, data=None): mp_watcher = singletons.MediaPlayerWatcher players = mp_watcher.get_all_player_names() if sender.lower() in players: return for ignored in ("network",): if ignored in sender.lower(): return self.notification_count += 1 self.update_label() self.emit("notification") def should_show(self): return self.notification_count > 0 def update_label(self): self.label.set_text(str(self.notification_count)) cinnamon-screensaver-6.2.0/src/widgets/powerWidget.py0000664000175000017500000001240614632071776021712 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, GObject, Gio from util import trackers import singletons import constants as c import status from util.utils import DEBUG UPOWER_STATE_CHARGING = 1 UPOWER_STATE_DISCHARGING = 2 UPOWER_STATE_FULLY_CHARGED = 4 UPOWER_STATE_PENDING_CHARGE = 5 UPOWER_STATE_PENDING_DISCHARGE = 6 class PowerWidget(Gtk.Frame): """ PowerWidget is a child of InfoPanel, and is only shown if we're on a system that can run on battery power. It is usually only visible if the system is actually currently running on battery power. """ __gsignals__ = { 'power-state-changed': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): super(PowerWidget, self).__init__() self.set_shadow_type(Gtk.ShadowType.NONE) self.get_style_context().add_class("powerwidget") self.path_widget_pairs = [] self.box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.add(self.box) self.box.show_all() self.power_client = singletons.UPowerClient self.battery_critical = False trackers.con_tracker_get().connect(self.power_client, "power-state-changed", self.on_power_state_changed) trackers.con_tracker_get().connect(self.power_client, "percentage-changed", self.on_percentage_changed) def refresh(self): self.on_power_state_changed(self.power_client) def on_power_state_changed(self, client): for widget in self.box.get_children(): widget.destroy() self.path_widget_pairs = [] self.battery_critical = False self.construct_icons() self.emit("power-state-changed") def on_percentage_changed(self, client, battery): battery_path = battery.get_object_path() for path, widget in self.path_widget_pairs: if path == battery_path: self.update_battery_tooltip(widget, battery) break def construct_icons(self): """ The upower dbus interface actually tells us what icon name to use. """ batteries = self.power_client.get_batteries() for path, battery in batteries: percentage = battery.get_property("percentage") gicon = self.get_gicon_for_current_level(battery) DEBUG("powerWidget: Updating battery info: %s - icon: %s - percentage: %s" % (path, gicon.to_string(), percentage)) image = Gtk.Image.new_from_gicon(gicon, Gtk.IconSize.LARGE_TOOLBAR) self.update_battery_tooltip(image, battery) self.box.pack_start(image, False, False, 4) self.path_widget_pairs.append((path, image)) self._should_show = True self.box.show_all() def get_gicon_for_current_level(self, battery): percentage = battery.get_property("percentage") state = battery.get_property("state") names = None if state in (UPOWER_STATE_CHARGING, UPOWER_STATE_DISCHARGING, UPOWER_STATE_PENDING_CHARGE, UPOWER_STATE_PENDING_DISCHARGE): if percentage < 10: names = ["battery-level-0", "battery-caution"] elif percentage < 20: names = ["battery-level-10", "battery-low"] elif percentage < 30: names = ["battery-level-20", "battery-low"] elif percentage < 40: names = ["battery-level-30", "battery-good"] elif percentage < 50: names = ["battery-level-40", "battery-good"] elif percentage < 60: names = ["battery-level-50", "battery-good"] elif percentage < 70: names = ["battery-level-60", "battery-full"] elif percentage < 80: names = ["battery-level-70", "battery-full"] elif percentage < 90: names = ["battery-level-80", "battery-full"] elif percentage < 99: names = ["battery-level-90", "battery-full"] else: names = ["battery-level-100", "battery-full"] if state in (UPOWER_STATE_CHARGING, UPOWER_STATE_PENDING_CHARGE): names[0] += "-charging" names[1] += "-charging" names[0] += "-symbolic" names[1] += "-symbolic" elif state == UPOWER_STATE_FULLY_CHARGED: names = ["battery-level-100-charged-symbolic", "battery-full-charged-symbolic", "battery-full-charging-symbolic"] else: names = (battery.get_property("icon-name"),) return Gio.ThemedIcon.new_from_names(names) def update_battery_tooltip(self, widget, battery): text = "" try: pct = int(battery.get_property("percentage")) if pct > 0: text = _("%d%%" % pct) if pct < c.BATTERY_CRITICAL_PERCENT: self.battery_critical = True except Exception as e: pass widget.set_tooltip_text(text) def should_show(self): return not self.power_client.full_and_on_ac_or_no_batteries() cinnamon-screensaver-6.2.0/src/widgets/__init__.py0000664000175000017500000000000014632071776021134 0ustar fabiofabiocinnamon-screensaver-6.2.0/src/widgets/volumeSlider.py0000664000175000017500000000474114632071776022067 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, Gdk from util import trackers class VolumeSlider(Gtk.Scale): """ Custom GtkScale widget for controlling the volume. """ def __init__(self): super(VolumeSlider, self).__init__(orientation=Gtk.Orientation.HORIZONTAL) self.set_can_focus(False) self.muted = False self.set_range(0, 100.0) self.set_increments(5.0, 5.0) self.get_style_context().remove_class("scale") self.get_style_context().add_class("volumeslider") self.set_round_digits(0) self.set_draw_value(False) self.set_size_request(130, -1) self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) trackers.con_tracker_get().connect(self, "draw", self.on_draw) def set_muted(self, muted): if muted != self.muted: self.muted = muted self.queue_draw() def on_draw(self, widget, cr): ctx = widget.get_style_context() alloc = self.get_allocation() padding = ctx.get_padding(Gtk.StateFlags.NORMAL) border = ctx.get_border(Gtk.StateFlags.NORMAL) x = padding.left + border.left y = padding.top + border.top width = alloc.width - padding.left - padding.right - border.left - border.right height = alloc.height - padding.top - padding.bottom - border.top - border.bottom floor = y + height end = x + width value = round(self.get_value()) value_x = x + ((value / 100) * width) value_y = floor - ((value / 100) * height) if self.muted: fill_color = ctx.get_color(Gtk.StateFlags.INSENSITIVE) bg_color = ctx.get_background_color(Gtk.StateFlags.INSENSITIVE) else: fill_color = ctx.get_color(Gtk.StateFlags.NORMAL) bg_color = ctx.get_background_color(Gtk.StateFlags.NORMAL) cr.save() cr.new_sub_path() cr.move_to(x, floor) cr.line_to(end, floor) cr.line_to(end, y) cr.close_path() Gdk.cairo_set_source_rgba(cr, bg_color) cr.fill() cr.restore() cr.save() cr.new_sub_path() cr.move_to(x, floor) cr.line_to(value_x, floor) cr.line_to(value_x, value_y) cr.close_path() Gdk.cairo_set_source_rgba(cr, fill_color) cr.fill() cr.restore() return True cinnamon-screensaver-6.2.0/src/widgets/framedImage.py0000664000175000017500000001021314632071776021605 0ustar fabiofabio#!/usr/bin/python3 # coding: utf-8 import gi gi.require_version('CinnamonDesktop', '3.0') from gi.repository import Gtk, GdkPixbuf, Gio, GLib, GObject, Gdk from util import utils, trackers MAX_IMAGE_SIZE = 320 MAX_IMAGE_SIZE_LOW_RES = 200 class FramedImage(Gtk.Image): """ Widget to hold the user face image. It attempts to display an image at its native size, up to a max sane size. """ __gsignals__ = { "surface-changed": (GObject.SignalFlags.RUN_LAST, None, (object,)) } def __init__(self, low_res=False, scale_up=False): super(FramedImage, self).__init__() self.get_style_context().add_class("framedimage") self.cancellable = None self.file = None self.path = None self.scale_up = scale_up if low_res: self.max_size = MAX_IMAGE_SIZE_LOW_RES else: self.max_size = MAX_IMAGE_SIZE trackers.con_tracker_get().connect(self, "realize", self.on_realized) def on_realized(self, widget): self.generate_image() def clear_image(self): self.set_from_surface(None) self.emit("surface-changed", None) def set_from_path(self, path): self.path = path self.file = None if self.get_realized(): self.generate_image() def set_from_file(self, file): self.file = file self.path = None if self.get_realized(): self.generate_image() def set_image_internal(self, path): pixbuf = None scaled_max_size = self.max_size * self.get_scale_factor() try: pixbuf = GdkPixbuf.Pixbuf.new_from_file(path) except GLib.Error as e: message = "Could not load pixbuf from '%s' for FramedImage: %s" % (path, e.message) error = True if pixbuf is not None: if (pixbuf.get_height() > scaled_max_size or pixbuf.get_width() > scaled_max_size) or \ (self.scale_up and (pixbuf.get_height() < scaled_max_size / 2 or pixbuf.get_width() < scaled_max_size / 2)): try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, scaled_max_size, scaled_max_size) except GLib.Error as e: message = "Could not scale pixbuf from '%s' for FramedImage: %s" % (path, e.message) error = True if pixbuf: surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, self.get_scale_factor(), self.get_window()) self.set_from_surface(surface) self.emit("surface-changed", surface) else: print(message) self.clear_image() def generate_image(self): if self.path: self.set_image_internal(self.path) elif self.file: if self.cancellable is not None: self.cancellable.cancel() self.cancellable = None self.cancellable = Gio.Cancellable() self.file.load_contents_async(self.cancellable, self.load_contents_async_callback) def load_contents_async_callback(self, file, result, data=None): try: success, contents, etag_out = file.load_contents_finish(result) except GLib.Error: self.clear_image() return if contents: cache_name = GLib.build_filenamev([GLib.get_user_cache_dir(), "cinnamon-screensaver-albumart-temp"]) cache_file = Gio.File.new_for_path(cache_name) cache_file.replace_contents_async(contents, None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, self.cancellable, self.on_file_written) def on_file_written(self, file, result, data=None): try: if file.replace_contents_finish(result): self.set_image_internal(file.get_path()) except GLib.Error: pass cinnamon-screensaver-6.2.0/src/stage.py0000664000175000017500000011115314632071776017046 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CDesktopEnums', '3.0') from gi.repository import GLib, Gtk, Gdk, CScreensaver, CDesktopEnums, GObject import random import status import constants as c import singletons from monitorView import MonitorView from unlock import UnlockDialog from clock import ClockWidget from albumArt import AlbumArt from audioPanel import AudioPanel from infoPanel import InfoPanel from osk import OnScreenKeyboard from floating import ALIGNMENTS from util import utils, trackers, settings from util.eventHandler import EventHandler from util.utils import DEBUG class Stage(Gtk.Window): """ The Stage is the toplevel window of the entire screensaver while in Active mode. It's the first thing made, the last thing destroyed, and all other widgets live inside of it (or rather, inside the GtkOverlay below) It is Gtk.WindowType.POPUP to avoid being managed/composited by muffin, and to prevent animation during its creation and destruction. The Stage reponds pretty much only to the instructions of the ScreensaverManager. """ __gsignals__ = { 'needs-refresh': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self, manager, away_message): if status.InteractiveDebug: Gtk.Window.__init__(self, type=Gtk.WindowType.TOPLEVEL, decorated=True, skip_taskbar_hint=False) else: Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP, decorated=False, skip_taskbar_hint=True) self.get_style_context().add_class("csstage") trackers.con_tracker_get().connect(singletons.Backgrounds, "changed", self.on_bg_changed) self.destroying = False self.manager = manager self.away_message = away_message self.activate_callback = None self.monitors = [] self.last_focus_monitor = -1 self.overlay = None self.clock_widget = None self.albumart_widget = None self.unlock_dialog = None self.audio_panel = None self.info_panel = None self.osk = None self.floaters = [] self.event_handler = EventHandler(manager) self.get_style_context().remove_class("background") self.set_events(self.get_events() | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK | Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK | Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.VISIBILITY_NOTIFY_MASK | Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK | Gdk.EventMask.FOCUS_CHANGE_MASK) c = Gdk.RGBA(0, 0, 0, 0) self.override_background_color(Gtk.StateFlags.NORMAL, c) self.update_geometry() self.overlay = Gtk.Overlay() trackers.con_tracker_get().connect(self.overlay, "realize", self.on_realized) trackers.con_tracker_get().connect(self.overlay, "get-child-position", self.position_overlay_child) self.overlay.show_all() self.add(self.overlay) # We hang onto the UPowerClient here so power events can # trigger changes to the info panel. self.power_client = singletons.UPowerClient trackers.con_tracker_get().connect(self.power_client, "power-state-changed", self.on_power_state_changed) # This filter suppresses any other windows that might share # our window group in muffin, from showing up over the Stage. # For instance: Chrome and Firefox native notifications. self.gdk_filter = CScreensaver.GdkEventFilter.new(self, 0) trackers.con_tracker_get().connect(status.screen, "size-changed", self.on_screen_size_changed) trackers.con_tracker_get().connect(status.screen, "monitors-changed", self.on_monitors_changed) trackers.con_tracker_get().connect(status.screen, "composited-changed", self.on_composited_changed) trackers.con_tracker_get().connect(self, "grab-broken-event", self.on_grab_broken_event) if status.InteractiveDebug: self.set_interactive_debugging(True) def update_monitors(self): self.destroy_monitor_views() try: self.setup_monitors() for monitor in self.monitors: self.sink_child_widget(monitor) except Exception as e: print("Problem updating monitor views views: %s" % str(e)) def on_screen_size_changed(self, screen, data=None): """ The screen changing size should be acted upon immediately, to ensure coverage. Wallpapers are secondary. """ DEBUG("Stage: Received screen size-changed signal, refreshing stage") self.emit("needs-refresh") def on_monitors_changed(self, screen, data=None): """ Updating monitors also will trigger an immediate stage coverage update (same as on_screen_size_changed), and follow up at idle with actual monitor view refreshes (wallpapers.) """ DEBUG("Stage: Received screen monitors-changed signal, refreshing stage") self.emit("needs-refresh") def on_composited_changed(self, screen, data=None): if self.get_realized(): DEBUG("Stage: Received screen composited-changed signal, refreshing stage") self.emit("needs-refresh") def on_grab_broken_event(self, widget, event, data=None): GObject.idle_add(self.manager.grab_stage) return False def activate(self, callback): """ This is the primary way of making the Stage visible. """ self.set_opacity(1.0) self.move_onscreen() self.show() if self.get_realized(): callback() else: self.activate_callback = callback def deactivate(self, callback): """ This is the primary way of destroying the stage. """ self.hide() callback() def on_realized(self, widget): """ Repositions the window when it is realized, to cover the entire GdkScreen (a rectangle exactly encompassing all monitors.) From here we also proceed to construct all overlay children and activate our window suppressor. """ window = self.get_window() utils.override_user_time(window) CScreensaver.Screen.set_net_wm_name(window, "cinnamon-screensaver-window") self.setup_children() self.gdk_filter.start(singletons.MuffinClient.get_using_fractional_scaling(), status.Debug) trackers.con_tracker_get().disconnect(self.overlay, "realize", self.on_realized) if self.activate_callback is not None: self.activate_callback() def move_onscreen(self): w = self.get_window() if w: w.move_resize(self.rect.x, self.rect.y, self.rect.width, self.rect.height) self.move(self.rect.x, self.rect.y) self.resize(self.rect.width, self.rect.height) def deactivate_after_timeout(self): self.manager.set_active(False) def setup_children(self): """ Creates all of our overlay children. If a new 'widget' gets added, this should be the setup point for it. We bail if something goes wrong on a critical widget - a monitor view or unlock widget. """ total_failure = False try: self.setup_monitors() except Exception as e: print("Problem setting up monitor views: %s" % str(e)) total_failure = True try: self.setup_unlock() except Exception as e: print("Problem setting up unlock dialog: %s" % str(e)) total_failure = True if not total_failure: try: self.setup_clock() except Exception as e: print("Problem setting up clock widget: %s" % str(e)) self.clock_widget = None try: self.setup_osk() except Exception as e: print("Problem setting up on-screen keyboard: %s" % str(e)) self.osk = None trackers.timer_tracker_get().start("setup-delayed-components", 2000, self.setup_delayed_components) else: print("Total failure somewhere, deactivating screensaver.") GObject.idle_add(self.deactivate_after_timeout) def setup_delayed_components(self, data=None): try: self.setup_albumart() except Exception as e: print("Problem setting up albumart widget: %s" % str(e)) self.albumart_widget = None try: self.setup_status_bars() except Exception as e: print("Problem setting up status bars: %s" % str(e)) self.audio_panel = None self.info_panel = None def destroy_children(self): try: self.destroy_monitor_views() except Exception as e: print(e) try: if self.unlock_dialog is not None: self.unlock_dialog.destroy() except Exception as e: print(e) try: if self.clock_widget is not None: self.clock_widget.stop_positioning() self.clock_widget.destroy() except Exception as e: print(e) try: if self.albumart_widget is not None: self.albumart_widget.stop_positioning() self.albumart_widget.destroy() except Exception as e: print(e) try: if self.info_panel is not None: self.info_panel.destroy() except Exception as e: print(e) try: if self.info_panel is not None: self.audio_panel.destroy() except Exception as e: print(e) try: if self.osk is not None: self.osk.destroy() except Exception as e: print(e) self.unlock_dialog = None self.clock_widget = None self.albumart_widget = None self.info_panel = None self.audio_panel = None self.osk = None self.away_message = None self.monitors = [] self.floaters = [] def destroy_stage(self): """ Performs all tear-down necessary to destroy the Stage, destroying all children in the process, and finally destroying itself. """ trackers.con_tracker_get().disconnect(self.unlock_dialog, "inhibit-timeout", self.set_timeout_active) trackers.con_tracker_get().disconnect(self.unlock_dialog, "uninhibit-timeout", self.set_timeout_active) trackers.con_tracker_get().disconnect(self.unlock_dialog, "authenticate-success", self.authentication_result_callback) trackers.con_tracker_get().disconnect(self.unlock_dialog, "authenticate-failure", self.authentication_result_callback) trackers.con_tracker_get().disconnect(self.unlock_dialog, "authenticate-cancel", self.authentication_cancel_callback) trackers.con_tracker_get().disconnect(singletons.Backgrounds, "changed", self.on_bg_changed) trackers.con_tracker_get().disconnect(self.power_client, "power-state-changed", self.on_power_state_changed) trackers.con_tracker_get().disconnect(self, "grab-broken-event", self.on_grab_broken_event) self.set_timeout_active(None, False) trackers.timer_tracker_get().cancel("setup-delayed-components") self.destroy_children() self.gdk_filter.stop() self.gdk_filter = None trackers.con_tracker_get().disconnect(status.screen, "size-changed", self.on_screen_size_changed) trackers.con_tracker_get().disconnect(status.screen, "monitors-changed", self.on_monitors_changed) trackers.con_tracker_get().disconnect(self.overlay, "get-child-position", self.position_overlay_child) self.destroy() def setup_monitors(self): """ Iterate through the monitors, and create MonitorViews for each one to cover them. """ self.monitors = [] status.Spanned = settings.bg_settings.get_enum("picture-options") == CDesktopEnums.BackgroundStyle.SPANNED if status.InteractiveDebug or status.Spanned: monitors = (status.screen.get_primary_monitor(),) else: n = status.screen.get_n_monitors() monitors = () for i in range(n): monitors += (i,) for index in monitors: monitor = MonitorView(index) image = Gtk.Image() singletons.Backgrounds.create_and_set_gtk_image (image, monitor.rect.width, monitor.rect.height) monitor.set_next_wallpaper_image(image) self.monitors.append(monitor) self.add_child_widget(monitor) self.update_monitor_views() def on_bg_changed(self, bg): """ Callback for our GnomeBackground instance, this tells us when the background settings have changed, so we can update our wallpaper. """ for monitor in self.monitors: image = Gtk.Image() singletons.Backgrounds.create_and_set_gtk_image (image, monitor.rect.width, monitor.rect.height) monitor.set_next_wallpaper_image(image) def on_power_state_changed(self, client, data=None): """ Callback for UPower changes, this will make our MonitorViews update themselves according to user setting and power state. """ DEBUG("stage: Power state changed, updating info panel") if self.info_panel is not None: self.info_panel.update_visibility() def setup_clock(self): """ Construct the clock widget and add it to the overlay, but only actually show it if we're a) Not running a plug-in, and b) The user wants it via preferences. Initially invisible, regardless - its visibility is controlled via its own positioning timer. """ self.clock_widget = ClockWidget(self.away_message, status.screen.get_mouse_monitor(), status.screen.get_low_res_mode()) self.add_child_widget(self.clock_widget) self.floaters.append(self.clock_widget) if settings.get_show_clock(): self.clock_widget.start_positioning() def setup_albumart(self): """ Construct the AlbumArt widget and add it to the overlay, but only actually show it if we're a) Not running a plug-in, and b) The user wants it via preferences. Initially invisible, regardless - its visibility is controlled via its own positioning timer. """ self.albumart_widget = AlbumArt(None, status.screen.get_mouse_monitor()) self.add_child_widget(self.albumart_widget) self.floaters.append(self.albumart_widget) if settings.get_show_albumart(): self.albumart_widget.start_positioning() def setup_osk(self): self.osk = OnScreenKeyboard() self.add_child_widget(self.osk) def setup_unlock(self): """ Construct the unlock dialog widget and add it to the overlay. It will always initially be invisible. Any time the screensaver is awake, and the unlock dialog is raised, a timer runs. After a certain elapsed time, the state will be reset, and the dialog will be hidden once more. Mouse and key events reset this timer, and the act of authentication temporarily suspends it - the unlock widget accomplishes this via its inhibit- and uninhibit-timeout signals We also listen to actual authentication events, to destroy the stage if there is success, and to do something cute if we fail (for now, this consists of 'blinking' the unlock dialog.) """ self.unlock_dialog = UnlockDialog() self.set_default(self.unlock_dialog.auth_unlock_button) self.add_child_widget(self.unlock_dialog) # Prevent a dialog timeout during authentication trackers.con_tracker_get().connect(self.unlock_dialog, "inhibit-timeout", self.set_timeout_active, False) trackers.con_tracker_get().connect(self.unlock_dialog, "uninhibit-timeout", self.set_timeout_active, True) # Respond to authentication success/failure trackers.con_tracker_get().connect(self.unlock_dialog, "authenticate-success", self.authentication_result_callback, True) trackers.con_tracker_get().connect(self.unlock_dialog, "authenticate-failure", self.authentication_result_callback, False) trackers.con_tracker_get().connect(self.unlock_dialog, "authenticate-cancel", self.authentication_cancel_callback) def setup_status_bars(self): """ Constructs the AudioPanel and InfoPanel and adds them to the overlay. """ self.audio_panel = AudioPanel() self.add_child_widget(self.audio_panel) if status.Awake: self.audio_panel.show_panel() self.info_panel = InfoPanel() self.add_child_widget(self.info_panel) self.info_panel.refresh_power_state() def queue_dialog_key_event(self, event): """ Sent from our EventHandler via the ScreensaverManager, this catches initial key events before the unlock dialog is made visible, so that the user doesn't have to first jiggle the mouse to wake things up before beginning to type their password. They can just start typing, and no keystrokes will be lost. """ self.unlock_dialog.queue_key_event(event) # Timer stuff - after a certain time, the unlock dialog will cancel itself. # This timer is suspended during authentication, and any time a new user event is received def reset_timeout(self): """ This is called when any user event is received in our EventHandler. This restarts our dialog timeout. """ self.set_timeout_active(None, True) def set_timeout_active(self, dialog, active): """ Start or stop the dialog timer """ if active and not status.InteractiveDebug: trackers.timer_tracker_get().start("wake-timeout", c.UNLOCK_TIMEOUT * 1000, self.on_wake_timeout) else: trackers.timer_tracker_get().cancel("wake-timeout") def on_wake_timeout(self): """ Go back to Sleep if we hit our timer limit """ self.set_timeout_active(None, False) self.manager.cancel_unlocking() return False def authentication_result_callback(self, dialog, success): """ Called by authentication success or failure. Either starts the stage despawning process or simply 'blinks' the unlock widget, depending on the outcome. """ if success: if self.clock_widget is not None: self.clock_widget.hide() if self.albumart_widget is not None: self.albumart_widget.hide() self.unlock_dialog.hide() self.manager.unlock() else: self.unlock_dialog.blink() def authentication_cancel_callback(self, dialog): self.manager.cancel_unlocking() def set_message(self, msg): """ Passes along an away-message to the clock. """ if self.clock_widget is not None: self.clock_widget.set_message(msg) def initialize_pam(self): return self.unlock_dialog.initialize_auth_client() def raise_unlock_widget(self): """ Bring the unlock widget to the front and make sure it's visible. """ self.reset_timeout() if status.Awake: return status.screen.place_pointer_in_primary_monitor () utils.clear_clipboards(self.unlock_dialog) if self.clock_widget is not None: self.clock_widget.stop_positioning() if self.albumart_widget is not None: self.albumart_widget.stop_positioning() status.Awake = True if self.info_panel: self.info_panel.refresh_power_state() if self.clock_widget is not None: self.clock_widget.show() if self.albumart_widget is not None: self.albumart_widget.show() self.unlock_dialog.show() if self.audio_panel is not None: self.audio_panel.show_panel() if self.info_panel is not None: self.info_panel.refresh_power_state() if self.osk is not None: self.osk.show() def cancel_unlocking(self): if self.unlock_dialog: self.unlock_dialog.cancel() self.set_timeout_active(None, False) utils.clear_clipboards(self.unlock_dialog) if self.unlock_dialog is not None: self.unlock_dialog.hide() if self.clock_widget is not None: self.clock_widget.hide() if self.albumart_widget is not None: self.albumart_widget.hide() if self.audio_panel is not None: self.audio_panel.hide() if self.info_panel is not None: self.info_panel.hide() if self.osk is not None: self.osk.hide() status.Awake = False self.update_monitor_views() if self.info_panel is not None: self.info_panel.refresh_power_state() def update_monitor_views(self): """ Updates all of our MonitorViews based on the power or Awake states. """ if not status.Awake: if self.clock_widget is not None and settings.get_show_clock(): self.clock_widget.start_positioning() if self.albumart_widget is not None and settings.get_show_albumart(): self.albumart_widget.start_positioning() for monitor in self.monitors: monitor.show() def destroy_monitor_views(self): """ Destroy all MonitorViews """ for monitor in self.monitors: monitor.destroy() del monitor def do_motion_notify_event(self, event): """ GtkWidget class motion-event handler. Delegate to EventHandler """ return self.event_handler.on_motion_event(event) def do_key_press_event(self, event): """ GtkWidget class key-press-event handler. Delegate to EventHandler """ return self.event_handler.on_key_press_event(event) def do_button_press_event(self, event): """ GtkWidget class button-press-event handler. Delegate to EventHandler """ return self.event_handler.on_button_press_event(event) def update_geometry(self): """ Override BaseWindow.update_geometry() - the Stage should always be the GdkScreen size, unless status.InteractiveDebug is True """ if status.InteractiveDebug: monitor_n = status.screen.get_primary_monitor() self.rect = status.screen.get_monitor_geometry(monitor_n) else: self.rect = status.screen.get_screen_geometry() scale = status.screen.get_global_scale() self.rect.width *= scale self.rect.height *= scale DEBUG("Stage.update_geometry - new backdrop position: %d, %d new size: %d x %d" % (self.rect.x, self.rect.y, self.rect.width, self.rect.height)) hints = Gdk.Geometry() hints.min_width = self.rect.width hints.min_height = self.rect.height hints.max_width = self.rect.width hints.max_height = self.rect.height hints.base_width = self.rect.width hints.base_height = self.rect.height self.set_geometry_hints(self, hints, Gdk.WindowHints.MIN_SIZE | Gdk.WindowHints.MAX_SIZE | Gdk.WindowHints.BASE_SIZE) # Overlay window management def get_mouse_monitor(self): if status.InteractiveDebug: return status.screen.get_primary_monitor() else: return status.screen.get_mouse_monitor() def maybe_update_layout(self): """ Called on all user events, moves widgets to the currently focused monitor if it changes (whichever monitor the mouse is in) """ current_focus_monitor = status.screen.get_mouse_monitor() if self.last_focus_monitor == -1: self.last_focus_monitor = current_focus_monitor return if self.unlock_dialog and current_focus_monitor != self.last_focus_monitor: self.last_focus_monitor = current_focus_monitor self.overlay.queue_resize() def add_child_widget(self, widget): """ Add a new child to the overlay """ self.overlay.add_overlay(widget) def sink_child_widget(self, widget): """ Move a child to the bottom of the overlay """ self.overlay.reorder_overlay(widget, 0) def position_overlay_child(self, overlay, child, allocation): """ Callback for our GtkOverlay, think of this as a mini- window manager for our Stage. Depending on what type child is, we position it differently. We always call child.get_preferred_size() whether we plan to use it or not - this prevents allocation warning spew, particularly in Gtk >= 3.20. Returning True says, yes draw it. Returning False tells it to skip drawing. If a new widget type is introduced that spawns directly on the stage, it must have its own handling code here. """ if isinstance(child, MonitorView): """ MonitorView is always the size and position of its assigned monitor. This is calculated and stored by the child in child.rect) """ w, h = child.get_preferred_size() allocation.x = child.rect.x allocation.y = child.rect.y allocation.width = child.rect.width allocation.height = child.rect.height return True if isinstance(child, UnlockDialog): """ UnlockDialog always shows on the currently focused monitor (the one the mouse is currently in), and is kept centered. """ monitor = status.screen.get_mouse_monitor() monitor_rect = status.screen.get_monitor_geometry(monitor) min_rect, nat_rect = child.get_preferred_size() allocation.width = nat_rect.width allocation.height = nat_rect.height allocation.x = monitor_rect.x + (monitor_rect.width / 2) - (allocation.width / 2) allocation.y = monitor_rect.y + (monitor_rect.height / 2) - (allocation.height / 2) return True if isinstance(child, ClockWidget) or isinstance(child, AlbumArt): """ ClockWidget and AlbumArt behave differently depending on if status.Awake is True or not. The widgets' halign and valign properties are used to store their gross position on the monitor. This limits the number of possible positions to (3 * 3 * n_monitors) when our screensaver is not Awake, and the widgets have an internal timer that randomizes halign, valign, and current monitor every so many seconds, calling a queue_resize on itself after each timer tick (which forces this function to run). """ min_rect, nat_rect = child.get_preferred_size() if status.Awake: current_monitor = status.screen.get_mouse_monitor() else: current_monitor = child.current_monitor monitor_rect = status.screen.get_monitor_geometry(current_monitor) region_w = monitor_rect.width / 3 region_h = monitor_rect.height if status.Awake: """ If we're Awake, force the clock to track to the active monitor, and be aligned to the left-center. The albumart widget aligns right-center. """ unlock_mw, unlock_nw = self.unlock_dialog.get_preferred_width() """ If, for whatever reason, we need more than 1/3 of the screen to fully display the unlock dialog, reduce our available region width to accomodate it, reducing the allocation for the floating widgets as required. """ if unlock_nw > region_w: region_w = (monitor_rect.width - unlock_nw) / 2 region_h = monitor_rect.height if isinstance(child, ClockWidget): child.set_halign(Gtk.Align.START) else: child.set_halign(Gtk.Align.END) child.set_valign(Gtk.Align.CENTER) else: if settings.get_allow_floating(): for floater in self.floaters: """ Don't let our floating widgets end up in the same spot. """ if floater is child: continue if floater.get_halign() != child.get_halign() and floater.get_valign() != child.get_valign(): continue region_h = monitor_rect.height / 3 fa = floater.get_halign() ca = child.get_halign() while fa == ca: ca = ALIGNMENTS[random.randint(0, 2)] child.set_halign(ca) fa = floater.get_valign() ca = child.get_valign() while fa == ca: ca = ALIGNMENTS[random.randint(0, 2)] child.set_valign(ca) # Restrict the widget size to the allowable region sizes if necessary. allocation.width = min(nat_rect.width, region_w) allocation.height = min(nat_rect.height, region_h) # Calculate padding required to center widgets within their particular 1/9th of the monitor padding_left = padding_right = (region_w - allocation.width) / 2 padding_top = padding_bottom = (region_h - allocation.height) / 2 halign = child.get_halign() valign = child.get_valign() if halign == Gtk.Align.START: allocation.x = monitor_rect.x + padding_left elif halign == Gtk.Align.CENTER: allocation.x = monitor_rect.x + (monitor_rect.width / 2) - (allocation.width / 2) elif halign == Gtk.Align.END: allocation.x = monitor_rect.x + monitor_rect.width - allocation.width - padding_right if valign == Gtk.Align.START: allocation.y = monitor_rect.y + padding_top elif valign == Gtk.Align.CENTER: allocation.y = monitor_rect.y + (monitor_rect.height / 2) - (allocation.height / 2) elif valign == Gtk.Align.END: allocation.y = monitor_rect.y + monitor_rect.height - allocation.height - padding_bottom return True if isinstance(child, AudioPanel): """ The AudioPanel is only shown when Awake, and attaches itself to the upper-left corner of the active monitor. """ min_rect, nat_rect = child.get_preferred_size() if status.Awake: current_monitor = status.screen.get_mouse_monitor() monitor_rect = status.screen.get_monitor_geometry(current_monitor) allocation.x = monitor_rect.x allocation.y = monitor_rect.y allocation.width = nat_rect.width allocation.height = nat_rect.height else: allocation.x = child.rect.x allocation.y = child.rect.y allocation.width = nat_rect.width allocation.height = nat_rect.height return True if isinstance(child, InfoPanel): """ The InfoPanel can be shown while not Awake, but will only appear if a) We have received notifications while the screensaver is running, or b) we're either on battery or plugged in but with a non-full battery. It attaches itself to the upper-right corner of the monitor. """ min_rect, nat_rect = child.get_preferred_size() if status.Awake: current_monitor = status.screen.get_mouse_monitor() monitor_rect = status.screen.get_monitor_geometry(current_monitor) allocation.x = monitor_rect.x + monitor_rect.width - nat_rect.width allocation.y = monitor_rect.y allocation.width = nat_rect.width allocation.height = nat_rect.height else: allocation.x = child.rect.x + child.rect.width - nat_rect.width allocation.y = child.rect.y allocation.width = nat_rect.width allocation.height = nat_rect.height return True if isinstance(child, OnScreenKeyboard): """ The InfoPanel can be shown while not Awake, but will only appear if a) We have received notifications while the screensaver is running, or b) we're either on battery or plugged in but with a non-full battery. It attaches itself to the upper-right corner of the monitor. """ min_rect, nat_rect = child.get_preferred_size() current_monitor = status.screen.get_mouse_monitor() monitor_rect = status.screen.get_monitor_geometry(current_monitor) allocation.x = monitor_rect.x allocation.y = monitor_rect.y + monitor_rect.height - (monitor_rect.height / 3) allocation.width = monitor_rect.width allocation.height = monitor_rect.height / 3 return True return False cinnamon-screensaver-6.2.0/src/util/0000775000175000017500000000000014632071776016344 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/util/meson.build0000664000175000017500000000033614632071776020510 0ustar fabiofabioapp_py = [ '__init__.py', 'eventHandler.py', 'fader.py', 'focusNavigator.py', 'keybindings.py', 'settings.py', 'trackers.py', 'utils.py' ] install_data(app_py, install_dir: join_paths(pkgdatadir, 'util')) cinnamon-screensaver-6.2.0/src/util/keybindings.py0000664000175000017500000001030214632071776021220 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CDesktopEnums', '3.0') from gi.repository import Gtk, GObject, Gdk, Gio, CinnamonDesktop from gi.repository.CDesktopEnums import MediaKeyType as MK import status import singletons from util import settings ALLOWED_ACTIONS = [MK.MUTE, MK.VOLUME_UP, MK.VOLUME_UP_QUIET, MK.VOLUME_DOWN, MK.VOLUME_DOWN_QUIET, MK.MIC_MUTE, MK.EJECT, MK.PLAY, MK.PAUSE, MK.STOP, MK.PREVIOUS, MK.NEXT, MK.REWIND, MK.FORWARD, MK.REPEAT, MK.RANDOM, MK.TOUCHPAD, MK.TOUCHPAD_ON, MK.TOUCHPAD_OFF, MK.SHUTDOWN, MK.SUSPEND, MK.HIBERNATE, MK.SCREEN_BRIGHTNESS_UP, MK.SCREEN_BRIGHTNESS_DOWN, MK.ROTATE_VIDEO, MK.KEYBOARD_BRIGHTNESS_UP, MK.KEYBOARD_BRIGHTNESS_DOWN, MK.KEYBOARD_BRIGHTNESS_TOGGLE] class ShortcutAction(GObject.GObject): """ A class representing a group of keybindings for a specific media key action. """ def __init__(self, action, bindings): super(ShortcutAction, self).__init__() self.action = action self.bindings = bindings self.parsed = [] for binding in self.bindings: key, codes, mods = Gtk.accelerator_parse_with_keycode(binding) self.parsed.append((key, codes, mods)) def activate(self, key, keycode, mods): for binding in self.parsed: if (key == binding[0] or keycode in binding[1]) and mods == binding[2]: return self.action return -1 class KeyBindings(GObject.GObject): """ Receives keystrokes from the EventHandler - it checks for media key activation, as well as special keys like Escape or Tab, and performs the appropriate actions. """ def __init__(self, manager): super(KeyBindings, self).__init__() self.manager = manager self.client = singletons.KeybindingHandlerClient self.keymap = Gdk.Keymap.get_default() self.media_key_settings = Gio.Settings(schema_id="org.cinnamon.desktop.keybindings.media-keys") self.shortcut_actions = [] self.load_bindings() def load_bindings(self): self.shortcut_actions = [] for action_id in ALLOWED_ACTIONS: bindings = self.media_key_settings.get_strv(CinnamonDesktop.desktop_get_media_key_string(action_id)) action = ShortcutAction(action_id, bindings) self.shortcut_actions.append(action) def maybe_handle_event(self, event): if event.type != Gdk.EventType.KEY_PRESS: return False filtered_state = Gdk.ModifierType(event.state & ~(Gdk.ModifierType.MOD2_MASK | Gdk.ModifierType.LOCK_MASK)) if filtered_state == 0 and event.keyval == Gdk.KEY_Escape: if status.Awake: self.manager.cancel_unlocking() return True if event.keyval == Gdk.KEY_Menu: return True if status.Awake: if event.keyval in (Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab): if event.keyval == Gdk.KEY_ISO_Left_Tab: self.manager.propagate_tab_event(True) else: self.manager.propagate_tab_event(False) return True elif (event.keyval in (Gdk.KEY_space, Gdk.KEY_Return, Gdk.KEY_KP_Enter)) and \ isinstance(self.manager.get_focused_widget(), Gtk.Button): self.manager.propagate_activation() return True if settings.get_allow_shortcuts(): for entry in self.shortcut_actions: res = entry.activate(event.keyval, event.hardware_keycode, filtered_state) if res == -1: continue else: self.client.handle_keybinding(res) return True return False cinnamon-screensaver-6.2.0/src/util/focusNavigator.py0000664000175000017500000000454214632071776021715 0ustar fabiofabio#!/usr/bin/python3 # coding: utf-8 from gi.repository import Gtk import status class FocusNavigator: """ FocusNavigator helps with tab navigation between widgets in our status.focusWidgets list. Since we handle most user events ourselves, we also need to handle Tab events correctly. """ def __init__(self, widgets=[]): status.focusWidgets = widgets def _get_focus_index(self): widgets = status.focusWidgets focus_index = -1 for widget in widgets: if widget.has_focus(): focus_index = widgets.index(widget) break return focus_index def _focus_first_possible(self): widgets = status.focusWidgets for widget in widgets: if widget.get_sensitive(): widget.grab_focus() widget.grab_default() break def _focus_next(self, current): widgets = status.focusWidgets new = current + 1 if new >= len(widgets): new = 0 if not widgets[new].get_sensitive(): self._focus_next(new) return widgets[new].grab_focus() widgets[new].grab_default() def _focus_previous(self, current): widgets = status.focusWidgets new = current - 1 if new < 0: new = len(widgets) - 1 if not widgets[new].get_sensitive(): self._focus_previous(new) return widgets[new].grab_focus() widgets[new].grab_default() def navigate(self, reverse): current_index = self._get_focus_index() if current_index == -1: self._focus_first_possible() if reverse: self._focus_previous(current_index) else: self._focus_next(current_index) def activate_focus(self): widgets = status.focusWidgets focus_index = self._get_focus_index() if focus_index == -1: return widget = widgets[focus_index] if isinstance(widget, Gtk.Button): widget.clicked() elif isinstance(widget, Gtk.Entry): widget.activate() def get_focused_widget(self): widgets = status.focusWidgets focus_index = self._get_focus_index() if focus_index == -1: return None return widgets[focus_index] cinnamon-screensaver-6.2.0/src/util/utils.py0000664000175000017500000001011314632071776020052 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import GLib, Gio, Gdk, Gtk import os import grp import subprocess import xapp.os import config import status # Various utility functions that are used in multiple places. def nofail_locale_to_utf8(string): try: ret = GLib.locale_to_utf8(string, -1, 0, 0) except: try: ret, br, bw = GLib.locale_to_utf8(string, -1) except: ret = string return ret def get_user_name(): name = GLib.get_user_name() utf8_name = None if name: utf8_name = nofail_locale_to_utf8(name) return utf8_name def get_user_display_name(): name = GLib.get_real_name() if not name or name == "Unknown": name = get_user_name() utf8_name = None if name: utf8_name = nofail_locale_to_utf8(name) return utf8_name def get_host_name(): name = GLib.get_host_name() utf8_name = None if name: utf8_name = nofail_locale_to_utf8(name) return utf8_name def user_can_lock(): if not status.LockEnabled: return False name = get_user_name() # KeyError is generated if group doesn't exist, ignore it and allow lock try: group = grp.getgrnam("nopasswdlogin") if name in group.gr_mem: return False except KeyError: pass # Don't lock the screensaver in guest or live sessions if xapp.os.is_live_session() or xapp.os.is_guest_session(): return False return True def process_is_running(name): res = "" try: res = subprocess.check_output(["pidof", name]) except subprocess.CalledProcessError: pass return res != "" def do_user_switch(): GLib.idle_add(do_user_switch_timeout) def do_user_switch_timeout(data=None): if process_is_running("mdm"): command = "%s %s" % ("mdmflexiserver", "--startnew Standard") ctx = Gdk.Display.get_default().get_app_launch_context() app = Gio.AppInfo.create_from_commandline(command, "mdmflexiserver", Gio.AppInfoCreateFlags.NONE) if app: app.launch(None, ctx) elif process_is_running("gdm"): command = "%s %s" % ("gdmflexiserver", "--startnew Standard") ctx = Gdk.Display.get_default().get_app_launch_context() app = Gio.AppInfo.create_from_commandline(command, "gdmflexiserver", Gio.AppInfoCreateFlags.NONE) if app: app.launch(None, ctx) elif process_is_running("gdm3"): ctx = Gdk.Display.get_default().get_app_launch_context() app = Gio.AppInfo.create_from_commandline("gdmflexiserver", None, Gio.AppInfoCreateFlags.NONE) if app: app.launch(None, ctx) elif os.getenv("XDG_SEAT_PATH") is not None: try: bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) bus.call_sync("org.freedesktop.DisplayManager", os.getenv("XDG_SEAT_PATH"), "org.freedesktop.DisplayManager.Seat", "SwitchToGreeter", None, None, Gio.DBusCallFlags.NONE, -1, None) except GLib.Error as err: print("Switch user failed: " + err.message) def session_is_cinnamon(): if "cinnamon" in GLib.getenv("DESKTOP_SESSION"): if GLib.find_program_in_path("cinnamon-session-quit"): return True return False def override_user_time(window): ev_time = Gtk.get_current_event_time() window.set_user_time(ev_time) def debug_allocation(alloc): print("x:%d, y:%d, width:%d, height:%d" % (alloc.x, alloc.y, alloc.width, alloc.height)) def CLAMP(value, low, high): return max(low, min(value, high)) def clear_clipboards(widget): clipboard = widget.get_clipboard(Gdk.SELECTION_PRIMARY) clipboard.clear() clipboard.set_text("", -1) clipboard = widget.get_clipboard(Gdk.SELECTION_CLIPBOARD) clipboard.clear() clipboard.set_text("", -1) def do_quit(): Gtk.main_quit() def DEBUG(message): if status.Debug: print(message, flush=True)cinnamon-screensaver-6.2.0/src/util/trackers.py0000664000175000017500000002045514632071776020542 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import GObject, GLib DEBUG_TIMERS=False DEBUG_SIGNALS=False def _debug(*args): first = True output = "" for arg in args: if not first: output += ": " first = False output += str(arg) print(output) def debug_timers(*args): if DEBUG_TIMERS: _debug(*args) def debug_sigs(*args): if DEBUG_SIGNALS: _debug(*args) class TimerTracker: """ Utility class for tracking mainloop timers - they are stored as [name : timeout id] pairs. This is simply a way of managing them without having to individually track source ids. """ def __init__(self): self.timers = {} def do_callback(self, callback, name, *args): ret = callback(*args) if ret: return True self.cancel(name) return False def start(self, name, duration, callback, *args): self.cancel(name) timeout_id = GObject.timeout_add(duration, self.do_callback, callback, name, *args) debug_timers("adding timer of name", name, "duration (sec)", duration / 1000.0, "callback", str(callback), "id", timeout_id) self.timers[name] = timeout_id def start_seconds(self, name, duration, callback, *args): self.cancel(name) timeout_id = GObject.timeout_add_seconds(duration, self.do_callback, callback, name, *args) debug_timers("adding timer of name", name, "duration (sec)", duration, "callback", str(callback), "id", timeout_id) self.timers[name] = timeout_id def add_idle(self, name, callback, *args): self.cancel(name) idle_id = GObject.idle_add(self.do_callback, callback, name, *args) debug_timers("adding idle callback of name", name, "callback", str(callback), "id", idle_id) self.timers[name] = idle_id def cancel(self, name): try: if self.timers[name]: if GLib.MainContext.default().find_source_by_id(self.timers[name]): GObject.source_remove(self.timers[name]) debug_timers("cancel succeeded for", name, "source id", self.timers[name]) else: debug_timers("cancel failed (not a valid id) for", name, "source id", self.timers[name]) del self.timers[name] except KeyError: pass def dump_timer_list(self): print("") print("/******************************************************************") print(" * There should be no timers leftover here *") print(" ******************************************************************/") print("") print("Timer tracker dump:") i = 0 for timer in self.timers: print(timer) i += 1 print("%d timers total" % i) print("") timer_tracker = TimerTracker() def timer_tracker_get(): global timer_tracker return timer_tracker class ConnectionTracker: """ Utility class for tracking GObject signal connections. It is tracked in [ name : (source id, instance) ] pairs. The name is generated as a magic string consisting of "instance hash+signal name+callback hash". As a convenience, we hang a weak reference on both the signal caller's instance as well as the callback's instance. If either of these instances are destroyed we will receive a notification and automatically attempt to disconnect the original signal connection. This will frequently fail, depending on what gets destroyed first (destruction of the source instance will usually mean implicit destruction of the callback.) """ def __init__(self): self.connections = {} def _name(self, instance, signal, callback): name = "%s-%s-%s" % (str(hash(instance)), signal, str(hash(callback))) return name def _disconnect_by_name(self, name): try: if self.connections[name]: (source_id, instance) = self.connections[name] if GObject.signal_handler_is_connected(instance, source_id): instance.disconnect(source_id) debug_sigs("_disconnect_by_name succeeded for", name, "id", source_id) else: debug_sigs("_disconnect_by_name failed (not a valid id) for", name, "id", source_id) del self.connections[name] except KeyError as e: debug_sigs("_disconnect_by_name failed (not being tracked or already disco'd) for", name, str(e)) def _connect_to_dispose(self, name, instance, callback): callback_instance = None try: callback_instance = callback.__self__ if callback_instance and isinstance(callback_instance, GObject.GObject): callback_instance.weak_ref(self._cleanup_disposed, name, "callback_instance") debug_sigs("_connect_to_dispose (callback_instance)", name) except: pass instance.weak_ref(self._cleanup_disposed, name, "instance") debug_sigs("_connect_to_dispose (instance)", name) def _cleanup_disposed(self, name, type_name): debug_sigs("_cleanup_disposed", type_name, name) self._disconnect_by_name(name) def connect(self, instance, signal, callback, *data): """ Wrapper for instance.connect() """ name = self._name(instance, signal, callback) self._disconnect_by_name(name) if data: source_id = instance.connect(signal, callback, *data) else: source_id = instance.connect(signal, callback) self.connections[name] = (source_id, instance) debug_sigs("connected", name, "id", source_id) self._connect_to_dispose(name, instance, callback) def connect_after(self, instance, signal, callback, *data): """ Wrapper for instance.connect_after() """ name = self._name(instance, signal, callback) self._disconnect_by_name(name) if data: source_id = instance.connect_after(signal, callback, data) else: source_id = instance.connect_after(signal, callback) self.connections[name] = (source_id, instance) debug_sigs("connected after", name, "id", source_id) self._connect_to_dispose(name, instance, callback) def handler_block(self, instance, signal, callback): """ Wrapper for g_signal_handler_block(). """ name = self._name(instance, signal, callback) if self.connections[name]: self.connections[name][1].handler_block(self.connections[name][0]) def handler_unblock(self, instance, signal, callback): """ Wrapper for g_signal_handler_unblock() """ name = self._name(instance, signal, callback) if self.connections[name]: self.connections[name][1].handler_unblock(self.connections[name][0]) def disconnect(self, instance, signal, callback): """ Wrapper for instance.disconnect() """ name = self._name(instance, signal, callback) debug_sigs("disconnect", name) self._disconnect_by_name(name) def dump_connections_list(self): print("") print("/******************************************************************") print(" * Currently only these signals should remain after deactivation: *") print(" * g-signal - mediaPlayerWatcher.py (player listener) *") print(" * idle-changed - manager.py (idle listener for GnomeSession) *") print(" * lock - singletons.py (logind/consolekit listeners *") print(" * unlock - singletons.py (logind/consolekit listeners *") print(" * active - singletons.py (logind/consolekit listeners *") print(" ******************************************************************/") print("") if len(self.connections) > 5: print("****** Leftover signal handlers!!") print("Connection tracker dump:") i = 0 for connection in self.connections: print(connection) i += 1 print("%d connections total" % i) print("") connection_tracker = ConnectionTracker() def con_tracker_get(): global connection_tracker return connection_tracker cinnamon-screensaver-6.2.0/src/util/settings.py0000664000175000017500000001121714632071776020560 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio # Background settings, the only one interested in this is Backgrounds in # singletons.py, and then only for the "changed" signal - it reloads # any individual gsettings internally. bg_settings = Gio.Settings(schema_id="org.cinnamon.desktop.background") # Screensaver settings - we have no need to listen to changes here, # They're read anew each time they're used, and while the screensaver # is active, the user won't be changing them. ss_settings = Gio.Settings(schema_id="org.cinnamon.desktop.screensaver") DEFAULT_MESSAGE_KEY = "default-message" SCREENSAVER_NAME_KEY = "screensaver-name" CUSTOM_SCREENSAVER_KEY = "custom-screensaver-command" USER_SWITCH_ENABLED_KEY = "user-switch-enabled" IDLE_ACTIVATE_KEY = "idle-activation-enabled" LOCK_ENABLED_KEY = "lock-enabled" LOCK_DELAY_KEY = "lock-delay" USE_CUSTOM_FORMAT_KEY = "use-custom-format" DATE_FORMAT_KEY = "date-format" TIME_FORMAT_KEY = "time-format" FONT_DATE_KEY = "font-date" FONT_MESSAGE_KEY = "font-message" FONT_TIME_KEY = "font-time" KB_LAYOUT_KEY = "layout-group" SHOW_CLOCK_KEY = "show-clock" SHOW_ALBUMART = "show-album-art" ALLOW_SHORTCUTS = "allow-keyboard-shortcuts" ALLOW_MEDIA_CONTROL = "allow-media-control" SHOW_INFO_PANEL = "show-info-panel" FLOATING_WIDGETS = "floating-widgets" # Interface settings - the same logic applies here as above - we don't # need to listen to changes to these. if_settings = Gio.Settings(schema_id="org.cinnamon.desktop.interface") KBD_LAYOUT_SHOW_FLAGS = "keyboard-layout-show-flags" KBD_LAYOUT_USE_CAPS = "keyboard-layout-use-upper" KBD_LAYOUT_PREFER_VARIANT = "keyboard-layout-prefer-variant-names" osk_settings = Gio.Settings(schema_id="org.cinnamon.keyboard") OSK_TYPE = "keyboard-type" OSK_SIZE = "keyboard-size" OSK_ACTIVATION = "activation-mode" a11y_settings = Gio.Settings(schema_id="org.cinnamon.desktop.a11y.applications") OSK_A11Y_ENABLED = "screen-keyboard-enabled" # Every setting has a getter (and setter, rarely). This is mainly for # organizational purposes and cleanliness - it's easier to read in the # main code if you see "settings.get_default_away_message()" than seeing # "settings.ss_settings.get_string(settings.DEFAULT_MESSAGE_KEY)" or keeping # instances of GioSettings wherever we need them. def _check_string(string): if string and string != "": return string return "" def get_default_away_message(): msg = ss_settings.get_string(DEFAULT_MESSAGE_KEY) return _check_string(msg) def get_custom_screensaver(): cmd = ss_settings.get_string(CUSTOM_SCREENSAVER_KEY) return _check_string(cmd) def get_user_switch_enabled(): return ss_settings.get_boolean(USER_SWITCH_ENABLED_KEY) def get_idle_activate(): return ss_settings.get_boolean(IDLE_ACTIVATE_KEY) def get_idle_lock_enabled(): return ss_settings.get_boolean(LOCK_ENABLED_KEY) def get_idle_lock_delay(): return ss_settings.get_uint(LOCK_DELAY_KEY) def get_use_custom_format(): return ss_settings.get_boolean(USE_CUSTOM_FORMAT_KEY) def get_custom_date_format(): date_format = ss_settings.get_string(DATE_FORMAT_KEY) return _check_string(date_format) def get_custom_time_format(): time_format = ss_settings.get_string(TIME_FORMAT_KEY) return _check_string(time_format) def get_date_font(): date_font = ss_settings.get_string(FONT_DATE_KEY) return _check_string(date_font) def get_message_font(): message_font = ss_settings.get_string(FONT_MESSAGE_KEY) return _check_string(message_font) def get_time_font(): time_font = ss_settings.get_string(FONT_TIME_KEY) return _check_string(time_font) def get_show_flags(): return if_settings.get_boolean(KBD_LAYOUT_SHOW_FLAGS) def get_show_upper_case_layout(): return if_settings.get_boolean(KBD_LAYOUT_USE_CAPS) def get_use_layout_variant_names(): return if_settings.get_boolean(KBD_LAYOUT_PREFER_VARIANT) def get_kb_group(): return ss_settings.get_int(KB_LAYOUT_KEY) def set_kb_group(group): return ss_settings.set_int(KB_LAYOUT_KEY, group) def get_show_clock(): return ss_settings.get_boolean(SHOW_CLOCK_KEY) def get_show_albumart(): return ss_settings.get_boolean(SHOW_ALBUMART) def get_allow_shortcuts(): return ss_settings.get_boolean(ALLOW_SHORTCUTS) def get_allow_media_control(): return ss_settings.get_boolean(ALLOW_MEDIA_CONTROL) def get_show_info_panel(): return ss_settings.get_boolean(SHOW_INFO_PANEL) def get_allow_floating(): return ss_settings.get_boolean(FLOATING_WIDGETS) def get_osk_type(): return osk_settings.get_string(OSK_TYPE) def get_osk_a11y_active(): return a11y_settings.get_boolean(OSK_A11Y_ENABLED) and osk_settings.get_string(OSK_ACTIVATION) == 'accessible'cinnamon-screensaver-6.2.0/src/util/__init__.py0000664000175000017500000000000014632071776020443 0ustar fabiofabiocinnamon-screensaver-6.2.0/src/util/fader.py0000664000175000017500000000722214632071776020002 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import GLib, GObject class Fader: """ The Fader is used by the Stage to fade in from the desktop. It uses a widget tick callback to change the opacity over time. """ def __init__(self, widget): self.widget = widget self.finished_cb = None self.repositioned = False self.starting_opacity = 0.0 self.current_opacity = 0.0 self.target_opacity = 0.0 self.tick_id = 0 self.start_time = 0 self.end_time = 0 def fade_in(self, ms, reposition_cb=None, finished_cb=None): GObject.idle_add(self._fade_in_idle, ms, reposition_cb, finished_cb) def fade_out(self, ms, finished_cb=None): GObject.idle_add(self._fade_out_idle, ms, finished_cb) def cancel(self): if self.tick_id > 0: self.widget.remove_tick_callback(self.tick_id) self.tick_id = 0 def _fade_in_idle(self, ms, reposition_cb=None, finished_cb=None): self.finished_cb = finished_cb self.reposition_cb = reposition_cb self.current_opacity = self.starting_opacity = self.widget.get_opacity() self.target_opacity = 1.0 if self.widget.get_mapped(): self.start_time = self.widget.get_frame_clock().get_frame_time() self.end_time = self.start_time + (ms * 1000) # ms to microsec if self.tick_id == 0: self.tick_id = self.widget.add_tick_callback(self._on_frame_tick_fade_in) self._fade_in_step(self.start_time) else: self.finished_cb() return GLib.SOURCE_REMOVE def _fade_out_idle(self, ms, finished_cb=None): self.finished_cb = finished_cb self.current_opacity = self.starting_opacity = self.widget.get_opacity() self.target_opacity = 0.0 if self.widget.get_mapped(): self.start_time = self.widget.get_frame_clock().get_frame_time() self.end_time = self.start_time + (ms * 1000) # ms to microsec if self.tick_id == 0: self.tick_id = self.widget.add_tick_callback(self._on_frame_tick_fade_out) self._fade_out_step(self.start_time) else: self.finished_cb() return GLib.SOURCE_REMOVE def _on_frame_tick_fade_in(self, widget, clock, data=None): now = clock.get_frame_time() self._fade_in_step(now) if not self.repositioned and self.current_opacity > .03: self.repositioned = True self.reposition_cb() if self.current_opacity == self.target_opacity: self.tick_id = 0 self.finished_cb() return GLib.SOURCE_REMOVE return GLib.SOURCE_CONTINUE def _fade_in_step(self, now): if now < self.end_time: t = ((now - self.start_time) / (self.end_time - self.start_time) * self.target_opacity) else: t = self.target_opacity self.current_opacity = t self.widget.set_opacity(self.current_opacity) def _on_frame_tick_fade_out(self, widget, clock, data=None): now = clock.get_frame_time() self._fade_out_step(now) if self.current_opacity == self.target_opacity: self.tick_id = 0 self.finished_cb() return GLib.SOURCE_REMOVE return GLib.SOURCE_CONTINUE def _fade_out_step(self, now): if now < self.end_time: t = self.starting_opacity - (((now - self.start_time) / (self.end_time - self.start_time)) * self.starting_opacity) else: t = self.target_opacity self.current_opacity = t self.widget.set_opacity(self.current_opacity) cinnamon-screensaver-6.2.0/src/util/eventHandler.py0000664000175000017500000000461614632071776021344 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gdk import status from util.keybindings import KeyBindings MOTION_THRESHOLD = 100 class EventHandler: """ The EventHandler receives all user key, button and motion events for the application. At various points it can be receiving them from the Stage window, an offscreen window, or the root GdkWindow. All events are stopped from passing beyond this point, to prevent any unintended events from propagating to Cinnamon or Muffin. """ def __init__(self, manager): self.manager = manager self.keybindings_handler = KeyBindings(manager) self.last_x = -1 self.last_y = -1 def on_user_activity(self): """ Any user event is a 'wake' event, and is propagated to the stage in order to reset our unlock cancellation timer. """ self.manager.simulate_user_activity() def on_motion_event(self, event): """ Any mouse movement is sent here - there is a threshold to reach when asleep, so that inadvertant motion doesn't wake the system unintentionally. """ if status.Awake: self.on_user_activity() return Gdk.EVENT_PROPAGATE if self.last_x == -1 or self.last_y == -1: self.last_x = event.x self.last_y = event.y return Gdk.EVENT_PROPAGATE distance = max(abs(self.last_x - event.x), abs(self.last_y - event.y)) if distance > MOTION_THRESHOLD: self.on_user_activity() return Gdk.EVENT_PROPAGATE def on_button_press_event(self, event): """ Any button presses are swallowed after interacting with their receiving widgets (in the case of buttons, entry, etc...) """ self.on_user_activity() return Gdk.EVENT_STOP def on_key_press_event(self, event): """ Any key events are checked with the KeybindingHandler in case a media shortcut is being used, or it's a special keystroke such as Escape or tab. Any other presses are sent to the password entry or swallowed. """ if self.keybindings_handler.maybe_handle_event(event): return Gdk.EVENT_STOP if status.Active: if status.Locked: self.manager.queue_dialog_key_event(event) self.on_user_activity() return Gdk.EVENT_STOP cinnamon-screensaver-6.2.0/src/monitorView.py0000664000175000017500000000637014632071776020271 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, Gio, GLib, GObject import re import cairo import signal import status from baseWindow import BaseWindow from util import settings, utils, trackers class WallpaperStack(Gtk.Stack): """ WallpaperStack implements a crossfade when changing backgrounds. An initial image is made and added to the GtkStack. When a new image is requested, it is created and added to the stack, then a crossfade transition is made to the new child. The former visible stack child is then destroyed. And this repeats. """ def __init__(self): super(WallpaperStack, self).__init__() self.set_transition_type(Gtk.StackTransitionType.NONE) self.set_transition_duration(1000) self.initialized = False self.current = None self.queued = None def transition_to_image(self, image): """ Queues a new image in the stack, and begins the transition to it. """ self.queued = image self.queued.set_visible(True) trackers.con_tracker_get().connect_after(image, "draw", self.shade_wallpaper) self.add(self.queued) if not self.initialized: self.visible_image_changed() self.set_transition_type(Gtk.StackTransitionType.CROSSFADE) self.initialized = True return self.set_visible_child(self.queued) GObject.timeout_add(2000, self.visible_image_changed) def visible_image_changed(self, data=None): if self.current is not None: tmp = self.current self.remove(tmp) tmp.destroy() self.current = self.queued self.queued = None return False def shade_wallpaper(self, widget, cr): """ This draw callback adds a shade mask over the current image. It is uniform when not Awake, and acquires a significant gradient vertically framing the unlock dialog when Awake. """ if not status.Awake: cr.set_source_rgba(0.0, 0.0, 0.0, 0.7) cr.paint() return False r = widget.get_allocation() pattern = cairo.LinearGradient(0, 0, 0, r.height) pattern.add_color_stop_rgba (0, 0, 0, 0, .75) pattern.add_color_stop_rgba (.35, 0, 0, 0, .9) pattern.add_color_stop_rgba (.65, 0, 0, 0, .9) pattern.add_color_stop_rgba (1, 0, 0, 0, .75) cr.set_source(pattern) cr.paint() return False class MonitorView(BaseWindow): """ A monitor-sized child of the stage that is responsible for displaying the currently-selected wallpaper or appropriate plug-in. """ def __init__(self, index): super(MonitorView, self).__init__() self.monitor_index = index self.update_geometry() self.wallpaper_stack = WallpaperStack() self.wallpaper_stack.show() self.wallpaper_stack.set_halign(Gtk.Align.FILL) self.wallpaper_stack.set_valign(Gtk.Align.FILL) self.add(self.wallpaper_stack) self.show_all() def set_next_wallpaper_image(self, image): self.wallpaper_stack.transition_to_image(image) cinnamon-screensaver-6.2.0/src/unlock.py0000664000175000017500000003221314632071776017235 0ustar fabiofabio#!/usr/bin/python3 # coding: utf-8 import gi from gi.repository import Gtk, Gdk, GObject, CScreensaver, Gio import traceback from util import utils, trackers import status import singletons from baseWindow import BaseWindow from widgets.framedImage import FramedImage from passwordEntry import PasswordEntry from pamhelper.authClient import AuthClient from widgets.transparentButton import TransparentButton class UnlockDialog(BaseWindow): """ The main widget for the unlock dialog - this is a direct child of the Stage's GtkOverlay. It has a number of parts, namely: - The user face image. - The user's real name (or username if the real name is unavailable) - The password entry widget - Unlock and Switch User buttons - A caps lock warning label - An invalid password error label """ __gsignals__ = { 'inhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'uninhibit-timeout': (GObject.SignalFlags.RUN_LAST, None, ()), 'authenticate-success': (GObject.SignalFlags.RUN_LAST, None, ()), 'authenticate-failure': (GObject.SignalFlags.RUN_LAST, None, ()), 'authenticate-cancel': (GObject.SignalFlags.RUN_LAST, None, ()) } def __init__(self): super(UnlockDialog, self).__init__() settings = Gio.Settings.new("org.cinnamon.desktop.lockdown") self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.CENTER) self.real_name = None self.user_name = None self.auth_info = None self.bounce_rect = None self.bounce_count = 0 self.box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) self.box.get_style_context().add_class("unlockbox") self.add(self.box) self.face_image = FramedImage(status.screen.get_low_res_mode()) self.face_image.set_halign(Gtk.Align.CENTER) self.face_image.get_style_context().add_class("faceimage") self.face_image.set_no_show_all(True) self.box.pack_start(self.face_image, False, False, 10) self.realname_label = Gtk.Label(None) self.realname_label.set_alignment(0, 0.5) self.realname_label.set_halign(Gtk.Align.CENTER) self.box.pack_start(self.realname_label, False, False, 10) self.entry_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, halign=Gtk.Align.CENTER) self.box.pack_start(self.entry_box, True, True, 2) self.password_entry = PasswordEntry() trackers.con_tracker_get().connect(self.password_entry, "changed", self.on_password_entry_text_changed) trackers.con_tracker_get().connect(self.password_entry, "button-press-event", self.on_password_entry_button_press) trackers.con_tracker_get().connect(self.password_entry, "activate", self.on_auth_enter_key) self.entry_box.pack_start(self.password_entry, False, False, 15) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.entry_box.pack_end(button_box, False, False, 0) self.auth_unlock_button = TransparentButton("screensaver-unlock-symbolic", Gtk.IconSize.LARGE_TOOLBAR) self.auth_unlock_button.set_tooltip_text(_("Unlock")) trackers.con_tracker_get().connect(self.auth_unlock_button, "clicked", self.on_unlock_clicked) button_box.pack_start(self.auth_unlock_button, False, False, 4) status.focusWidgets = [self.password_entry, self.auth_unlock_button] if not settings.get_boolean("disable-user-switching"): self.auth_switch_button = TransparentButton("screensaver-switch-users-symbolic", Gtk.IconSize.LARGE_TOOLBAR) self.auth_switch_button.set_tooltip_text(_("Switch User")) trackers.con_tracker_get().connect(self.auth_switch_button, "clicked", self.on_switch_user_clicked) button_box.pack_start(self.auth_switch_button, False, False, 4) status.focusWidgets.append(self.auth_switch_button) vbox_messages = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2) self.capslock_label = Gtk.Label("") self.capslock_label.get_style_context().add_class("caps-message") self.capslock_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.capslock_label, False, False, 2) self.auth_message_label = Gtk.Label("") self.auth_message_label.get_style_context().add_class("auth-message") self.auth_message_label.set_alignment(0.5, 0.5) vbox_messages.pack_start(self.auth_message_label, False, False, 2) self.authinfo_label = Gtk.Label(None) self.authinfo_label.set_alignment(0, 0.5) self.authinfo_label.set_halign(Gtk.Align.CENTER) vbox_messages.pack_start(self.authinfo_label, False, False, 2) self.box.pack_start(vbox_messages, False, False, 0) self.real_name = utils.get_user_display_name() self.user_name = utils.get_user_name() self.update_realname_label() self.account_client = singletons.AccountsServiceClient self.set_user_details() trackers.con_tracker_get().connect(self.account_client, "accounts-ready", self.on_accounts_ready) self.keymap = Gdk.Keymap.get_default() trackers.con_tracker_get().connect(self.keymap, "state-changed", self.keymap_handler) self.keymap_handler(self.keymap) self.auth_client = AuthClient() trackers.con_tracker_get().connect(self.auth_client, "auth-success", self.on_authentication_success) trackers.con_tracker_get().connect(self.auth_client, "auth-failure", self.on_authentication_failure) trackers.con_tracker_get().connect(self.auth_client, "auth-cancel", self.on_authentication_cancelled) trackers.con_tracker_get().connect(self.auth_client, "auth-busy", self.on_authentication_busy_changed) trackers.con_tracker_get().connect(self.auth_client, "auth-prompt", self.on_authentication_prompt_changed) trackers.con_tracker_get().connect(self.auth_client, "auth-info", self.on_authentication_info_changed) self.box.show_all() def initialize_auth_client(self): return self.auth_client.initialize() def on_authentication_success(self, auth_client): self.set_busy(False) self.emit("authenticate-success") def on_authentication_failure(self, auth_client): """ Called upon authentication failure, clears the password, sets an error message, and refocuses the password entry. """ self.set_busy(False) self.auth_message_label.set_text(_("Incorrect password")) self.emit("authenticate-failure") self.emit("uninhibit-timeout") def on_authentication_cancelled(self, auth_client): self.emit("authenticate-cancel") def on_authentication_busy_changed(self, auth_client, busy): self.set_busy(busy) def set_busy(self, busy): if busy: self.auth_message_label.set_text("") self.clear_entry() self.entry_box.set_sensitive(False) self.password_entry.start_progress() self.password_entry.set_placeholder_text (_("Checking...")) else: self.entry_box.set_sensitive(True) self.password_entry.stop_progress() self.password_entry.set_placeholder_text (self.password_entry.placeholder_text) self.auth_info = "" self.update_authinfo_label() def on_authentication_prompt_changed(self, auth_client, prompt): if "password:" in prompt.lower(): prompt = _("Please enter your password...") else: prompt = prompt.replace(":", "") self.password_entry.placeholder_text = prompt self.password_entry.set_placeholder_text(self.password_entry.placeholder_text) def on_authentication_info_changed(self, auth_client, info): self.auth_info = info self.update_authinfo_label() def cancel(self): """ Clears the auth message text if we have any. """ self.auth_client.cancel() self.clear_entry() def queue_key_event(self, event): """ Takes a propagated key event from the stage and passes it to the entry widget, possibly queueing up the first character of the password. """ if not self.password_entry.get_realized(): self.password_entry.realize() self.password_entry.grab_focus() self.password_entry.event(event) def keymap_handler(self, keymap): """ Handler for the GdkKeymap changing - updates our capslock indicator label. """ if keymap.get_caps_lock_state(): self.capslock_label.set_text(_("You have the Caps Lock key on.")) else: self.capslock_label.set_text("") def on_accounts_ready(self, client): """ Handler for the AccountsService - requests the user real name and .face image. """ trackers.con_tracker_get().disconnect(self.account_client, "accounts-ready", self.on_accounts_ready) self.set_user_details() def set_user_details(self): if self.account_client.get_real_name() is not None: self.real_name = self.account_client.get_real_name() self.update_realname_label() if self.account_client.get_face_path() is not None: self.face_image.set_from_path(self.account_client.get_face_path()) self.face_image.show() def on_password_entry_text_changed(self, editable): """ Handler for the password entry text changing - this controls the sensitivity of the unlock button, as well as returning visual focus to the entry any time a key event is received. """ if not self.password_entry.has_focus(): self.password_entry.grab_focus() def on_password_entry_button_press(self, widget, event): """ Prevents the usual copy/paste popup when right-clicking the PasswordEntry. """ if event.button == 3 and event.type == Gdk.EventType.BUTTON_PRESS: return Gdk.EVENT_STOP return Gdk.EVENT_PROPAGATE def on_unlock_clicked(self, button=None): """ Callback for the unlock button. Activates the 'progress' animation in the GtkEntry, and attempts to authenticate the password. During this time, we also inhibit the unlock timeout, so we don't fade out while waiting for an authentication result (highly unlikely.) """ self.emit("inhibit-timeout") text = self.password_entry.get_text() # We must end with a newline, fgets relies upon that to continue. if text[-1:] != "\n": text += "\n" self.auth_client.message_to_child(text) def on_auth_enter_key(self, widget): """ Implicitly activates the unlock button when the Enter/Return key is pressed. """ self.on_unlock_clicked() def on_switch_user_clicked(self, widget): """ Callback for the switch-user button. """ utils.do_user_switch() def clear_entry(self): """ Clear the password entry widget. """ self.password_entry.set_text("") self.auth_info = "" self.update_authinfo_label() def update_realname_label(self): """ Updates the name label to the current real_name. """ self.realname_label.set_text(self.real_name) def update_authinfo_label(self): """ Updates the auth info label to the current auth_info message. """ self.authinfo_label.set_text(self.auth_info) def blink(self): GObject.timeout_add(75, self.on_blink_tick) def on_blink_tick(self, data=None): window = self.get_window() if window is None: return False x, y = window.get_position() if self.bounce_count < 6: if self.bounce_count % 2 == 0: y += 6 else: y -= 6 self.get_window().move(x, y) self.queue_draw() self.bounce_count += 1 return True self.bounce_count = 0 return False cinnamon-screensaver-6.2.0/src/__init__.py0000664000175000017500000000000014632071776017466 0ustar fabiofabiocinnamon-screensaver-6.2.0/src/tests/0000775000175000017500000000000014632071776016531 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/tests/test-auth0000775000175000017500000000066714632071776020406 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CinnamonDesktop', '3.0') from gi.repository import CinnamonDesktop, GLib import getpass name = input("Username: ") password = getpass.getpass() def callback(success): if success: print("\n\nAccepted!") else: print("\n\nFailed!") ml.quit() CinnamonDesktop.desktop_check_user_password(name, password, callback) ml = GLib.MainLoop.new(None, True) ml.run() cinnamon-screensaver-6.2.0/src/tests/test-osk0000775000175000017500000000162714632071776020236 0ustar fabiofabio#!/usr/bin/python3 import sys import signal import gettext import gi gi.require_version('Gtk', '3.0') gi.require_version('CScreensaver', '1.0') sys.path.append("/usr/share/cinnamon-screensaver") gettext.install("cinnamon-screensaver", "/usr/share/locale") from gi.repository import Gtk, CScreensaver from osk import OnScreenKeyboard import status signal.signal(signal.SIGINT, signal.SIG_DFL) class Main: def __init__(self): status.screen = CScreensaver.Screen.new(True) win = Gtk.Window() box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) win.add(box) win.get_style_context().add_class("csstage") self.osk = OnScreenKeyboard() box.pack_start(self.osk, True, True, 2) box.show_all() win.connect("delete-event", lambda w, e: Gtk.main_quit()) win.present() Gtk.main() if __name__ == "__main__": main = Main() cinnamon-screensaver-6.2.0/src/tests/test-notifications0000775000175000017500000000177014632071776022312 0ustar fabiofabio#!/usr/bin/python3 import subprocess import time import os print("Assertion: Notifications should not appear while the screensaver is active") print("Test: Activate the screensaver, then send a test notification a couple seconds later.") print("Expected outcome: Notification should *not* appear over the screensaver window.\n") print("Starting cinnamon-screensaver if necessary...") ps_output = subprocess.check_output(["ps", "-A"]) if "cinnamon-screen" not in ps_output.decode("utf-8"): os.system("cinnamon-screensaver &") time.sleep(2) print("Ok\n") print("Locking screen...") os.system("cinnamon-screensaver-command --lock") print("Ok\n") print("waiting a few seconds...") time.sleep(5) print("Ok\n") print("Sending a notification...") os.system("notify-send 'Test notification' 'You should not see this while the screen is locked'") print("Ok\n") print("waiting a few more seconds...") time.sleep(5) os.system("cinnamon-screensaver-command --deactivate") print("Unlocked. Terminating.") quit() cinnamon-screensaver-6.2.0/src/tests/test-layouts0000775000175000017500000000142014632071776021131 0ustar fabiofabio#!/usr/bin/python3 import sys import signal import gettext import gi gi.require_version('Gtk', '3.0') gi.require_version('CScreensaver', '1.0') sys.path.append("/usr/share/cinnamon-screensaver") gettext.install("cinnamon-screensaver", "/usr/share/locale") from gi.repository import Gtk from unlock import PasswordEntry signal.signal(signal.SIGINT, signal.SIG_DFL) class Main: def __init__(self): win = Gtk.Window() box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) win.add(box) self.entry = PasswordEntry() box.pack_start(self.entry, True, True, 2) box.show_all() win.connect("delete-event", lambda w, e: Gtk.main_quit()) win.present() Gtk.main() if __name__ == "__main__": main = Main() cinnamon-screensaver-6.2.0/src/singletons.py0000664000175000017500000001454214632071776020134 0ustar fabiofabio#!/usr/bin/python3 import gi from util import trackers, settings from util.utils import DEBUG import status # Watch for, and kill, mate- and gnome-screensaver in case they're activated by a program # over dbus. from dbusdepot.nameBlocker import NameBlocker as _NameBlocker NameBlocker = _NameBlocker() # Our dbus proxies are abstracted out one level more than really necessary - we have # clients that the screensaver initializes, that can never fail. The actual connection # business to the various dbus address is performed asynchronously from within each client. # The following clients can fail to establish with their respective dbus interfaces without # competely breaking the program (or at least that's what we're after) - it just means that # depending on what fails, you may end up without keyboard shortcut support, or a battery # widget, etc... from dbusdepot.cinnamonClient import CinnamonClient as _CinnamonClient from dbusdepot.sessionClient import SessionClient as _SessionClient from dbusdepot.uPowerClient import UPowerClient as _UPowerClient from dbusdepot.keybindingHandlerClient import KeybindingHandlerClient as _KeybindingHandlerClient from dbusdepot.mediaPlayerWatcher import MediaPlayerWatcher as _MediaPlayerWatcher from dbusdepot.accountsServiceClient import AccountsServiceClient as _AccountsServiceClient from dbusdepot.muffinClient import MuffinClient as _MuffinClient CinnamonClient = _CinnamonClient() SessionClient = _SessionClient() UPowerClient = _UPowerClient() KeybindingHandlerClient = _KeybindingHandlerClient() MediaPlayerWatcher = _MediaPlayerWatcher() AccountsServiceClient = _AccountsServiceClient() # The notification watcher is a C introspected class - some of the functions it uses # don't work well via introspection. from gi.repository import CScreensaver NotificationWatcher = CScreensaver.NotificationWatcher.new(status.Debug) # We only need one instance of CinnamonDesktop.BG - have it listen to bg gsettings changes # and we just connect to "changed" on the Backgrounds object from our user (the Stage) gi.require_version('CinnamonDesktop', '3.0') from gi.repository import CinnamonDesktop Backgrounds = CinnamonDesktop.BG() Backgrounds.load_from_preferences(settings.bg_settings) settings.bg_settings.connect("changed", lambda s,k: Backgrounds.load_from_preferences(s)) # We use XAppKbdLayoutController as a wrapper around libgnomekbd to supply the icon theme # with icons, as well as providing correct group names. gi.require_version('XApp', '1.0') from gi.repository import XApp KeyboardLayoutController = XApp.KbdLayoutController() # This sets up synchronously, as we need to know the fractional scaling state before # setting up the screensaver. MuffinClient = _MuffinClient() # The login client is a bit different - we can have either logind or ConsoleKit. # So, we have to do a bit more work to determine which one we're going to use. # This doesn't really need to impact the main startup business though - whichever # one we end up using, all we're doing is connecting to signals from one or the # other client. Whichever we end up with is invisible/not relevant to the rest of # the application. from dbusdepot.consoleKitClient import ConsoleKitClient from dbusdepot.logindClient import LogindClient class LoginClientResolver: def __init__(self, manager): self.manager = manager self.login_client = None self.try_logind() def try_logind(self): print("Trying to connect to logind...", flush=True) login_client = LogindClient() trackers.con_tracker_get().connect(login_client, "startup-status", self.on_logind_startup_result) def on_logind_startup_result(self, client, success): trackers.con_tracker_get().disconnect(client, "startup-status", self.on_logind_startup_result) if success: print("Successfully using logind", flush=True) self.login_client = client self.setup_manager_connections() else: print("Failed to connect to logind, or it doesn't exist.", flush=True) self.try_console_kit() def try_console_kit(self): print("Trying to connect to ConsoleKit...", flush=True) login_client = ConsoleKitClient() trackers.con_tracker_get().connect(login_client, "startup-status", self.on_consolekit_startup_result) def on_consolekit_startup_result(self, client, success): trackers.con_tracker_get().disconnect(client, "startup-status", self.on_consolekit_startup_result) if success: print("Successfully using ConsoleKit", flush=True) self.login_client = client self.setup_manager_connections() else: print("Failed to connect to ConsoleKit, or it doesn't exist.\n", flush=True) print("Unable to connect to either logind or ConsoleKit. Certain things will not work,", flush=True) print("such as automatic unlocking when switching users from the desktop manager,", flush=True) print("or locking in appropriate power/system-management events.", flush=True) def setup_manager_connections(self): trackers.con_tracker_get().connect(self.login_client, "lock", self.on_session_manager_lock) trackers.con_tracker_get().connect(self.login_client, "unlock", self.on_session_manager_unlock) trackers.con_tracker_get().connect(self.login_client, "active", self.on_session_manager_active) def on_session_manager_lock(self, client): DEBUG("Received Lock from session manager") self.manager.lock() def on_session_manager_unlock(self, client): DEBUG("Received Unlock from session manager") self.manager.unlock() def on_session_manager_active(self, client): DEBUG("Received Active changed from session manager") self.manager.refresh_stage() cinnamon-screensaver-6.2.0/src/dbusdepot/0000775000175000017500000000000014632071776017360 5ustar fabiofabiocinnamon-screensaver-6.2.0/src/dbusdepot/consoleKitClient.py0000664000175000017500000000535514632071776023213 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GLib, CScreensaver from dbusdepot.baseClient import BaseClient from dbusdepot.loginInterface import LoginInterface class ConsoleKitClient(LoginInterface, BaseClient): """ Client for communicating with ConsoleKit - this is a backup to logind, if it's not available. """ CK_SERVICE = "org.freedesktop.ConsoleKit" CK_MANAGER_PATH = "/org/freedesktop/ConsoleKit/Manager" def __init__(self): """ We first try to connect to the ConsoleKit manager. """ super(ConsoleKitClient, self).__init__(Gio.BusType.SYSTEM, CScreensaver.ConsoleKitManagerProxy, self.CK_SERVICE, self.CK_MANAGER_PATH) self.session_proxy = None self.session_id = None def on_client_setup_complete(self): """ If our manager connection succeeds, we ask it for the current session id and then attempt to connect to its session interface. """ self.session_id = self.proxy.call_get_current_session_sync() try: self.session_proxy = CScreensaver.ConsoleKitSessionProxy.new_for_bus(Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, self.CK_SERVICE, self.session_id, None, self.on_session_ready, None) except GLib.Error: self.session_proxy = None self.on_failure() def on_session_ready(self, object, result, data=None): """ Once we're connected to the session interface, we can respond to signals sent from it - used primarily when returning from suspend, hibernation or the login screen. """ self.session_proxy = CScreensaver.ConsoleKitSessionProxy.new_for_bus_finish(result) self.session_proxy.connect("unlock", lambda proxy: self.emit("unlock")) self.session_proxy.connect("lock", lambda proxy: self.emit("lock")) self.session_proxy.connect("active-changed", self.on_active_changed) self.emit("startup-status", True) def on_active_changed(self, proxy, active, data=None): if active: self.emit("active") def on_failure(self, *args): self.emit("startup-status", False) cinnamon-screensaver-6.2.0/src/dbusdepot/meson.build0000664000175000017500000000060114632071776021517 0ustar fabiofabioapp_py = [ '__init__.py', 'accountsServiceClient.py', 'baseClient.py', 'cinnamonClient.py', 'consoleKitClient.py', 'keybindingHandlerClient.py', 'logindClient.py', 'loginInterface.py', 'mediaPlayerWatcher.py', 'muffinClient.py', 'nameBlocker.py', 'sessionClient.py', 'uPowerClient.py' ] install_data(app_py, install_dir: join_paths(pkgdatadir, 'dbusdepot')) cinnamon-screensaver-6.2.0/src/dbusdepot/mediaPlayerWatcher.py0000664000175000017500000002321014632071776023502 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GLib, CScreensaver, GObject from enum import IntEnum from dbusdepot.baseClient import BaseClient from util import trackers class PlaybackStatus(IntEnum): Unknown = 0 Playing = 1 Paused = 2 Stopped = 3 class MprisClient(BaseClient): """ Represents a media player with an mpris dbus interface. There can be as many of these as there are players active in the session, but we only control the first active one in our list. These are instantiated by our MediaPlayerWatcher. """ __gsignals__ = { "status-changed": (GObject.SignalFlags.RUN_LAST, None, (int,)), "metadata-changed": (GObject.SignalFlags.RUN_LAST, None, ()) } def __init__(self, name, path): super(MprisClient, self).__init__(Gio.BusType.SESSION, CScreensaver.MediaPlayerProxy, name, path) self.identity = None self.metadata = None self.album_name = "" self.track_name = "" self.artist_name = "" self.albumart_url = "" def on_client_setup_complete(self): trackers.con_tracker_get().connect(self.proxy, "notify::playback-status", self.on_playback_status_changed) trackers.con_tracker_get().connect(self.proxy, "notify::metadata", self.on_metadata_changed) self.ensure_metadata() # This isn't the app-provided MediaPlayer.Identity, but we'd need to # set up another proxy and it's not really necessary for what this is # used for. self.identity = self.proxy.get_name().rpartition(".")[2] def get_identity(self): return self.identity def get_playback_status(self): status = PlaybackStatus.Unknown if self.ensure_proxy_alive(): str_prop = self.proxy.get_property("playback-status") try: status = PlaybackStatus(eval("PlaybackStatus." + str_prop)) except (ValueError, TypeError, SyntaxError): pass return status def get_can_play_pause(self): if self.ensure_proxy_alive(): return self.proxy.get_property("can-play") or self.proxy.get_property("can-pause") return False def get_can_control(self): if self.ensure_proxy_alive(): return self.proxy.get_property("can-control") return False def play_pause(self): if self.ensure_proxy_alive(): self.proxy.call_play_pause() def get_can_go_next(self): if self.ensure_proxy_alive(): return self.proxy.get_property("can-go-next") return False def go_next(self): if self.ensure_proxy_alive(): self.proxy.call_next() def get_can_go_previous(self): if self.ensure_proxy_alive(): return self.proxy.get_property("can-go-previous") return False def go_previous(self): if self.ensure_proxy_alive(): self.proxy.call_previous() def get_name(self): if self.ensure_proxy_alive(): return self.proxy.get_name() return "" def get_track_name(self): self.ensure_metadata() return self.track_name def get_artist_name(self): self.ensure_metadata() return self.artist_name def get_album_name(self): self.ensure_metadata() return self.album_name def get_albumart_url(self): self.ensure_metadata() return self.albumart_url def on_failure(self, *args): pass def return_best_string(self, item): if type(item) == list: return ", ".join(item) elif type(item) == str: return item else: return "" def ensure_metadata(self): if not self.metadata: self.metadata = self.proxy.get_property("metadata") if self.metadata: try: self.track_name = self.return_best_string(self.metadata["xesam:title"]) except KeyError: self.track_name = "" try: self.album_name = self.return_best_string(self.metadata["xesam:album"]) except KeyError: self.album_name = "" try: self.artist_name = self.return_best_string(self.metadata["xesam:albumArtist"]) except KeyError: try: self.artist_name = self.return_best_string(self.metadata["xesam:artist"]) except: self.artist_name = "" try: self.albumart_url = self.return_best_string(self.metadata["mpris:artUrl"]) except KeyError: self.albumart_url = "" def on_playback_status_changed(self, proxy, pspec, data=None): self.emit("status-changed", self.get_playback_status()) def on_metadata_changed(self, proxy, pspec, data=None): self.metadata = None self.ensure_metadata() self.emit("metadata-changed") class MediaPlayerWatcher(GObject.Object): """ Media player interfaces are different from our other interfaces. There is no common owned name, players export their own unique interface, within the org.mpris.MediaPlayer2.* namespace. This allows multiple players to exist on the bus at one time. It requires us to list all interfaces and filter out all but the ones in that namespace. We then create separate Player clients for each one. """ MPRIS_PATH = "/org/mpris/MediaPlayer2" def __init__(self): """ Connect to the bus and retrieve a list of interfaces. """ super(MediaPlayerWatcher, self).__init__() self.player_clients = [] try: self.dbus_proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION, Gio.DBusProxyFlags.NONE, None, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", None) trackers.con_tracker_get().connect(self.dbus_proxy, "g-signal", self.on_dbus_proxy_signal) self.find_initial_players() except GLib.Error: self.dbus_proxy = None print("Cannot acquire session org.freedesktop.DBus client to watch for media players") def on_dbus_proxy_signal(self, proxy, sender, signal, parameters, data=None): """ NameOwnerChanged is called both when a name appears on the bus, as well as when it disappears. The filled parameters tell us whether we've gained or lost the player. """ if signal == "NameOwnerChanged": if parameters[2] != "": self.on_name_acquired(parameters[0]) else: self.on_name_lost(parameters[0]) def find_initial_players(self): self.dbus_proxy.call("ListNames", None, Gio.DBusCallFlags.NONE, -1, None, self.on_names_listed) def on_names_listed(self, bus, result, data=None): names = bus.call_finish(result)[0] for name in names: self.on_name_acquired(name) def on_name_acquired(self, name): """ Create an mpris client for any discovered interfaces. """ if name.startswith("org.mpris.MediaPlayer2."): self.player_clients.append(MprisClient(name, self.MPRIS_PATH)) def on_name_lost(self, name): """ Remove any clients that disappear off the bus. """ item = None for client in self.player_clients: if client.get_name() == name: item = client break if item: self.player_clients.remove(item) def get_best_player(self): """ Find the first player in our list that is playing, then for one that *can* be played. Players that are simply loaded but don't have any playlist queued up should not pass these tests - we have only limited control from the lockscreen. """ first_idle = None for client in self.player_clients: if client.get_playback_status() == PlaybackStatus.Playing: return client if first_idle is None and client.get_can_play_pause() and client.get_can_control(): first_idle = client return first_idle def get_all_player_names(self): """ Return a list of all player simple names - this is used by our notification code to ignore notifications sent from media players, which are usually unimportant, but not marked as transient (which would normally cause them to be ignored in the CsNotificationWatcher.) """ ret = [] for client in self.player_clients: fullname = client.get_name() split = fullname.split(".") ret.append(split[len(split) - 1].lower()) return ret cinnamon-screensaver-6.2.0/src/dbusdepot/uPowerClient.py0000664000175000017500000001372414632071776022361 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GObject, CScreensaver, GLib from enum import IntEnum from dbusdepot.baseClient import BaseClient class DeviceType(IntEnum): Unknown = 0 LinePower = 1 Battery = 2 Ups = 3 Monitor = 4 Mouse = 5 Keyboard = 6 Pda = 7 Phone = 8 class DeviceState(IntEnum): Unknown = 0 Charging = 1 Discharging = 2 Empty = 3 FullyCharged = 4 PendingCharge = 5 PendingDischarge = 6 class UPowerClient(BaseClient): """ This client communicates with the upower provider, tracking the power state of the system (laptops - are we on battery or plugged in?) """ __gsignals__ = { 'power-state-changed': (GObject.SignalFlags.RUN_LAST, None, ()), 'percentage-changed': (GObject.SignalFlags.RUN_LAST, None, (GObject.Object,)) } UPOWER_SERVICE = "org.freedesktop.UPower" UPOWER_PATH = "/org/freedesktop/UPower" def __init__(self): super(UPowerClient, self).__init__(Gio.BusType.SYSTEM, CScreensaver.UPowerProxy, self.UPOWER_SERVICE, self.UPOWER_PATH) self.have_battery = False self.plugged_in = False self.update_state_id = 0 self.devices_dirty = True self.relevant_devices = [] def on_client_setup_complete(self): self.proxy.connect("device-removed", self.on_device_added_or_removed) self.proxy.connect("device-added", self.on_device_added_or_removed) self.queue_update_state() def on_device_added_or_removed(self, proxy, path): self.devices_dirty = True self.queue_update_state() def on_battery_changed(self, proxy, pspec, data=None): self.queue_update_state() def queue_update_state(self): if self.update_state_id > 0: GObject.source_remove(self.update_state_id) self.update_state_id = 0 GObject.idle_add(self.idle_update_cb) def idle_update_cb(self, data=None): if self.devices_dirty: self.rescan_devices() self.devices_dirty = False if self.update_state(): self.emit_changed() def rescan_devices(self): if len(self.relevant_devices) > 0: for path, dev in self.relevant_devices: dev.disconnect(dev.prop_changed_id) del dev del path self.relevant_devices = [] try: # The return type for this call has to be overridden in gdbus-codegen # (See the Makefile.am) - or else we get utf-8 errors (python3 issue?) for path in self.proxy.call_enumerate_devices_sync(): try: dev = CScreensaver.UPowerDeviceProxy.new_for_bus_sync(Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, self.UPOWER_SERVICE, path, None) if dev.get_property("type") in (DeviceType.Battery, DeviceType.LinePower): self.relevant_devices.append((path, dev)) dev.prop_changed_id = dev.connect("notify", self.on_device_properties_changed) except GLib.Error: print("UPowerClient had trouble connecting with device:", path, " - skipping it") except GLib.Error: print("UPowerClient had trouble enumerating through devices. The battery indicator will be disabled") self.queue_update_state() def update_state(self): changes = False old_plugged_in = self.plugged_in old_have_battery = self.have_battery # UPower doesn't necessarily have a LinePower device if there are no batteries. # Default to plugged in, then. new_plugged_in = False new_have_battery = False for path, dev in self.relevant_devices: if dev.get_property("type") == DeviceType.Battery: new_plugged_in = dev.get_property("state") != DeviceState.Discharging new_have_battery = True if (new_plugged_in != old_plugged_in) or (new_have_battery != old_have_battery): changes = True self.have_battery = new_have_battery self.plugged_in = new_plugged_in return changes def on_device_properties_changed(self, proxy, pspec, data=None): if pspec.name in ("online", "icon-name", "state"): self.queue_update_state() if pspec.name == "percentage": self.emit_percentage_changed(proxy) def emit_changed(self): self.emit("power-state-changed") def emit_percentage_changed(self, battery): self.emit("percentage-changed", battery) def get_batteries(self): if len(self.relevant_devices) == 0: return [] ret = [] for path, dev in self.relevant_devices: if dev.get_property("type") == DeviceType.Battery: ret.append((path, dev)) return ret def full_and_on_ac_or_no_batteries(self): """ This figures out whether the power widget should be shown or not - currently we only show the widget if we have batteries and are not plugged in. """ batteries = self.get_batteries() if batteries == []: return True all_batteries_full = True for path, dev in batteries: if dev.get_property("state") not in (DeviceState.FullyCharged, DeviceState.Unknown): all_batteries_full = False break return self.plugged_in and all_batteries_full def on_failure(self, *args): print("Failed to establish a connection with UPower - the battery indicator will be disabled.") cinnamon-screensaver-6.2.0/src/dbusdepot/baseClient.py0000664000175000017500000000575114632071776022013 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GObject, GLib class BaseClient(GObject.GObject): """ The base constructor for all of our generated GDBusProxies. This initializes and sets self.proxy, then fires on_client_setup_complete() or on_failure(), which the subclasses implement, depending on the outcome. These clients are technically one more level of abstraction than would be needed in a perfect world, where all dbus proxies work as they should and are supported properly by the actual providers they proxy for, but they provide a convenient and relatively clean way of implementing workarounds and alternate ways of retrieving or calculating values when the interface itself is broken in some way (this is common.) They also provide a bit of fault tolerance in cases where one or more of these providers do not exist on the bus, and we can provide sane default values for our widgets. """ def __init__(self, bustype, proxy_class, service, path): """ Asynchronously initialize the GDBusProxy - we'll call on_client_setup_complete() or on_failure() depending on this outcome. """ super(BaseClient, self).__init__() self.proxy_class = proxy_class self.path = path self.proxy = None self.watch_name_id = Gio.bus_watch_name(bustype, service, Gio.BusNameWatcherFlags.NONE, self._on_appeared, self.on_failure) def _on_appeared(self, connection, name, name_owner, data=None): try: self.proxy_class.new(connection, Gio.DBusProxyFlags.NONE, name, self.path, None, self._on_proxy_ready) except GLib.Error: self.proxy = None self.on_failure() Gio.bus_unwatch_name(self.watch_name_id) def _on_proxy_ready(self, object, result, data=None): self.proxy = self.proxy_class.new_finish(result) self.on_client_setup_complete() def ensure_proxy_alive(self): """ Use this as a safety check to see if a given proxy is valid and owned. """ return self.proxy and self.proxy.get_name_owner() is not None def on_client_setup_complete(self): """ Subclasses must implement this - to complete setup after self.proxy is successfully initialized. """ print("You need to implement on_client_setup_complete(self) in your real client class") raise NotImplementedError def on_failure(self, *args): """ Can be implemented by subclasses, but not necessary. Nothing further is done anyhow if on_client_setup_complete() is never called. """ pass cinnamon-screensaver-6.2.0/src/dbusdepot/loginInterface.py0000664000175000017500000000103514632071776022662 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import GObject class LoginInterface(GObject.Object): """ A common signal interface for our Logind and ConsoleKit clients. """ __gsignals__ = { 'startup-status': (GObject.SignalFlags.RUN_LAST, None, (bool, )), 'lock': (GObject.SignalFlags.RUN_LAST, None, ()), 'unlock': (GObject.SignalFlags.RUN_LAST, None, ()), 'active': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self, *args): super(LoginInterface, self).__init__(*args) cinnamon-screensaver-6.2.0/src/dbusdepot/accountsServiceClient.py0000664000175000017500000000573014632071776024236 0ustar fabiofabio#!/usr/bin/python3 import gi from gi.repository import GObject, CScreensaver, Gio, GLib import os import time from util import utils, trackers class AccountsServiceClient(GObject.Object): """ Singleton for working with the AccountsService, which we use to retrieve the user's face image and their real name. """ ACCOUNTS_SERVICE = "org.freedesktop.Accounts" ACCOUNTS_PATH = "/org/freedesktop/Accounts" __gsignals__ = { 'accounts-ready': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): super(AccountsServiceClient, self).__init__() self.accounts = None self.user = None print("Loading AccountsService") CScreensaver.AccountsServiceProxy.new_for_bus(Gio.BusType.SYSTEM, Gio.DBusProxyFlags.DO_NOT_AUTO_START, self.ACCOUNTS_SERVICE, self.ACCOUNTS_PATH, None, self.on_accounts_connected) def on_accounts_connected(self, source, res): try: self.accounts = CScreensaver.AccountsServiceProxy.new_for_bus_finish(res) except GLib.Error as e: print(f"Could not connect to AccountsService: {e}", flush=True) return self.accounts.call_find_user_by_name(utils.get_user_name(), None, self.got_user_proxy) def got_user_proxy(self, source, res): try: proxy_path = self.accounts.call_find_user_by_name_finish(res) except GLib.Error as e: print(f"Could not get AccountsService User object path: {e}", flush=True) return CScreensaver.AccountsUserProxy.new_for_bus(Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, self.ACCOUNTS_SERVICE, proxy_path, None, self.on_user_loaded) def on_user_loaded(self, source, res): try: self.user = CScreensaver.AccountsUserProxy.new_for_bus_finish(res) except GLib.Error as e: print(f"Could not create AccountsService.User: {e}", flush=True) print("AccountsService ready") self.emit("accounts-ready") def get_real_name(self): if self.user is not None: return self.user.get_property("real-name") return None def get_face_path(self): face = os.path.join(GLib.get_home_dir(), ".face") if os.path.exists(face): return face if self.user is not None: accounts_path = self.user.get_property("icon-file") if os.path.exists(accounts_path): return accounts_path return None cinnamon-screensaver-6.2.0/src/dbusdepot/sessionClient.py0000664000175000017500000000245514632071776022562 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GObject, CScreensaver from dbusdepot.baseClient import BaseClient class SessionClient(BaseClient): """ This client is for connecting to the session manager's Presence interface - it is responsible for triggering activation of the screensaver when the session goes into an idle state. """ __gsignals__ = { 'idle-changed': (GObject.SignalFlags.RUN_LAST, None, (bool, )), } GSM_SERVICE = "org.gnome.SessionManager" GSM_PRESENCE_PATH = "/org/gnome/SessionManager/Presence" def __init__(self): super(SessionClient, self).__init__(Gio.BusType.SESSION, CScreensaver.SessionPresenceProxy, self.GSM_SERVICE, self.GSM_PRESENCE_PATH) self.idle = False def on_client_setup_complete(self): self.proxy.connect("status-changed", self.on_status_changed) def on_status_changed(self, proxy, status): new_idle = status == 3 if new_idle != self.idle: self.idle = new_idle self.emit("idle-changed", self.idle) def on_failure(self, *args): print("Failed to connect to session manager - idle detection will not work.") cinnamon-screensaver-6.2.0/src/dbusdepot/logindClient.py0000664000175000017500000000615014632071776022347 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GLib, CScreensaver import os import subprocess import status from dbusdepot.baseClient import BaseClient from dbusdepot.loginInterface import LoginInterface from util.utils import DEBUG class LogindClient(LoginInterface, BaseClient): """ A client for communicating with logind. At startup we check for its availability, falling back to ConsoleKit if it's not available. """ LOGIND_SERVICE = "org.freedesktop.login1" LOGIND_PATH = "/org/freedesktop/login1" def __init__(self): """ We first try to connect to the logind manager. """ super(LogindClient, self).__init__(Gio.BusType.SYSTEM, CScreensaver.LogindManagerProxy, self.LOGIND_SERVICE, self.LOGIND_PATH) self.pid = os.getpid() self.session_path = None self.session_proxy = None def on_client_setup_complete(self): """ If our manager connection succeeds, we get the current session path and attempt to connect to its interface. """ try: current_user = GLib.get_user_name() cmd = "loginctl show-user %s -pDisplay --value" % current_user current_session_id = subprocess.check_output(cmd, shell=True).decode().replace("\n", "") self.session_path = self.proxy.call_get_session_sync(current_session_id, None) DEBUG("login client: found session path for user '%s' (session_id: %s): %s" % (current_user, current_session_id, self.session_path)) except GLib.Error as e: print("login client: could not get session path: %s" % e, flush=True) self.on_failure() return CScreensaver.LogindSessionProxy.new_for_bus(Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, self.LOGIND_SERVICE, self.session_path, None, self.on_session_ready, None) def on_session_ready(self, object, result, data=None): """ Once we're connected to the session interface, we can respond to signals sent from it - used primarily when returning from suspend, hibernation or the login screen. """ self.session_proxy = CScreensaver.LogindSessionProxy.new_for_bus_finish(result) self.session_proxy.connect("unlock", lambda proxy: self.emit("unlock")) self.session_proxy.connect("lock", lambda proxy: self.emit("lock")) self.session_proxy.connect("notify::active", self.on_active_changed) self.emit("startup-status", True) def on_active_changed(self, proxy, pspec, data=None): if self.session_proxy.get_property("active"): self.emit("active") def on_failure(self, *args): self.emit("startup-status", False) cinnamon-screensaver-6.2.0/src/dbusdepot/nameBlocker.py0000664000175000017500000000350614632071776022160 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, GObject, GLib import status from util.utils import DEBUG class NameBlocker(GObject.GObject): """ Terminates other screensavers. Ideally we'd just take ownership of their dbus names, but due to how DE detection works in xdg-screensaver, that script incorrectly assumes we're a gnome or mate session if their names are owned. """ def __init__(self): super(NameBlocker, self).__init__() self.owned_names = [] self.watch("org.gnome.ScreenSaver") self.watch("org.mate.ScreenSaver") def watch(self, name): handle = Gio.bus_watch_name(Gio.BusType.SESSION, name, Gio.BusNameWatcherFlags.NONE, self.on_name_appeared, self.on_name_lost) self.owned_names.append((handle, "name")) def unwatch_all(self): for (handle, name) in self.owned_names: DEBUG("Releasing dbus name: %s" % name) Gio.bus_unown_name(handle) self.owned_names = [] def on_name_appeared(self, connection, name, name_owner, data=None): DEBUG("%s appeared on the session bus, killing it" % name) connection.call(name_owner, "/" + name.replace(".", "/"), name, "Quit", None, None, Gio.DBusCallFlags.NONE, -1, None) def on_name_lost(self, connection, name, data=None): DEBUG("%s is gone from the session bus" % name) def do_dispose(self): DEBUG("nameBlocker do_dispose") self.unwatch_all() super(NameBlocker, self).do_dispose() cinnamon-screensaver-6.2.0/src/dbusdepot/__init__.py0000664000175000017500000000000014632071776021457 0ustar fabiofabiocinnamon-screensaver-6.2.0/src/dbusdepot/cinnamonClient.py0000664000175000017500000000225414632071776022676 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, CScreensaver from dbusdepot.baseClient import BaseClient class CinnamonClient(BaseClient): """ Simple client to talk to Cinnamon's dbus interface. Currently its only use is for attempting to force an exit from overview and expo mode (both of which do a fullscreen grab and would prevent the screensaver from acquiring one.) """ CINNAMON_SERVICE = "org.Cinnamon" CINNAMON_PATH = "/org/Cinnamon" def __init__(self): super(CinnamonClient, self).__init__(Gio.BusType.SESSION, CScreensaver.CinnamonProxy, self.CINNAMON_SERVICE, self.CINNAMON_PATH) def on_client_setup_complete(self): pass def exit_expo_and_overview(self): if self.ensure_proxy_alive(): self.proxy.set_property("overview-active", False) self.proxy.set_property("expo-active", False) def on_failure(self, *args): print("Failed to connect to Cinnamon - screensaver will not activate when expo or overview modes are active.", flush=True) cinnamon-screensaver-6.2.0/src/dbusdepot/keybindingHandlerClient.py0000664000175000017500000000222714632071776024515 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, CScreensaver from dbusdepot.baseClient import BaseClient class KeybindingHandlerClient(BaseClient): """ Connects to the media key interface of cinnamon-settings-daemon. This calls the keybinding handler for shortcuts received by the Keybindings object. """ KEYBINDING_HANDLER_SERVICE = "org.cinnamon.SettingsDaemon.KeybindingHandler" KEYBINDING_HANDLER_PATH = "/org/cinnamon/SettingsDaemon/KeybindingHandler" def __init__(self): super(KeybindingHandlerClient, self).__init__(Gio.BusType.SESSION, CScreensaver.KeybindingHandlerProxy, self.KEYBINDING_HANDLER_SERVICE, self.KEYBINDING_HANDLER_PATH) def on_client_setup_complete(self): pass def handle_keybinding(self, mk_type): if self.proxy: self.proxy.call_handle_keybinding(mk_type, None, None) def on_failure(self, *args): print("Failed to connect to the keybinding handler - media key shortcuts will not work.") cinnamon-screensaver-6.2.0/src/dbusdepot/muffinClient.py0000664000175000017500000000655514632071776022370 0ustar fabiofabio#!/usr/bin/python3 import gi gi.require_version('CScreensaver', '1.0') from gi.repository import GLib, Gio, GObject, CScreensaver # TODO # self.monitors, etc.. replace or at least prefer this over CsScreen, as it will be more accurate. # Nothing currently listens to muffin-config-changed. This class is only used to initialize the event filters. class MuffinClient(GObject.Object): MUFFIN_SERVICE = "org.cinnamon.Muffin.DisplayConfig" MUFFIN_PATH = "/org/cinnamon/Muffin/DisplayConfig" __gsignals__ = { 'muffin-config-changed': (GObject.SignalFlags.RUN_LAST, None, ()), } def __init__(self): GObject.Object.__init__(self) self.proxy = None self.using_fractional_scaling = False try: self.proxy = CScreensaver.MuffinDisplayConfigProxy.new_for_bus_sync(Gio.BusType.SESSION, Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | Gio.DBusProxyFlags.DO_NOT_AUTO_START, self.MUFFIN_SERVICE, self.MUFFIN_PATH, None) self.proxy.connect("monitors-changed", self.on_monitors_changed) # cinnamon restart (monitors-changed isn't emitted at muffin startup) self.proxy.connect("notify::g-name-owner", self.on_name_owner_changed) self.update() except GLib.Error as e: print(f"Could not connect to Muffin's DisplayConfig service: {e}", flush=True) def on_monitors_changed(self, proxy): self.update() def on_name_owner_changed(self, proxy, pspec): if proxy.get_name_owner() is not None: self.update() def update(self): if self.read_current_state(): self.emit("muffin-config-changed") def read_current_state(self, *args): old_scaling = self.using_fractional_scaling if self.proxy.get_name_owner() is None: print("Muffin not running, skipping fractional scaling check.") return False try: logical_monitors = self.proxy.call_get_current_state_sync(None)[2] except GLib.Error as e: print(f"Could not read current state from Muffin: {e}", flush=True) self.using_fractional_scaling = False return self.using_fractional_scaling != old_scaling fractional = False previous_scale = -1 for monitor in logical_monitors.unpack(): scale = monitor[2] # one or more monitors using some non-integer scale. if int(scale) != scale: fractional = True break # multiple monitors with non-identical scales if previous_scale > 0 and scale != previous_scale: fractional = True break previous_scale = scale self.using_fractional_scaling = fractional print(f"Fractional scaling active: {self.using_fractional_scaling}", flush=True) return self.using_fractional_scaling != old_scaling def get_using_fractional_scaling(self): return self.using_fractional_scaling cinnamon-screensaver-6.2.0/src/config.py.in0000664000175000017500000000042114632071776017610 0ustar fabiofabio# Generated file - DO NOT EDIT. Edit config.py.in instead. prefix="@prefix@" datadir="@datadir@" localedir=datadir+"/locale" pkgdatadir="@pkgdatadir@" libdir="@libdir@" libexecdir="@libexecdir@" PACKAGE="@PACKAGE@" VERSION="@VERSION@" GETTEXT_PACKAGE="@GETTEXT_PACKAGE@" cinnamon-screensaver-6.2.0/src/albumArt.py0000664000175000017500000000463314632071776017516 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gio, Gtk from util import trackers, settings from baseWindow import BaseWindow from floating import Floating from widgets.framedImage import FramedImage import singletons import status class AlbumArt(Floating, BaseWindow): """ AlbumArt It is a child of the Stage's GtkOverlay, and its placement is controlled by the overlay's child positioning function. When not Awake, it positions itself around all monitors using a timer which randomizes its halign and valign properties as well as its current monitor. """ def __init__(self, away_message=None, initial_monitor=0): super(AlbumArt, self).__init__(initial_monitor) self.get_style_context().add_class("albumart") self.set_halign(Gtk.Align.END) if not settings.get_show_albumart(): return self.watcher = singletons.MediaPlayerWatcher self.player = self.watcher.get_best_player() self.current_url = None self.image = FramedImage(status.screen.get_low_res_mode(), scale_up=True) self.image.show() self.image.set_opacity(0.0) self.add(self.image) trackers.con_tracker_get().connect(self.image, "surface-changed", self.on_surface_changed) if self.player is not None: trackers.con_tracker_get().connect(self.player, "metadata-changed", self.on_metadata_changed) self.on_metadata_changed(self.player) def on_surface_changed(self, image, surface): if surface is not None: self.image.set_opacity(1.0) else: self.image.set_opacity(0.0) def on_metadata_changed(self, player): self.update_image() def update_image(self): url = self.player.get_albumart_url() if self.player.get_identity() == "spotify": url = url.replace("open.spotify.com", "i.scdn.co") if url == self.current_url: return self.current_url = url if url == "": self.image.clear_image() f = Gio.File.new_for_uri(url) if f.get_uri_scheme() == "file": self.image.set_from_path(f.get_path()) elif f.get_uri_scheme() == "http": self.image.set_from_file(f) cinnamon-screensaver-6.2.0/src/binfile.in0000664000175000017500000000025614632071776017332 0ustar fabiofabio#!/bin/sh if [ "$XDG_SESSION_TYPE" = "wayland" ]; then echo "cinnamon-screensaver is disabled in wayland sessions. Exiting." exit 1 fi exec @install_dir@/@target@ "$@" cinnamon-screensaver-6.2.0/src/passwordEntry.py0000664000175000017500000002226114632071776020630 0ustar fabiofabio#!/usr/bin/python3 # coding: utf-8 import gi gi.require_version('CinnamonDesktop', '3.0') from gi.repository import Gtk, Gdk, GdkPixbuf, GLib import cairo from util import trackers, settings import singletons class PasswordEntry(Gtk.Entry): """ The GtkEntry where the user types their password. It also implements a clickable imagine showing the currently chosen keyboard layout, and allowing switching of the layout. """ def __init__(self): super(PasswordEntry, self).__init__(max_length=200) self.get_style_context().add_class("passwordentry") placeholder_text = _("Please enter your password...") self.set_width_chars(len(placeholder_text) + 1) # account for the flag self.set_has_frame(True) self.set_input_purpose(Gtk.InputPurpose.PASSWORD) self.set_visibility(False) self.set_property("caps-lock-warning", False) self.set_placeholder_text (placeholder_text) self.set_can_default(True) self.placeholder_text = placeholder_text self.current_icon_name = None self.current_flag_id = 0 self.original_group = 0 self.keyboard_controller = singletons.KeyboardLayoutController trackers.con_tracker_get().connect(self.keyboard_controller, "config-changed", self.on_config_changed) trackers.con_tracker_get().connect(self.keyboard_controller, "layout-changed", self.on_layout_changed) self.set_lockscreen_keyboard_layout() trackers.con_tracker_get().connect(self, "destroy", self.on_destroy) def on_draw(self, widget, cr, data=None): """ GtkEntry always makes its icons menu-sized, no matter how much actual space is available for the image. So, we use a transparent icon in update_layout_icon(), just so GtkEntry thinks there's an icon there, that way it allocates space for it, and responds to clicks in the area. """ if not self.keyboard_controller.get_enabled(): return False icon_rect = widget.get_icon_area(Gtk.EntryIconPosition.PRIMARY) x = icon_rect.x y = icon_rect.y + 2 width = (icon_rect.width // 2) * 2 height = icon_rect.height - 4 handled = False if settings.get_show_flags(): name = self.keyboard_controller.get_current_icon_name() ui_scale = self.get_scale_factor() if name: filename = "/usr/share/iso-flag-png/%s.png" % name try: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, -1, height * ui_scale) logical_width = pixbuf.get_width() / ui_scale logical_height = pixbuf.get_height() / ui_scale render_x = (x + (width / 2) - (logical_width / 2)) render_y = (y + (height / 2) - (logical_height / 2)) if pixbuf: surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, ui_scale, self.get_window()) cr.set_source_surface(surface, render_x, render_y) cr.paint() self.keyboard_controller.render_cairo_subscript(cr, render_x + (logical_width / 2), render_y + (logical_height / 2), logical_width / 2, logical_height / 2, self.keyboard_controller.get_current_flag_id()) handled = True except GLib.Error: pass if not handled: if settings.get_use_layout_variant_names(): name = self.keyboard_controller.get_current_variant_label() else: name = self.keyboard_controller.get_current_short_group_label() if settings.get_show_upper_case_layout(): name = name.upper() ctx = widget.get_style_context() ctx.save() ctx.set_state(Gtk.StateFlags.BACKDROP) font_size = ctx.get_property("font-size", Gtk.StateFlags.BACKDROP) family = ctx.get_property("font-family", Gtk.StateFlags.BACKDROP) cr.select_font_face(family[0], cairo.FONT_WEIGHT_NORMAL, cairo.FONT_SLANT_NORMAL) cr.set_font_size(font_size) (xb, yb, w, h, xa, ya) = cr.text_extents(name) # Drop shadow for visibility - 1px, 1px cr.set_source_rgba(0, 0, 0, 0.8) cr.move_to((x + (width / 2) - (w / 2)) + 1, (y + (height / 2) + (h / 2) + 1)) cr.show_text(name) # Text text_color = widget.get_style_context().get_color(Gtk.StateFlags.BACKDROP) Gdk.cairo_set_source_rgba(cr, text_color) cr.move_to((x + (width / 2) - (w / 2)), (y + (height / 2) + (h / 2))) cr.show_text(name) ctx.restore() return False def start_progress(self): self.set_progress_pulse_step(0.2) trackers.timer_tracker_get().start("auth-progress", 100, self.pulse) def stop_progress(self): trackers.timer_tracker_get().cancel("auth-progress") self.set_progress_fraction(0.0) def pulse(self): """ Periodic callback for the progress bar. """ self.progress_pulse() return True def on_layout_changed(self, controller, layout): self.grab_focus() self.update_layout_icon() def on_config_changed(self, controller): self.set_lockscreen_keyboard_layout() def on_icon_pressed(self, entry, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.PRIMARY: self.keyboard_controller.next_group() def update_layout_icon(self): """ Set an empty icon here so the widget responds to clicks and allocates space for it. We'll do the actual flag or whatever in the 'draw' callback. Setting the icon here also ensures a redraw at the correct time to update the flag image. """ self.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, "screensaver-blank") self.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY, self.keyboard_controller.get_current_name()) self.update_saved_group(self.keyboard_controller.get_current_group()) def on_destroy(self, widget, data=None): self.stop_progress() trackers.con_tracker_get().disconnect(self.keyboard_controller, "config-changed", self.on_config_changed) trackers.con_tracker_get().disconnect(self.keyboard_controller, "layout-changed", self.on_layout_changed) self.restore_original_layout() def set_lockscreen_keyboard_layout(self): if not self.keyboard_controller.get_enabled(): return # If there are multiple keyboard layouts, we want to store # the one the user ends up using in the unlock widget, as they'll # want to use the same one each time, at least until they change # their password. saved_group = settings.get_kb_group() self.original_group = self.keyboard_controller.get_current_group() new_group = 0 if saved_group == -1: new_group = self.original_group else: new_group = saved_group self.keyboard_controller.set_current_group(new_group) self.update_saved_group(new_group) self.update_layout_icon() trackers.con_tracker_get().connect(self, "icon-press", self.on_icon_pressed) trackers.con_tracker_get().connect(self, "draw", self.on_draw) def update_saved_group(self, group): settings.set_kb_group(group) def restore_original_layout(self): """ Called when the unlock dialog is destroyed, restores the group that was active before the screensaver was activated. """ if not self.keyboard_controller.get_enabled(): return self.keyboard_controller.set_current_group(self.original_group) def grab_focus(self): Gtk.Widget.grab_focus(self) length = self.get_buffer().get_length() self.select_region(length, -1) cinnamon-screensaver-6.2.0/src/baseWindow.py0000664000175000017500000000131014632071776020036 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gtk, GObject, Gdk from util import trackers import status class BaseWindow(Gtk.Bin): """ BaseWindow is the base class for all of the Stage GtkOverlay's immediate children. """ def __init__(self, *args): super(BaseWindow, self).__init__() self.disabled = False c = Gdk.RGBA(0, 0, 0, 0) self.override_background_color (Gtk.StateFlags.NORMAL, c) def destroy_window(self): self.destroy() def update_geometry(self): if status.Spanned: self.rect = status.screen.get_screen_geometry() else: self.rect = status.screen.get_monitor_geometry(self.monitor_index) cinnamon-screensaver-6.2.0/src/manager.py0000664000175000017500000004317214632071776017362 0ustar fabiofabio#!/usr/bin/python3 from gi.repository import Gdk, GObject, GLib, Gio, CScreensaver import time import traceback import os import signal import subprocess import config import constants as c import status from stage import Stage import singletons from util import utils, settings, trackers from util.focusNavigator import FocusNavigator from util.utils import DEBUG class ScreensaverManager(GObject.Object): """ The ScreensaverManager is the central point where most major decision are made, and where ScreensaverService requests are acted upon. """ __gsignals__ = { 'active-changed': (GObject.SignalFlags.RUN_LAST, None, (bool, )), } def __init__(self): super(ScreensaverManager, self).__init__() self.activated_timestamp = 0 self.stage = None self.old_stage = None self.stage_refresh_id = 0 self.refreshing = False self.refresh_again = False self.fb_pid = 0 self.fb_failed_to_start = False # Ensure our state status.Active = False status.Locked = False status.Awake = False self.focus_nav = FocusNavigator() self.session_client = singletons.SessionClient trackers.con_tracker_get().connect(self.session_client, "idle-changed", self.on_session_idle_changed) self.cinnamon_client = singletons.CinnamonClient singletons.LoginClientResolver(self) def is_locked(self): """ Return if we're Locked - we could be Active without being locked. """ return status.Locked def set_locked(self, locked): if locked: status.Locked = True if status.UseFallback: self.spawn_fallback_window() else: status.Locked = False if status.UseFallback: self.kill_fallback_window() def lock(self, msg=""): """ Initiate locking (activating first if necessary.) Return True if we were already active and just need to set the lock flag (or we were already locked as well). Return False if we're not active, and need to construct a stage, etc... """ if not status.Active: if self.set_active(True, msg): self.stop_lock_delay() if utils.user_can_lock(): self.set_locked(True) return False else: if utils.user_can_lock(): self.set_locked(True) self.stage.set_message(msg) # Return True to complete any invocation immediately because: # - we were already active and possibly already locked # - we were unable to achieve a server grab, or something else # prevents the activation from proceeding, and we don't want to # block anything...?? return True def unlock(self): """ Initiate unlocking and deactivating """ self.set_active(False) self.set_locked(False) status.Awake = False def set_active(self, active, msg=None): """ Activates or deactivates the screensaver. Activation involves: - sending a request to Cinnamon to exit Overview or Expo - this could prevent a successful screen grab and keep the screensaver from activating. - grabbing the keyboard and mouse. - creating the screensaver Stage. Deactivation involves: - destroying the screensaver stage. - releasing our keyboard and mouse grabs. """ if active: if not status.Active: self.cinnamon_client.exit_expo_and_overview() self.grab_helper = CScreensaver.EventGrabber.new(status.Debug) status.screen = CScreensaver.Screen.new(status.Debug) if self.grab_helper.grab_root(False): if not self.stage: Gio.Application.get_default().hold() self.spawn_stage(msg, self.on_spawn_stage_complete) else: self.stage.activate(self.on_spawn_stage_complete) self.stage.set_message(msg) return True else: status.Active = False return False else: self.stage.set_message(msg) return True else: if self.stage: self.grab_helper.release() self.despawn_stage() Gio.Application.get_default().release() status.focusWidgets = [] status.screen = None return True def get_active(self): """ Return whether we're Active or not (showing) - this is not necessarily Locked. """ return status.Active def get_active_time(self): """ Return how long we've been activated, or 0 if we're not """ if self.activated_timestamp != 0: return int(time.time() - self.activated_timestamp) else: return 0 def simulate_user_activity(self): """ Called upon any key, motion or button event, does different things depending on our current state. If we're idle: - do nothing If we're locked: - show the unlock widget (if it's already visible, this also has the effect of resetting the unlock timeout - see Stage.py) - show the mouse pointer, so the user can navigate the unlock screen. If we're Active but not Locked, simply deactivate (destroying the Stage and returning the screensaver back to idle mode.) """ if not status.Active: return if not status.Awake: DEBUG("*******************************************") DEBUG("manager: user activity, waking:") # traceback.print_stack() DEBUG("*******************************************") if status.Locked and self.stage.initialize_pam(): if not status.Awake: DEBUG("manager: locked, raising unlock widget") self.stage.raise_unlock_widget() self.grab_helper.release_mouse() self.stage.maybe_update_layout() else: DEBUG("manager: not locked, queueing idle deactivation") trackers.timer_tracker_get().add_idle("idle-deactivate", self.idle_deactivate) def idle_deactivate(self): self.set_active(False) trackers.timer_tracker_get().cancel("idle-deactivate") return False def spawn_stage(self, away_message, callback=None): """ Create the Stage and begin fading it in. This may run quickly, in the case of user-initiated activation, or slowly, when the session has gone idle. """ try: self.stage = Stage(self, away_message) self.stage.activate(callback) except Exception: print("Could not spawn screensaver stage:\n") traceback.print_exc() self.grab_helper.release() status.Active = False self.cancel_timers() def spawn_fallback_window(self): if self.fb_pid > 0: return DEBUG("manager: spawning fallback window") if self.stage.get_realized(): self._real_spawn_fallback_window(self) else: self.stage.connect("realize", self._real_spawn_fallback_window) def get_tty_vals(self): session_tty = None term_tty = None username = GLib.get_user_name()[:8] used_tty = [] try: tty_output = subprocess.check_output(["w", "-h"]).decode("utf-8") for line in tty_output.split("\n"): if line.startswith(username): if "cinnamon-session" in line and "tty" in line: session_tty = line.split()[1].replace("tty", "") used_tty.append(session_tty) elif "tty" in line: term_tty = line.split()[1].replace("tty", "") elif "tty" in line: used_tty.append(line.split()[1].replace("tty", "")) used_tty.sort() if term_tty is None: for i in range(1, 6): if str(i) not in used_tty: term_tty = str(i) break except Exception as e: print("Failed to get tty numbers using w -h: %s" % str(e)) if session_tty is None: try: session_tty = os.environ["XDG_VTNR"] except KeyError: session_tty = "7" if term_tty is None: term_tty = "2" if session_tty != "2" else "1" return [term_tty, session_tty] def _real_spawn_fallback_window(self, stage, data=None): if self.fb_pid > 0: return term_tty, session_tty = self.get_tty_vals() argv = [ os.path.join(config.libexecdir, "cs-backup-locker"), "--xid", str(self.stage.get_window().get_xid()), "--term", term_tty, "--session", session_tty ] if status.Debug: argv.append("--debug") try: self.fb_pid = GLib.spawn_async(argv)[0] except GLib.Error as e: self.fb_failed_to_start = True print("Could not start screensaver fallback process: %s" % e.message, flush=True) try: self.stage.disconnect_by_func(self._real_spawn_fallback_window) except: pass def kill_fallback_window(self): if self.fb_pid == 0 and not self.fb_failed_to_start: return DEBUG("manager: killing fallback window") try: DEBUG("manager: checking if fallback window exists first.") if self.fb_pid > 0: os.kill(self.fb_pid, 0) elif self.fb_failed_to_start: raise ProcessLookupError("Fallback window failed to start") except ProcessLookupError: DEBUG("manager: fallback window terminated before the main screensaver, something went wrong!") notification = Gio.Notification.new(_("Cinnamon Screensaver has experienced an error")) notification.set_body(_("The 'cs-backup-locker' process terminated before the screensaver did. " "Please report this issue and try to describe any actions you may " "have performed prior to this occurring.")) notification.set_icon(Gio.ThemedIcon(name="dialog-error")) notification.set_priority(Gio.NotificationPriority.URGENT) Gio.Application.get_default().send_notification("cinnamon-screensaver", notification) try: os.kill(self.fb_pid, signal.SIGTERM) except: pass self.fb_failed_to_start = False self.fb_pid = 0 def on_spawn_stage_complete(self): """ Called after the stage become visible. All user events are now redirected to GrabHelper, our status is updated, our active timer is started, and emit an active-changed signal (Which is listened to by our ConsoleKit client if we're using it, and our own ScreensaverService.) """ self.grab_stage() self.stage.connect("needs-refresh", self.queue_refresh_stage) if self.old_stage: self.old_stage.hide() self.old_stage.destroy_stage() self.old_stage = None self.stage_refreshed() status.Active = True self.emit("active-changed", True) self.start_timers() def despawn_stage(self): """ The stage is destroyed, our status is updated, timer is canceled and active-changed is fired. """ self.stage.cancel_unlocking() self.stage.hide() was_active = status.Active == True status.Active = False if was_active: self.emit("active-changed", False) self.cancel_timers() self.stage.destroy_stage() self.stage = None # Ideal time to check for leaking connections that might prevent GC by python and gobject if status.Debug: trackers.con_tracker_get().dump_connections_list() trackers.timer_tracker_get().dump_timer_list() def grab_stage(self): """ Makes a hard grab on the Stage window, all keyboard and mouse events are dispatched or eaten by us now. """ if self.stage is not None: self.grab_helper.move_to_window(self.stage.get_window(), Gdk.Screen.get_default(), True) def queue_refresh_stage(self, stage): """ Queues a complete refresh of the stage, resizing the screen if necessary, reconstructing the individual monitor objects, etc... """ if self.stage_refresh_id > 0: GObject.source_remove(self.stage_refresh_id) self.stage_refresh_id = 0 DEBUG("manager: queuing stage refresh") # self.stage_refresh_id = GLib.timeout_add_seconds(3, self._update_full_stage_on_idle, priority=GLib.PRIORITY_DEFAULT) self.stage_refresh_id = GLib.idle_add(self._update_full_stage_on_idle, priority=GLib.PRIORITY_DEFAULT) def _update_full_stage_on_idle(self, data=None): self.stage_refresh_id = 0 if self.refreshing: self.refresh_again = True return Gdk.flush() self.refreshing = True self.refresh_stage() return False def stage_refreshed(self): DEBUG("manager: stage refresh complete") self.grab_stage() self.refreshing = False if self.refresh_again: self.refresh_again = False DEBUG("Got refresh signal while refreshing, doing it again.") self.queue_refresh_stage() self.simulate_user_activity() def refresh_stage(self, stage=None): """ Tells the stage to check its canvas size and make sure its windows are up-to-date. This is called when our login manager tells us its "Active" property has changed. We are always connected to the login manager, so we first check if we have a stage. """ if self.stage is None: return DEBUG("Stage: refreshing") self.stage.cancel_unlocking() self.cancel_timers() status.focusWidgets = [] self.grab_helper.release() away_message = self.stage.away_message self.old_stage = self.stage self.spawn_stage(away_message, self.on_spawn_stage_complete) def start_timers(self): """ Stamps our current time starts our lock delay timer (the elapsed time to allow after activation, to lock the computer.) """ self.activated_timestamp = time.time() self.start_lock_delay() def cancel_timers(self): """ Zeros out our activated timestamp and cancels our lock delay timer. """ self.activated_timestamp = 0 self.stop_lock_delay() def cancel_unlocking(self): """ Return to sleep (not Awake) - hides the pointer and the unlock widget. """ self.grab_stage() self.stage.cancel_unlocking() def on_lock_delay_timeout(self): """ Updates the lock status when our timer has hit its limit """ DEBUG("manager: locking after delay ('lock-delay')") self.set_locked(True) return False def start_lock_delay(self): """ Setup the lock delay timer based on user prefs - if there is no delay, or if idle locking isn't enabled, we run the callback immediately, or simply return, respectively. """ if not settings.get_idle_lock_enabled(): return if not utils.user_can_lock(): return lock_delay = settings.get_idle_lock_delay() if lock_delay == 0: self.on_lock_delay_timeout() else: trackers.timer_tracker_get().start_seconds("idle-lock-delay", lock_delay, self.on_lock_delay_timeout) def stop_lock_delay(self): """ Cancels the lock delay timer. """ trackers.timer_tracker_get().cancel("idle-lock-delay") ##### EventHandler/GrabHelper/FocusNavigator calls. def queue_dialog_key_event(self, event): """ Forwards a captured key event to the stage->unlock dialog. """ self.stage.queue_dialog_key_event(event) def propagate_tab_event(self, shifted): """ Forwards a tab event to the focus navigator. """ self.focus_nav.navigate(shifted) def propagate_activation(self): """ Forwards an activation event (return) to the focus navigator. """ self.focus_nav.activate_focus() def get_focused_widget(self): """ Returns the currently focused widget from the FocusNavigator """ return self.focus_nav.get_focused_widget() def on_session_idle_changed(self, proxy, idle): """ Call back for the session client - initiates a slow fade-in for the stage when the session goes idle. Cancels the stage fade-in if idle becomes False before it has completed its animation. """ DEBUG("manager: session idle-changed - %s" % str(idle)) if idle and not status.Active: self.set_active(True) cinnamon-screensaver-6.2.0/src/osk.py0000664000175000017500000002216514632071776016543 0ustar fabiofabio#!/usr/bin/python3 # coding: utf-8 import gi gi.require_version('Caribou', '1.0') from gi.repository import Gtk, Gdk, GObject, Caribou, Gio, GLib import status from util import utils, trackers, settings from widgets.transparentButton import TransparentButton from baseWindow import BaseWindow LARGEST_OSK_WIDTH = 1200 LARGEST_OSK_HEIGHT = 360 DEFAULT_PADDING = 2 class ExtendedKey(Gtk.Button): def __init__(self, label, xkey): super(ExtendedKey, self).__init__(label) self.get_style_context().add_class("osk-button") self._key = xkey self.connect("button-press-event", lambda widget, event: self._key.press()) self.connect("button-release-event", lambda widget, event: self._key.release()) def update_sizes(self, width, height): real_width = width * self._key.props.width self.set_size_request(real_width, height) class Key(Gtk.Button): def __init__(self, key): super(Key, self).__init__() self.get_style_context().add_class("osk-button") self._key = key self.checked = False self._extended_keys = key.get_extended_keys() self._extended_keyboard = None self._grabbed = False self._eventCaptureId = 0 self.set_label(self._key.props.label) self._popup = None self._popover_box = None if self._extended_keys: self._key.connect("notify::show-subkeys", self._on_show_subkeys_changed) self._popup = Gtk.Popover(relative_to=self) self._popup.get_style_context().add_class("osk-popover") self.get_extended_keys() self.connect("button-press-event", self.button_press_event) self.connect("button-release-event", self.button_release_event) if self._key.props.name in ("Control_L", "Alt_L"): self.model_press_handler = self._key.connect("key-pressed", self._model_key_pressed) self.model_release_handler = self._key.connect("key-released", self._model_key_released) def update_sizes(self, width, height): # The virtual key width is a multiplier based on the default key width. # Keys such as the spacebar use this to become proportionally wider than # other keys. real_width = width * self._key.props.width self.set_size_request(real_width, height) if self._popover_box: for child in self._popover_box.get_children(): child.update_sizes(width, height) def _model_key_pressed(self, key, data=None): print("pressed model") def _model_key_released(self, key, data=None): print("released model") def button_press_event(self, widget, event, data=None): # Pressing buttons quickly can be recognized as a double- or triple-click, ignore # when that happens. Extended keys should only appear on a click-hold. if event.type in (Gdk.EventType._2BUTTON_PRESS, Gdk.EventType._3BUTTON_PRESS): return Gdk.EVENT_PROPAGATE self._key.press() return Gdk.EVENT_PROPAGATE def button_release_event(self, widget, event, data=None): self._key.release() return Gdk.EVENT_PROPAGATE def get_uni_char(self, key): keyval = key.props.keyval unichar = Gdk.keyval_to_unicode(keyval) if unichar: return chr(unichar) else: return key.props.name def get_extended_keys(self): self._popover_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, margin=6) for xkey in self._extended_keys: label = self.get_uni_char(xkey) key = ExtendedKey(label, xkey) self._popover_box.pack_start(key, False, False, 2) self._popover_box.show_all() self._popup.add(self._popover_box) def _on_show_subkeys_changed(self, key, pspec, data=None): if self._key.props.show_subkeys: self._popup.popup() else: self._popup.popdown() class OnScreenKeyboard(BaseWindow): """ An on-screen keyboard that can be used to input the password in the lockscreen. If accessibility doesn't have the osk enabled, we don't construct the keyboard initially, to save footprint. If needed, the keyboard button can be pressed to display the keyboard on demand, and it gets constructed then. """ def __init__(self): super(OnScreenKeyboard, self).__init__() self.set_halign(Gtk.Align.CENTER) self.set_valign(Gtk.Align.END) self.props.margin = 30 smallest_width, smallest_height = status.screen.get_smallest_monitor_sizes() self.max_width = min(smallest_width, LARGEST_OSK_WIDTH) - 60 self.max_height = min(smallest_height / 3, LARGEST_OSK_HEIGHT) - 60 # print(self.max_width, self.max_height) self._group_stack = None self.base_stack = Gtk.Stack() self.add(self.base_stack) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, halign=Gtk.Align.CENTER, valign=Gtk.Align.END) activate_button = TransparentButton("input-keyboard-symbolic", Gtk.IconSize.LARGE_TOOLBAR) activate_button.connect("clicked", self.on_activate_button_clicked) box.pack_start(activate_button, False, False, 0) box.show_all() self.base_stack.add_named(box, "disabled") self.base_stack.show_all() if settings.get_osk_a11y_active(): self.build_and_show_keyboard() def on_activate_button_clicked(self, button, data=None): self.build_and_show_keyboard() def on_caribou_button_clicked(self, button, data=None): self.base_stack.set_visible_child_name("disabled") def build_and_show_keyboard(self): if not self._group_stack: self._keyboard = Caribou.KeyboardModel(keyboard_type=settings.get_osk_type()) self._group_stack = Gtk.Stack(visible=True) self._groups = {} self._add_keys() self._group_stack.show_all() self.base_stack.add_named(self._group_stack, "enabled") self.base_stack.set_visible_child_name("enabled") def _add_keys(self): groups = self._keyboard.get_groups() size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) for group_name in groups: group = self._keyboard.get_group(group_name) group.connect("notify::active-level", self._on_level_changed) layers = {} levels = group.get_levels() for level_name in levels: level = group.get_level(level_name) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) size_group.add_widget(box) self._load_rows(level, box) layers[level_name] = box box.show_all() self._group_stack.add_named(box, "%s::%s" % (group_name, level_name)) self._groups[group_name] = layers self.set_active_layer() def _on_level_changed(self, object, pspec, data=None): self.set_active_layer() def _load_rows(self, level, box): rows = level.get_rows() row_height = self.max_height / len(rows) for row in rows: self._add_rows(row.get_columns(), box, row_height) def _add_rows(self, keys, box, row_height): num_keys = 0 row_children = [] row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) for key in keys: right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) children = key.get_children() right_box_members = [] for child in children: button = Key(child) num_keys += child.props.width row_children.append(button) if child.props.align == "right": right_box_members.append(button) else: left_box.pack_start(button, False, False, DEFAULT_PADDING) if right_box_members: right_box_members.reverse() for member in right_box_members: right_box.pack_end(member, False, False, DEFAULT_PADDING) if child.props.name == "Caribou_Prefs": button.connect("clicked", self.on_caribou_button_clicked) if left_box.get_children(): row.pack_start(left_box, True, True, 0) if right_box.get_children(): row.pack_end(right_box, True, True, 0) key_width = (self.max_width / num_keys) - (DEFAULT_PADDING * 2) key_height = row_height - (DEFAULT_PADDING * 2) for child in row_children: child.update_sizes(key_width, key_height) box.pack_start(row, False, False, DEFAULT_PADDING) def set_active_layer(self): active_group_name = self._keyboard.props.active_group active_group = self._keyboard.get_group(active_group_name) active_level = active_group.props.active_level layers = self._groups[active_group_name] self._group_stack.set_visible_child_name("%s::%s" % (active_group_name, active_level))