├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom-module-proposal.md │ └── feature_request.md └── workflows │ ├── ci.yaml │ └── codeql.yaml ├── .gitignore ├── CMakeLists.txt ├── COPYING ├── Extra ├── completions │ ├── _clight │ ├── clight │ └── clight.fish ├── config │ ├── clight.conf │ └── modules.conf.d │ │ ├── backlight.conf │ │ ├── daytime.conf │ │ ├── dimmer.conf │ │ ├── dpms.conf │ │ ├── gamma.conf │ │ ├── inhibit.conf │ │ ├── keyboard.conf │ │ ├── screen.conf │ │ └── sensor.conf ├── desktop │ ├── clight.desktop │ └── clightc.desktop ├── icons │ └── clight.svg ├── man │ └── clight.1 ├── org.clight.clight.service └── skeletons │ ├── README.md │ ├── inhibit_bl.skel │ ├── nightmode.skel │ └── synctemp_bumblebee.skel ├── README.md ├── TODO.md └── src ├── commons.h ├── conf ├── config.c ├── config.h ├── opts.c └── opts.h ├── main.c ├── modules ├── backlight.c ├── bus.c ├── bus.h ├── daytime.c ├── dimmer.c ├── display.c ├── dpms.c ├── gamma.c ├── inhibit.c ├── interface.c ├── interface.h ├── keyboard.c ├── location.c ├── pm.c ├── screen.c ├── signal.c ├── upower.c └── wizard.c ├── public.h ├── pubsub ├── topics.c ├── validations.c └── validations.h └── utils ├── idler.c ├── idler.h ├── log.c ├── log.h ├── my_math.c ├── my_math.h ├── timer.c ├── timer.h ├── utils.c └── utils.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: A report of an issue in Clight 4 | title: "[BUG] " 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **NOTE:** Please try to upgrade Clight if not running on latest release, before reporting a bug! 11 | 12 | **Clight version the issue has been seen with:** 13 | > ... 14 | 15 | **Used distribution:** 16 | > ... 17 | 18 | **Describe the bug** 19 | A clear and concise description of what the bug is. 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **To Reproduce** 25 | Steps to reproduce the behavior. 26 | 27 | Please attach a Clight [verbose log](https://github.com/FedeDP/Clight/wiki/FAQ#where-is-clight-log) too! 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom-module-proposal.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom Module proposal 3 | about: Propose a new custom module to be installed together with clight, as skeleton 4 | title: "[CUSTOM] " 5 | labels: Contribution 6 | assignees: FedeDP 7 | 8 | --- 9 | 10 | **Custom Module's behaviour** 11 | Why did you create this custom module? Ie: what's its purpose? 12 | 13 | **Custom Module's code** 14 | Share your module's code here! 15 | ``` 16 | #include 17 | 18 | CLIGHT_MODULE("MYMOD"); 19 | 20 | static void init(void) { 21 | 22 | } 23 | 24 | static void receive(const msg_t *msg, const void *userdata) { 25 | switch (MSG_TYPE()) { 26 | default: 27 | break; 28 | } 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea/improvement for Clight 4 | title: "[FEATURE REQ]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. EG: I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI Build 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-ubuntu-amd64: 11 | name: build-ubuntu-amd64 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Check out libmodule 16 | uses: actions/checkout@v3 17 | with: 18 | repository: fededp/libmodule 19 | ref: '5.0.1' 20 | path: libmodule 21 | - name: Install deps 22 | run: | 23 | sudo apt update 24 | sudo apt install -y --no-install-recommends build-essential pkg-config cmake libsystemd-dev libpopt-dev libconfig-dev libgsl-dev libdbus-1-dev 25 | - name: Install libmodule 26 | run: | 27 | cd libmodule 28 | mkdir build && cd build 29 | cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_BUILD_TYPE="Release" .. 30 | make 31 | sudo make install 32 | - name: Configure project 33 | run: | 34 | mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. 35 | - name: Build 36 | run: | 37 | cd build && make 38 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | branches: [ "master" ] 19 | 20 | jobs: 21 | analyze: 22 | name: Analyze 23 | runs-on: ubuntu-latest 24 | permissions: 25 | actions: read 26 | contents: read 27 | security-events: write 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | language: [ 'cpp' ] 33 | 34 | steps: 35 | - name: Checkout repository 36 | uses: actions/checkout@v3 37 | with: 38 | fetch-depth: 0 39 | 40 | - name: Check out libmodule 41 | uses: actions/checkout@v3 42 | with: 43 | repository: fededp/libmodule 44 | ref: '5.0.1' 45 | path: libmodule 46 | 47 | - name: Initialize CodeQL 48 | uses: github/codeql-action/init@v2 49 | with: 50 | languages: ${{ matrix.language }} 51 | 52 | - name: Update base image 53 | run: sudo apt update -y 54 | 55 | - name: Install build dependencies 56 | run: sudo DEBIAN_FRONTEND=noninteractive apt install build-essential pkg-config cmake libsystemd-dev libpopt-dev libconfig-dev libgsl-dev libdbus-1-dev -y 57 | 58 | - name: Install libmodule 59 | run: | 60 | cd libmodule 61 | mkdir build && cd build 62 | cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_BUILD_TYPE="Release" .. 63 | make 64 | sudo make install 65 | 66 | - name: Build project 67 | run: | 68 | mkdir build && cd build 69 | cmake .. 70 | make 71 | 72 | - name: Perform CodeQL Analysis 73 | uses: github/codeql-action/analyze@v2 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.kdev4 2 | *.o 3 | .kdev4/ 4 | build/ 5 | Debian/ 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(clight VERSION 4.12 LANGUAGES C) 4 | 5 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 6 | 7 | include(GNUInstallDirs) 8 | find_package(PkgConfig) 9 | 10 | # Needed folders 11 | set(CLIGHT_CONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/clight" 12 | CACHE PATH "Path for config file") 13 | set(CLIGHT_DATADIR "${CMAKE_INSTALL_FULL_DATADIR}/clight" 14 | CACHE PATH "Path for data dir folder") 15 | 16 | execute_process( 17 | COMMAND sh -c "test -d .git && git log -1 --format=%h" 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 19 | OUTPUT_VARIABLE GIT_HASH 20 | OUTPUT_STRIP_TRAILING_WHITESPACE 21 | ) 22 | 23 | if(GIT_HASH) 24 | set(VERSION "${PROJECT_VERSION}-${GIT_HASH}") 25 | else() 26 | set(VERSION "${PROJECT_VERSION}") 27 | endif() 28 | 29 | # Create program target 30 | file(GLOB_RECURSE SOURCES src/*.c) 31 | add_executable(${PROJECT_NAME} ${SOURCES}) 32 | target_include_directories(${PROJECT_NAME} PRIVATE 33 | # Internal headers 34 | "${CMAKE_CURRENT_SOURCE_DIR}/src" 35 | "${CMAKE_CURRENT_SOURCE_DIR}/src/conf" 36 | "${CMAKE_CURRENT_SOURCE_DIR}/src/modules" 37 | "${CMAKE_CURRENT_SOURCE_DIR}/src/utils" 38 | "${CMAKE_CURRENT_SOURCE_DIR}/src/pubsub" 39 | ) 40 | target_compile_definitions(${PROJECT_NAME} PRIVATE 41 | -D_GNU_SOURCE 42 | -DVERSION="${VERSION}" 43 | -DCONFDIR="${CLIGHT_CONFDIR}" 44 | -DOLDCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}/default" 45 | -DDATADIR="${CLIGHT_DATADIR}" 46 | ) 47 | set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD_REQUIRED ON) 48 | set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 11) 49 | 50 | # Required dependencies 51 | pkg_check_modules(REQ_LIBS REQUIRED popt gsl libconfig libmodule>=5.0.0) 52 | pkg_search_module(LOGIN_LIBS REQUIRED libelogind libsystemd>=234) 53 | 54 | # Avoid float versioning for libsystemd/libelogind 55 | string(REPLACE "." ";" LOGIN_LIBS_VERSION_LIST ${LOGIN_LIBS_VERSION}) 56 | list(GET LOGIN_LIBS_VERSION_LIST 0 LOGIN_LIBS_VERSION_MAJOR) 57 | message(STATUS "Found lib${LOGIN_LIBS_LIBRARIES} version ${LOGIN_LIBS_VERSION_MAJOR}") 58 | 59 | target_link_libraries(${PROJECT_NAME} 60 | m 61 | ${REQ_LIBS_LIBRARIES} 62 | ${LOGIN_LIBS_LIBRARIES} 63 | ) 64 | target_include_directories(${PROJECT_NAME} PRIVATE 65 | "${REQ_LIBS_INCLUDE_DIRS}" 66 | "${LOGIN_LIBS_INCLUDE_DIRS}" 67 | ) 68 | target_compile_definitions(${PROJECT_NAME} PRIVATE 69 | -DLIBSYSTEMD_VERSION=${LOGIN_LIBS_VERSION_MAJOR} 70 | ) 71 | list(APPEND COMBINED_LDFLAGS ${REQ_LIBS_LDFLAGS}) 72 | list(APPEND COMBINED_LDFLAGS ${LOGIN_LIBS_LDFLAGS}) 73 | 74 | # Convert ld flag list from list to space separated string. 75 | string(REPLACE ";" " " COMBINED_LDFLAGS "${COMBINED_LDFLAGS}") 76 | 77 | set(PUBLIC_H src/public.h) 78 | 79 | # Set the LDFLAGS target property 80 | set_target_properties( 81 | ${PROJECT_NAME} PROPERTIES 82 | LINK_FLAGS "${COMBINED_LDFLAGS} -rdynamic" 83 | PUBLIC_HEADER "${PUBLIC_H}" 84 | ) 85 | 86 | # Installation of targets (must be before file configuration to work) 87 | install(TARGETS ${PROJECT_NAME} 88 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 89 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/clight) 90 | 91 | # Configure files with install paths 92 | set(EXTRA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Extra") 93 | 94 | configure_file(${EXTRA_DIR}/org.clight.clight.service 95 | org.clight.clight.service 96 | @ONLY) 97 | 98 | # Installation of files 99 | if (NOT DEFINED BASH_COMPLETIONS_DIR) 100 | pkg_get_variable(BASH_COMPLETIONS_DIR bash-completion completionsdir) 101 | endif() 102 | if (NOT DEFINED ZSH_COMPLETIONS_DIR) 103 | set(ZSH_COMPLETIONS_DIR "${CMAKE_INSTALL_FULL_DATADIR}/zsh/site-functions" 104 | CACHE STRING "Installation directory for Zsh completions.") 105 | endif() 106 | if (NOT DEFINED FISH_COMPLETIONS_DIR) 107 | pkg_get_variable(FISH_COMPLETIONS_DIR fish completionsdir) 108 | endif() 109 | if (NOT DEFINED SESSION_BUS_DIR) 110 | pkg_get_variable(SESSION_BUS_DIR dbus-1 session_bus_services_dir) 111 | endif() 112 | 113 | file(GLOB_RECURSE SKELETONS Extra/skeletons/*.skel) 114 | 115 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.clight.clight.service 116 | DESTINATION ${SESSION_BUS_DIR}) 117 | install(DIRECTORY ${EXTRA_DIR}/config/ 118 | DESTINATION ${CLIGHT_CONFDIR}) 119 | install(FILES ${EXTRA_DIR}/desktop/clight.desktop 120 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/xdg/autostart) 121 | install(FILES ${EXTRA_DIR}/desktop/clightc.desktop 122 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) 123 | install(FILES ${EXTRA_DIR}/icons/clight.svg 124 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) 125 | install(FILES ${SKELETONS} DESTINATION ${CLIGHT_DATADIR}) 126 | install(DIRECTORY DESTINATION ${CLIGHT_DATADIR}/modules.d/) 127 | 128 | # Install completion files 129 | if (BASH_COMPLETIONS_DIR) 130 | install(FILES ${EXTRA_DIR}/completions/clight 131 | DESTINATION ${BASH_COMPLETIONS_DIR}) 132 | endif() 133 | if (ZSH_COMPLETIONS_DIR) 134 | install(FILES ${EXTRA_DIR}/completions/_clight 135 | DESTINATION ${ZSH_COMPLETIONS_DIR}) 136 | endif() 137 | if (FISH_COMPLETIONS_DIR) 138 | install(FILES ${EXTRA_DIR}/completions/clight.fish 139 | DESTINATION ${FISH_COMPLETIONS_DIR}) 140 | endif() 141 | 142 | # Configure, gzip and install man pages 143 | configure_file(${EXTRA_DIR}/man/clight.1 clight.1 @ONLY) 144 | execute_process(COMMAND gzip clight.1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) 145 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/clight.1.gz DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/man/man1) 146 | 147 | # 148 | # Packaging support 149 | # 150 | SET(CPACK_SET_DESTDIR "on") 151 | set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) 152 | set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION}) 153 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 154 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 155 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 156 | 157 | # 158 | # Metadata common to all packaging systems 159 | # 160 | set(CPACK_PACKAGE_CONTACT "Federico Di Pierro ") 161 | set(CPACK_PACKAGE_DESCRIPTION "Clight is a tiny C utility that can turn your webcam into a light sensor; moreover it supports a redshift-like gamma control, a screen dimmer and dpms settings.") 162 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A C user daemon utility that aims to fully manage your screens.") 163 | 164 | # 165 | # RPM Specific configuration 166 | # 167 | set(CPACK_RPM_PACKAGE_LICENSE "GPL") 168 | set(CPACK_RPM_PACKAGE_URL "https://github.com/FedeDP/Clight") 169 | set(CPACK_RPM_PACKAGE_GROUP "Applications/System") 170 | set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) 171 | set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "${CMAKE_INSTALL_SYSCONFDIR}/xdg" "${CMAKE_INSTALL_SYSCONFDIR}/xdg/autostart" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_BINDIR}" "${CMAKE_INSTALL_DATAROOTDIR}/applications" "${SESSION_BUS_DIR}" "${CMAKE_INSTALL_DATAROOTDIR}/icons" "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor" "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable" "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps") 172 | set(CPACK_RPM_PACKAGE_REQUIRES "systemd-libs popt libconfig gsl clightd >= 5.0 libmodule >= 5.0.0") 173 | set(CPACK_RPM_PACKAGE_SUGGESTS "geoclue-2.0 upower bash-completion") 174 | set(CPACK_RPM_FILE_NAME RPM-DEFAULT) 175 | 176 | # 177 | # DEB Specific configuration 178 | # 179 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/FedeDP/Clight") 180 | set(CPACK_DEBIAN_PACKAGE_SECTION "utils") 181 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libsystemd-dev, libpopt-dev, libconfig-dev, libgsl-dev, clightd (>= 5.0), libmodule (>= 5.0.0)") 182 | set(CPACK_DEBIAN_PACKAGE_SUGGESTS "geoclue-2.0, upower, bash-completion") 183 | set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) 184 | 185 | include(CPack) 186 | -------------------------------------------------------------------------------- /Extra/completions/_clight: -------------------------------------------------------------------------------- 1 | #compdef clight 2 | # zsh completion for clight 3 | 4 | local arguments 5 | 6 | arguments=( 7 | {-f,--frames=}'[Frames taken for each capture, Between 1 and 20]' 8 | {-d,--device=}'[Path to webcam device. If empty, first matching device is used]:device:_files -W /dev/' 9 | '--no-backlight-smooth[Disable smooth backlight transitions]' 10 | '--no-gamma-smooth[Disable smooth gamma transitions]' 11 | '--no-dimmer-smooth-enter[Disable smooth dimmer transitions while entering dimmed state]' 12 | '--no-dimmer-smooth-exit[Disable smooth dimmer transitions while leaving dimmed state]' 13 | '--day-temp=[Daily gamma temperature, between 1000 and 10000]' 14 | '--night-temp=[Nightly gamma temperature, between 1000 and 10000]' 15 | '--lat=[Your desired latitude]' 16 | '--lon=[Your desired longitude]' 17 | '--sunrise=[Force sunrise time for gamma correction]' 18 | '--sunset=[Force sunset time for gamma correction]' 19 | '--no-gamma[Disable gamma correction tool]' 20 | '--no-dimmer[Disable dimmer tool]' 21 | '--no-dpms[Disable dpms tool]' 22 | '--no-backlight[Disable backlight module]' 23 | '--no-screen[Disable screen module (screen content based backlight adjustment)]' 24 | '--no-kbd[Disable keyboard backlight calibration]' 25 | '--dimmer-pct=[Backlight level used while screen is dimmed, in percentage]' 26 | '--verbose[Enable verbose mode]' 27 | '--no-auto-calib[Disable screen backlight automatic calibration]' 28 | '--shutter-thres=[Threshold to consider a capture as clogged]' 29 | {-v,--version}'[Show version info]' 30 | {-c,--conf-file=}'[Specify a conf file to be parsed]:filename:_files' 31 | '--gamma-long-transition[Enable a very long smooth transition for gamma]' 32 | '--ambient-gamma[Enable screen temperature matching ambient brightness instead of time]' 33 | {-w,--wizard}'[Enable wizard mode]' 34 | {-?,--help}'[Show help message]' 35 | '--usage[Display brief usage message]' 36 | ) 37 | 38 | _arguments $arguments 39 | 40 | -------------------------------------------------------------------------------- /Extra/completions/clight: -------------------------------------------------------------------------------- 1 | # bash completion for clight -*- shell-script -*- 2 | 3 | _clight() 4 | { 5 | local cur prev words cword 6 | _init_completion || return 7 | 8 | case $prev in 9 | "--device"|"-d"|"--conf-file"|"-c") 10 | _filedir 11 | return 0 12 | ;; 13 | "--frames") 14 | COMPREPLY=( $( compgen -W "5" -- ${cur}) ) 15 | return 0 16 | ;; 17 | "--day-temp") 18 | COMPREPLY=( $( compgen -W "6500" -- ${cur}) ) 19 | return 0 20 | ;; 21 | "--night-temp") 22 | COMPREPLY=( $( compgen -W "4000" -- ${cur}) ) 23 | return 0 24 | ;; 25 | "--sunrise") 26 | COMPREPLY=( $( compgen -W "07:00" -- ${cur}) ) 27 | return 0 28 | ;; 29 | "--sunset") 30 | COMPREPLY=( $( compgen -W "19:00" -- ${cur}) ) 31 | return 0 32 | ;; 33 | "--dimmer-pct") 34 | COMPREPLY=( $( compgen -W "0.2" -- ${cur}) ) 35 | return 0 36 | ;; 37 | "--shutter-thres") 38 | COMPREPLY=( $( compgen -W "0.1" -- ${cur}) ) 39 | return 0 40 | ;; 41 | "--lat") 42 | COMPREPLY=( $( compgen -W "49.2" -- ${cur}) ) 43 | return 0 44 | ;; 45 | "--lon") 46 | COMPREPLY=( $( compgen -W "9.22" -- ${cur}) ) 47 | return 0 48 | ;; 49 | esac 50 | opts="--device --frames --no-backlight-smooth --no-gamma-smooth --no-dimmer-smooth-enter --no-dimmer-smooth-exit --day-temp --night-temp --lat --lon --sunrise --sunset --no-gamma --dimmer-pct --no-dimmer --no-dpms --no-backlight --verbose --no-auto-calib --version --no-kbd-backlight --shutter-thres --conf-file --gamma-long-transition --ambient-gamma --no-screen --wizard" 51 | if [[ "$cur" == -* ]] || [[ -z "$cur" ]]; then 52 | COMPREPLY=( $( compgen -W "${opts}" -- ${cur}) ) 53 | fi 54 | 55 | } && 56 | complete -F _clight clight 57 | 58 | # ex: ts=4 sw=4 et filetype=sh 59 | -------------------------------------------------------------------------------- /Extra/completions/clight.fish: -------------------------------------------------------------------------------- 1 | # fish completion for clight 2 | 3 | complete -c clight -o f -x -d "Frames taken for each capture, Between 1 and 20" 4 | complete -c clight -l frames -x -d "Frames taken for each capture, Between 1 and 20" 5 | complete -c clight -o d -r -d "Path to webcam device. If empty, first matching device is used" 6 | complete -c clight -l device -r -d "Path to webcam device. If empty, first matching device is used" 7 | complete -c clight -l no-backlight-smooth -f -d "Disable smooth backlight transitions" 8 | complete -c clight -l no-gamma-smooth -f -d "Disable smooth gamma transitions" 9 | complete -c clight -l no-dimmer-smooth-enter -f -d "Disable smooth dimmer transitions while entering dimmed state" 10 | complete -c clight -l no-dimmer-smooth-exit -f -d "Disable smooth dimmer transitions while leaving dimmed state" 11 | complete -c clight -l day-temp -x -d "Daily gamma temperature, between 1000 and 10000" 12 | complete -c clight -l night-temp -x -d "Nightly gamma temperature, between 1000 and 10000" 13 | complete -c clight -l lat -x -d "Your desired latitude" 14 | complete -c clight -l lon -x -d "Your desired longitude" 15 | complete -c clight -l sunrise -x -d "Force sunrise time for gamma correction" 16 | complete -c clight -l sunset -x -d "Force sunset time for gamma correction" 17 | complete -c clight -l no-gamma -f -d "Disable gamma correction tool" 18 | complete -c clight -l no-dimmer -f -d "Disable dimmer tool" 19 | complete -c clight -l no-dpms -f -d "Disable dpms tool" 20 | complete -c clight -l no-backlight -f -d "Disable backlight module" 21 | complete -c clight -l no-screen -f -d "Disable screen module (screen content based backlight adjustment)" 22 | complete -c clight -l no-kbd -f -d "Disable keyboard backlight calibration" 23 | complete -c clight -l dimmer-pct -x -d "Backlight level used while screen is dimmed, in percentage" 24 | complete -c clight -l verbose -f -d "Enable verbose mode" 25 | complete -c clight -l no-auto-calib -f -d "Disable screen backlight automatic calibration" 26 | complete -c clight -l shutter-thres -x -d "Threshold to consider a capture as clogged" 27 | complete -c clight -o v -f -d "Show version info" 28 | complete -c clight -l version -f -d "Show version info" 29 | complete -c clight -o c -r -d "Specify a conf file to be parsed" 30 | complete -c clight -l conf-file -r -d "Specify a conf file to be parsed" 31 | complete -c clight -l gamma-long-transition -f -d "Enable a very long smooth transition for gamma" 32 | complete -c clight -l ambient-gamma -f -d "Enable screen temperature matching ambient brightness instead of time" 33 | complete -c clight -o w -f -d "Enable wizard mode" 34 | complete -c clight -l wizard -f -d "Enable wizard mode" 35 | complete -c clight -o "?" -f -d "Show help message" 36 | complete -c clight -l help -f -d "Show help message" 37 | complete -c clight -l usage -f -d "Display brief usage message" 38 | -------------------------------------------------------------------------------- /Extra/config/clight.conf: -------------------------------------------------------------------------------- 1 | #################### 2 | # Clight conf file # 3 | #################### 4 | 5 | ## Verbose mode, useful to report bugs: 6 | ## run clight in verbose mode, 7 | ## then open issue on github attaching log 8 | # verbose = true; 9 | 10 | ## Delay in seconds before clight restarts working 11 | ## after system is resumed from suspend/hibernation. 12 | ## This may be needed because on some laptops on resume 13 | ## screen temp is not correctly applied, with warning in logs: "Failed to set gamma temperature." 14 | ## Clight is in fact too quick to act on resume, and it is resuming before X is fully resumed; 15 | ## thus failing to apply screen temperature. 16 | ## By default, it is disabled (0 seconds). Max value: 30seconds. 17 | ## Note: it requires systemd-logind (org.freedesktop.login1 dbus interface) 18 | # resumedelay = 0; 19 | 20 | ################### 21 | # INHIBITION TOOL # 22 | ######################################################## 23 | # Requires DIMMER or DPMS enabled. Disabled otherwise. # 24 | ######################################################## 25 | @include "modules.conf.d/inhibit.conf" 26 | 27 | ############################## 28 | # BACKLIGHT CALIBRATION TOOL # 29 | ############################## 30 | @include "modules.conf.d/backlight.conf" 31 | 32 | ################### 33 | # SENSOR settings # 34 | ################### 35 | @include "modules.conf.d/sensor.conf" 36 | 37 | ############################## 38 | # KEYBOARD BACKLIGHT TOOL # 39 | ############################## 40 | @include "modules.conf.d/keyboard.conf" 41 | 42 | ############## 43 | # GAMMA TOOL # 44 | ############## 45 | @include "modules.conf.d/gamma.conf" 46 | 47 | ################ 48 | # DAYTIME TOOL # 49 | ################ 50 | ######################################################## 51 | # Retrieves Sunrise and Sunset times for user location # 52 | ######################################################## 53 | @include "modules.conf.d/daytime.conf" 54 | 55 | ############### 56 | # DIMMER TOOL # 57 | ############### 58 | ########################################### 59 | # Requires BACKLIGHT. Disabled otherwise. # 60 | ########################################### 61 | @include "modules.conf.d/dimmer.conf" 62 | 63 | ############# 64 | # DPMS TOOL # 65 | ############# 66 | @include "modules.conf.d/dpms.conf" 67 | 68 | ############################ 69 | # SCREEN COMPENSATION TOOL # 70 | ############################ 71 | ########################################### 72 | # Requires BACKLIGHT. Disabled otherwise. # 73 | ########################################### 74 | @include "modules.conf.d/screen.conf" 75 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/backlight.conf: -------------------------------------------------------------------------------- 1 | backlight: 2 | { 3 | ## Uncomment to disable 4 | # disabled = true; 5 | 6 | ## Uncomment to restore screen backlight on exit 7 | # restore_on_exit = true; 8 | 9 | ## Uncomment to disable smooth transitions 10 | # no_smooth_transition = true; 11 | 12 | ## Transition step in percentage 13 | # trans_step = 0.05; 14 | 15 | ## Transition timeout in ms 16 | # trans_timeout = 30; 17 | 18 | ## When > 0, use a fixed transition duration (in ms), 19 | ## overriding trans_timeout and trans_step configs. 20 | # trans_fixed = 1000; 21 | 22 | ## Timeouts between captures during day/night/event on AC 23 | ## Set any of these to <= 0 to disable captures 24 | ## in the corresponding day time. 25 | # ac_timeouts = [ 600, 2700, 300 ]; 26 | 27 | ## Timeouts between captures during day/night/event on BATT 28 | ## Set any of these to <= 0 to disable captures 29 | ## in the corresponding day time. 30 | # batt_timeouts = [ 1200, 5400, 600 ]; 31 | 32 | ## Set a threshold: if detected ambient brightness is below this threshold, 33 | ## capture will be discarded and no backlight change will be made. 34 | ## Very useful to discard captures with covered webcam. 35 | # shutter_threshold = 0.10; 36 | 37 | ## Disables automatic calibration for screen backlight. 38 | ## Then, it can only be manually triggered by bus api. 39 | # no_auto_calibration = true; 40 | 41 | ## Uncomment to let BACKLIGHT module pause automatic calibration when laptop lid is closed. 42 | ## Mostly useful when laptop gets docked and thus internal webcam 43 | ## would not be able to correctly capture ambient brightness. 44 | # pause_on_lid_closed = true; 45 | 46 | ## Uncomment to let BACKLIGHT module fire an automatic calibration when laptop lid gets opened. 47 | # capture_on_lid_opened = true; 48 | 49 | ## Uncomment to set a delay before GAMMA and BACKLIGHT get synced 50 | ## whenever a monitor is added/removed. 51 | ## Similarly to "resumedelay", some monitors are slow to become active, 52 | ## therefore a small delay is needed. 53 | ## By default, it is disabled (0 seconds). Max value: 10seconds. 54 | # hotplug_delay = 5; 55 | }; 56 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/daytime.conf: -------------------------------------------------------------------------------- 1 | daytime: 2 | { 3 | ## Desired latitude for gamma support (sunrise/sunset in this location) 4 | # latitude = 40.9; 5 | 6 | ## Desired longitude for gamma support (sunrise/sunset in this location) 7 | # longitude = 7.16; 8 | 9 | ## Force set a sunrise time 10 | # sunrise = "7:00"; 11 | 12 | ## Force set a sunset time 13 | # sunset = "19:00"; 14 | 15 | ## Duration of an "event". Clight will enter "event" mode (more frequent screen recalibrations) 16 | ## from event_duration seconds before a sunrise/sunset, until event_duration seconds after. 17 | # event_duration = 1800; 18 | 19 | ## Set an offset in seconds from sunrise; 20 | ## eg: a value of 600 would force Clight to manage sunrise event 21 | ## 10 minutes after the real event. 22 | ## You can use negative values too. 23 | # sunrise_offset = 0; 24 | 25 | ## Set an offset in seconds from sunset; 26 | ## eg: a value of 600 would force Clight to manage sunset event 27 | ## 10 minutes after the real event. 28 | ## You can use negative values too. 29 | # sunset_offset = 0; 30 | }; 31 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/dimmer.conf: -------------------------------------------------------------------------------- 1 | dimmer: 2 | { 3 | ## Uncomment to disable dimmer tool 4 | # disabled = true; 5 | 6 | ## Uncomment to disable smooth transitions 7 | ## when entering/leaving dimmed state 8 | # no_smooth_transition [ true, true ]; 9 | 10 | ## Transition step in percentage. 11 | ## Entering dimmed state fade, and leaving fade 12 | # trans_steps = [ 0.05, 0.05 ]; 13 | 14 | ## Transition timeout in ms. 15 | ## Entering dimmed state fade, and leaving fade 16 | # trans_timeouts = [ 30, 30 ]; 17 | 18 | ## When > 0, use a fixed transition duration (in ms), 19 | ## for enter or leave, overriding trans_timeouts and trans_steps configs. 20 | # trans_fixed = [ 1000, 1000 ]; 21 | 22 | ## Timeouts on AC/on BATT. 23 | ## Set any of these to <= 0 to disable dimmer 24 | ## in the corresponding AC state. 25 | # timeouts = [ 45, 20 ]; 26 | 27 | ## Change dimmed backlight level, in percentage 28 | # dimmed_pct = 0.2; 29 | }; 30 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/dpms.conf: -------------------------------------------------------------------------------- 1 | dpms: 2 | { 3 | ## Uncomment to disable dpms management 4 | # disabled = true; 5 | 6 | ## Timeouts on AC/on BATT. 7 | ## Set any of these to <= 0 to disable dpms 8 | ## in the corresponding AC state. 9 | # timeouts = [ 900, 300 ]; 10 | }; 11 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/gamma.conf: -------------------------------------------------------------------------------- 1 | gamma: 2 | { 3 | ## Uncomment to disable gamma tool 4 | # disabled = true; 5 | 6 | ## Uncomment to restore screen temperature on exit 7 | # restore_on_exit = true; 8 | 9 | ## Uncomment to disable gamma smooth transitions 10 | # no_smooth_transition = true; 11 | 12 | ## Gamma transition step 13 | # trans_step = 50; 14 | 15 | ## Gamma transition timeout in ms 16 | # trans_timeout = 300; 17 | 18 | ## Gamma temperature during day and night 19 | # temp = [ 6500, 4000 ]; 20 | 21 | ## Enable to let GAMMA smooth transitions last (2 * event_duration), 22 | ## in a redshift-like way. 23 | ## When enabling this, transition steps and timeouts are automatically computed 24 | ## given DAY-NIGHT temperature difference and (2 * event_duration) duration. 25 | ## 26 | ## Note that if clight is started outside of an event, correct gamma temperature 27 | ## will be immediately setted using normal parameters: 28 | ## no_smooth_gamma_transition, gamma_trans_step, gamma_trans_timeout 29 | # long_transition = true; 30 | 31 | ## Let screen temperature match monitor backlight, using following algorithm: 32 | ## ```pseudocode 33 | ## diff = abs(temp[DAY] - temp[NIGHT]) 34 | ## min_temp = min(temp[NIGHT], temp[DAY]) 35 | ## new_temp = (diff * screen_bl) + min_temp; 36 | ## ``` 37 | ## Ie: the higher the screen backlight, the colder the temp. 38 | ## 39 | ## When enabled, screen temperature won't be changed time-based. 40 | ## Note also that LOCATION is still needed to let BACKLIGHT module know current time of day. 41 | ## Finally, it requires BACKLIGHT module to be enabled, otherwise it gets disabled. 42 | # ambient_gamma = true; 43 | }; 44 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/inhibit.conf: -------------------------------------------------------------------------------- 1 | inhibit: 2 | { 3 | ## Uncomment to disable 4 | # disabled = true; 5 | 6 | ## Uncomment to let Clight manage "Docked" laptop state 7 | ## as an inhibition (pausing DPMS and dimmer). 8 | ## Note that "Docked" state is only checked when 9 | ## laptop lid is closed or opened. 10 | # inhibit_docked = true; 11 | 12 | ## Uncomment to let Clight manage inhibition requests 13 | ## suppressing org.freedesktop.PowerManagement too 14 | ## (ie: preventing your laptop to suspend/hibernate) 15 | # inhibit_pm = true; 16 | 17 | ## Uncomment to let Clight pause backlight and screen modules 18 | ## while any inhibition is active; 19 | ## this is useful eg: to pause backlight calibration 20 | ## while watching a movie. 21 | # inhibit_bl = true; 22 | }; 23 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/keyboard.conf: -------------------------------------------------------------------------------- 1 | keyboard: 2 | { 3 | ## Uncomment to disable keyboard automatic calibration. 4 | ## It is automatically disabled anyway where not available. 5 | # disabled = true; 6 | 7 | ## Timeouts on AC/on BATT for keyboard auto dimming. 8 | ## Set any of these to <= 0 to disable kbd backlight 9 | ## in the corresponding AC state. 10 | # timeouts = [ 15, 5 ]; 11 | 12 | ## Curves used to match screen backlight to keyboard backlight level for each AC state. 13 | ## X axis: default screen backlight level (from 0 to 10) 14 | ## Y axis: desired keyboard backlight level for corresponding screen backlight. 15 | ## Note: the array can be expanded up to 50 points for finer granularity. 16 | ## Note also that most keyboard offers only 3 backlight levels (off, mid, high). 17 | ## Default curves are same as default backlight curves but upside down 18 | ## (ie: the lower the screen backlight, the higher the keyboard backlight). 19 | # ac_regression_points = [ 1.0, 0.97, 0.93, 0.88, 0.81, 0.74, 0.61, 0.45, 0.29, 0.15, 0.0 ]; 20 | # batt_regression_points = [ 0.80, 0.78, 0.75, 0.71, 0.65, 0.59, 0.52, 0.36, 0.23, 0.15, 0.0 ]; 21 | }; 22 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/screen.conf: -------------------------------------------------------------------------------- 1 | screen: 2 | { 3 | ############################################################################################################## 4 | ## Use this feature to enable screen-content based backlight adjustments to Clight. # 5 | ## This means that monitord backlight won't be changed to match ambient brightness alone, # 6 | ## but screen content brightness too, using following algorithm: # 7 | ## ```pseudocode # 8 | ## window_min = ambient_br - contrib; # 9 | ## window_max = ambient_br + contrib; # 10 | ## bl_new = window_max - (0.4 * screen_br) # 11 | ## ``` # 12 | ## Ie: ambient brightness is used as a sliding window 20% wide, # 13 | ## and screen content brightness is used to choose the correct point inside the window. # 14 | ## Rule is: higher the screen content brightness, nearest to window max; and viceversa. # 15 | ## Idea is fairly simple: given current ambient brightness, try to light up dark screen content, and dim # 16 | ## bright screen content. # 17 | ## # 18 | ## This can impact battery life, that is the reason why SCREEN module is disabled on battery by default; # 19 | ## in this case, you might want to keep SCREEN module disabled and manually calibrating screen backlight # 20 | ## through Clight desktop file "Capture" quick action or its DBus command. # 21 | ############################################################################################################## 22 | 23 | ## Uncomment to disable screen-emitted brightness compensation support 24 | # disabled = true; 25 | 26 | ## How much does your screen brightness affect 27 | ## ambient brightness, in percentage. 28 | ## As above algorithm outlines, it is used to build the ambient brightness window. 29 | ## Set it to 0.0 to disable screen-content based backlight compensation. 30 | # contrib = 0.2; 31 | 32 | ## Screen timeouts on AC/on BATT. 33 | ## Set any of these to <= 0 to disable screen-content based backlight compensation 34 | ## in the corresponding AC state. 35 | ## Disabled by default on BATT because it is quite an heavy operation, 36 | ## as it has to take a snapshot of your X desktop and compute its brightness. 37 | # timeouts = [ 5, -1 ]; 38 | }; 39 | -------------------------------------------------------------------------------- /Extra/config/modules.conf.d/sensor.conf: -------------------------------------------------------------------------------- 1 | sensor: 2 | { 3 | ## Curves used to match ambient brightness to backlight level for each AC state. 4 | ## X axis: ambient brightness values (from 0 to 10) 5 | ## Y axis: desired backlight level for corresponding ambient brightness. 6 | ## Note: the array can be expanded up to 50 points for finer granularity. 7 | # ac_regression_points = [ 0.0, 0.15, 0.29, 0.45, 0.61, 0.74, 0.81, 0.88, 0.93, 0.97, 1.0 ]; 8 | # batt_regression_points = [ 0.0, 0.15, 0.23, 0.36, 0.52, 0.59, 0.65, 0.71, 0.75, 0.78, 0.80 ]; 9 | 10 | ## Sensor device to be used (Webcam or ALS device, eg: video0 or iio:device0). 11 | ## Leave this empty to let clight use first device it finds between supported ones, 12 | ## ie: webcams, ambient light sensors, or custom devices. 13 | ## Refer to Clightd wiki for more info: https://github.com/FedeDP/Clightd/wiki/Sensors 14 | # devname = ""; 15 | 16 | ## Sensor settings to be used. Leave empty/commented to use default values. 17 | ## This can be really useful to further customize your sensor behaviour (together with backlight_regression_points). 18 | ## Have a look at Clightd wiki for more info: https://github.com/FedeDP/Clightd/wiki/Sensors#settings-string. 19 | ## Example for Camera sensor, to set a manual exposure -> "10094849=1,10094850=166"; 20 | # settings = ""; 21 | 22 | ## Number of frames or ALS device pollings to be captured on AC/on BATT. 23 | ## Must be between 1 and 20. 24 | # captures = [ 5, 5 ]; 25 | }; 26 | 27 | ## Curves used to match reference backlight level (computed through sensor.regression_points curves), 28 | ## to backlight level for each AC state for specific monitors. 29 | ## This allows per-monitor backlight adjustment; it means eg: 30 | ## that a 0.7 backlight level for your internal laptop monitor, 31 | ## can be mapped to 0.8 backlight level for your external monitor. 32 | ## It is mostly useful for people with internal laptop + external monitors or multiple external monitors, 33 | ## as often their backlight level will not properly match (ie: a 50% level on internal laptop can be much brighter than external monitor). 34 | ## All monitors not specified below will use default reference values (specified by sensor.regression_points curves). 35 | ## X axis: default backlight level (from 0 to 10) 36 | ## Y axis: desired backlight level adjusted for specific monitor. 37 | ## Note: arrays can be expanded up to 50 points for finer granularity. 38 | ## Customize "intel_backlight" or "acpi_video0" or whatever using your monitors IDs. 39 | ## To find monitor IDs, use Clightd: 40 | ## busctl call org.clightd.clightd /org/clightd/clightd/Backlight org.clightd.clightd.Backlight GetAll "s" "" 41 | ## 42 | ## You might ask why using a backlight-to-backlight mapping for specific monitors, instead of using 43 | ## multiple ambient_brightness-to-backlight mapping curves; fact is that this way Clight is also able to 44 | ## set correct backlight for each monitor even when just asked to set the backlight, ie: 45 | ## when the request comes from eg: dbus API asking for a certain backlight level (IncBl, DecBl dbus methods). 46 | ## In this case, we wouldn't have an "ambient brightess" to be used to compute correct backlight level on each monitor; 47 | ## instead, we just set the requested backlight as default, and adjust to it for each monitor specified here below. 48 | monitor_override: 49 | ( 50 | { 51 | # monitor_id = "intel_backlight" 52 | # ac_regression_points = [ 0.0, 0.18, 0.22, 0.33, 0.55, 0.64, 0.71, 0.80, 0.90, 0.97, 1.0 ]; 53 | # batt_regression_points = [ 0.0, 0.15, 0.29, 0.45, 0.61, 0.74, 0.81, 0.88, 0.93, 0.97, 1.0 ]; 54 | }, 55 | { 56 | # monitor_id = "acpi_video0" 57 | # ac_regression_points = [ 0.0, 0.18, 0.22, 0.33, 0.55, 0.64, 0.71, 0.80, 0.90, 0.97, 1.0 ]; 58 | # batt_regression_points = [ 0.0, 0.15, 0.29, 0.45, 0.61, 0.74, 0.81, 0.88, 0.93, 0.97, 1.0 ]; 59 | } 60 | ); 61 | -------------------------------------------------------------------------------- /Extra/desktop/clight.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Clight 4 | Exec=clight 5 | NoDisplay=true 6 | Comment=Start Clight user daemon and turn your webcam into a light sensor 7 | Categories=System 8 | -------------------------------------------------------------------------------- /Extra/desktop/clightc.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Clight 4 | GenericName=Quick screen backlight calibration 5 | Exec=busctl --expect-reply=false --user call org.clight.clight /org/clight/clight org.clight.clight Capture "bb" false false 6 | Comment=Recalibrate your screen backlight! 7 | Icon=clight 8 | Categories=System 9 | Actions=Inhibit;Uninhibit;PauseCalib;ResumeCalib;Pause;Resume;ForceGamma;ToggleGamma 10 | 11 | [Desktop Action Inhibit] 12 | Exec=busctl --expect-reply=false --user call org.clight.clight /org/clight/clight org.clight.clight Inhibit "b" true 13 | Name=Pause screen dimmer 14 | 15 | [Desktop Action Uninhibit] 16 | Exec=busctl --expect-reply=false --user call org.clight.clight /org/clight/clight org.clight.clight Inhibit "b" false 17 | Name=Resume screen dimmer 18 | 19 | [Desktop Action PauseCalib] 20 | Exec=busctl --expect-reply=false --user set-property org.clight.clight /org/clight/clight/Conf/Backlight org.clight.clight.Conf.Backlight NoAutoCalib "b" true 21 | Name=Pause backlight calibration 22 | 23 | [Desktop Action ResumeCalib] 24 | Exec=busctl --expect-reply=false --user set-property org.clight.clight /org/clight/clight/Conf/Backlight org.clight.clight.Conf.Backlight NoAutoCalib "b" false 25 | Name=Resume backlight calibration 26 | 27 | [Desktop Action Pause] 28 | Exec=busctl --expect-reply=false --user call org.clight.clight /org/clight/clight org.clight.clight Pause "b" true 29 | Name=Pause Clight 30 | 31 | [Desktop Action Resume] 32 | Exec=busctl --expect-reply=false --user call org.clight.clight /org/clight/clight org.clight.clight Pause "b" false 33 | Name=Resume Clight 34 | 35 | [Desktop Action ForceGamma] 36 | Exec=sh -c "busctl --expect-reply=false --user set-property org.clight.clight /org/clight/clight/Conf/Gamma org.clight.clight.Conf.Gamma DayTemp "i" 0 && busctl --expect-reply=false --user set-property org.clight.clight /org/clight/clight/Conf/Gamma org.clight.clight.Conf.Gamma NightTemp "i" 0" 37 | Name=Force-set screen temperature for current daytime 38 | 39 | [Desktop Action ToggleGamma] 40 | Exec=busctl --user call org.clight.clight /org/clight/clight/Conf/Gamma org.clight.clight.Conf.Gamma Toggle 41 | Name=Toggle between config screen temperatures 42 | -------------------------------------------------------------------------------- /Extra/icons/clight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 22 | 24 | image/svg+xml 25 | 27 | 28 | 29 | 30 | 31 | 33 | 53 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Extra/man/clight.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH Clight 1 Linux "User Manuals" 3 | .SH NAME 4 | .PP 5 | Clight \- a user daemon utility that aims to fully manage your display. 6 | 7 | .SH SYNOPSIS 8 | .PP 9 | \fB\fCclight\fR [\fB\fC\-vw?\fR] [\fB\fC\-f, \-\-frames\fR INT] [\fB\fC\-d, \-\-device\fR video0] [\fB\fC\-b, \-\-backlight\fR intel\_backlight] 10 | .br 11 | [\fB\fC\-\-no\-backlight\-smooth\fR] [\fB\fC\-\-no\-gamma\-smooth\fR] [\fB\fC\-\-no\-dimmer\-smooth\-enter\fR] [\fB\fC\-\-no\-dimmer\-smooth\-exit\fR] 12 | .br 13 | [\fB\fC\-\-day\-temp\fR INT] [\fB\fC\-\-night\-temp\fR INT] [\fB\fC\-\-lat\fR DOUBLE] [\fB\fC\-\-lon\fR DOUBLE] [\fB\fC\-\-sunrise\fR 07:00] [\fB\fC\-\-sunset\fR 19:00] 14 | .br 15 | [\fB\fC\-\-no\-gamma\fR] [\fB\fC\-\-no\-dimmer\fR] [\fB\fC\-\-no\-dpms\fR] [\fB\fC\-\-no\-backlight\fR] [\fB\fC\-\-no\-screen\fR] [\fB\fC\-\-no\-kbd\fR] 16 | .br 17 | [\fB\fC\-\-dimmer\-pct\fR DOUBLE] [\fB\fC\-\-no\-auto\-calib\fR] [\fB\fC\-\-shutter\-thres\fR DOUBLE] [\fB\fC\-\-gamma\-long\-transition\fR] [\fB\fC\-\-ambient\-gamma\fR] 18 | .br 19 | [\fB\fC\-c, \-\-conf\-file\fR STRING] [\fB\fC\-w, \-\-wizard\fR] [\fB\fC\-\-verbose\fR] [\fB\fC\-v, \-\-version\fR] [\fB\fC\-?, \-\-help\fR] [\fB\fC\-\-usage\fR] 20 | 21 | .SH DESCRIPTION 22 | .PP 23 | \fB\fCclight\fR allows to match your backlight level to ambient brightness, 24 | computed by capturing frames from webcam or Ambient Light Sensors. 25 | .br 26 | It does also support adjusting external monitors and keyboards backlight. 27 | .br 28 | Moreover, it can manage your screen temperature, 29 | dim your screen after a timeout and manage screen DPMS. 30 | 31 | .SH OPTIONS 32 | .PP 33 | \fB\fC\-c, \-\-conf\-file\fR \fIconfig\-file\fP 34 | Parse an additonal \fIconfig\-file\fP to override other configs. 35 | 36 | .PP 37 | \fB\fC\-v, \-\-version\fR 38 | Print program version. 39 | 40 | .PP 41 | \fB\fC\-w, \-\-wizard\fR 42 | Run a small wizard to calibrate sensor curve. 43 | 44 | .PP 45 | \fB\fC\-f, \-\-frames\fR \fInumber\fP 46 | Customize frames taken for each capture, between 1 and 20. 47 | 48 | .PP 49 | \fB\fC\-d, \-\-device\fR \fIdevice\fP 50 | Specify sensor device. If empty, first matching device is used. 51 | 52 | .PP 53 | \fB\fC\-\-no\-backlight\-smooth\fR 54 | Disable backlight smooth transitions. 55 | 56 | .PP 57 | \fB\fC\-\-no\-gamma\-smooth\fR 58 | Disable gamma smooth transitions. 59 | 60 | .PP 61 | \fB\fC\-\-no\-dimmer\-smooth\-enter\fR 62 | Disable smooth transitions while entering dimmed state. 63 | 64 | .PP 65 | \fB\fC\-\-no\-dimmer\-smooth\-exit\fR 66 | Disable smooth transitions while leaving dimmed state. 67 | 68 | .PP 69 | \fB\fC\-\-day\-temp\fR \fInumber\fP 70 | Customize daily screen temperature. 71 | 72 | .PP 73 | \fB\fC\-\-night\-temp\fR \fInumber\fP 74 | Customize nightly screen temperature. 75 | 76 | .PP 77 | \fB\fC\-\-gamma\-long\-transition\fR 78 | Enable a very long smooth transition for gamma (redshift\-like). 79 | 80 | .PP 81 | \fB\fC\-\-ambient\-gamma\fR 82 | Enable screen temperature matching ambient brightness instead of being time based. 83 | 84 | .PP 85 | \fB\fC\-\-lat\fR \fIdouble\fP 86 | Desired latitude. 87 | 88 | .PP 89 | \fB\fC\-\-lon\fR \fIdouble\fP 90 | .br 91 | Desired longitude. 92 | 93 | .PP 94 | \fB\fC\-\-sunrise\fR \fItime\fP 95 | Specify sunrise time for gamma correction. 96 | 97 | .PP 98 | \fB\fC\-\-sunset\fR \fItime\fP 99 | Specify sunset time for gamma correction. 100 | 101 | .PP 102 | \fB\fC\-\-no\-gamma\fR 103 | Disable gamma correction tool. 104 | 105 | .PP 106 | \fB\fC\-\-no\-dimmer\fR 107 | Disable dimmer tool. 108 | 109 | .PP 110 | \fB\fC\-\-no\-dpms\fR 111 | Disable dpms tool. 112 | 113 | .PP 114 | \fB\fC\-\-no\-backlight\fR 115 | Disable backlight calibration tool. 116 | 117 | .PP 118 | \fB\fC\-\-no\-screen\fR 119 | Disable screen emitted brightness correction tool. 120 | 121 | .PP 122 | \fB\fC\-\-no\-kbd\fR 123 | .br 124 | Disable keyboard backlight calibration tool. 125 | 126 | .PP 127 | \fB\fC\-\-dimmer\-pct\fR \fIdouble\fP 128 | Backlight level used while screen is dimmed, in pergentage. 129 | 130 | .PP 131 | \fB\fC\-\-verbose\fR 132 | .br 133 | Enable verbose mode. 134 | 135 | .PP 136 | \fB\fC\-\-no\-auto\-calib\fR 137 | .br 138 | Disable screen backlight automatic calibration. 139 | 140 | .PP 141 | \fB\fC\-\-shutter\-thres\fR \fIdouble\fP 142 | Threshold to consider a capture as clogged. 143 | 144 | .SH FILES 145 | .PP 146 | \fI@CLIGHT_CONFDIR@/clight.conf\fP 147 | The system wide configuration file. 148 | 149 | .PP 150 | \fI$HOME/.config/clight.conf\fP 151 | Per user configuration file. 152 | 153 | .SH AUTHOR 154 | .PP 155 | Federico Di Pierro nierro92@gmail.com 156 | \[la]mailto:nierro92@gmail.com\[ra] 157 | -------------------------------------------------------------------------------- /Extra/org.clight.clight.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=org.clight.clight 3 | Exec=@CMAKE_INSTALL_FULL_BINDIR@/clight 4 | -------------------------------------------------------------------------------- /Extra/skeletons/README.md: -------------------------------------------------------------------------------- 1 | # Custom Modules Skeletons 2 | 3 | Here you can find a couple of small but hopefully self-explaining custom modules. 4 | You can change them as you like, or you can just use them. 5 | 6 | * inhibit_bl.skel -> will set 100% BL level when getting inhibited (eg: when start watching a movie) and will pause automatic BACKLIGHT calibration too. 7 | As soon as inhibition disappears, it will take a quick capture and resume automatic calibration. 8 | * nightmode.skel -> will just log new daytime value (eg "Day" or "Night"). It has a couple of commented lines to gracefully change DE theme at DAY/NIGHT. 9 | * synctemp_bumblebee.skel -> it is a workaround for optimus laptop using bumblebee in conjunction with intel-virtual-output to handle an external monitor wired directly to nvidia gpu (through hdmi). It keeps temp in sync between external monitor and integrated one (as bumblebee starts a new X display on hdmi monitor), see https://github.com/FedeDP/Clight/issues/144 10 | -------------------------------------------------------------------------------- /Extra/skeletons/inhibit_bl.skel: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * Small example custom module for Clight. 5 | * 6 | * It just hooks on INHIBIT state updates and: 7 | * -> when entering INHIBIT state (eg: start watching a movie), sets 100% backlight and disable automatic calibration; 8 | * -> when leaving INHIBIT state, triggers a new backlight calibration and re-enables automatic calibration 9 | **/ 10 | 11 | /* 12 | * Rename to: inhibit_bl.c 13 | * 14 | * Build with: gcc -shared -fPIC inhibit_bl.c -o inhibit_bl -Wno-unused 15 | * 16 | * Place inhibit_bl in: $HOME/.local/share/clight/modules.d/ OR, globally, in /usr/share/clight/modules.d/ 17 | */ 18 | 19 | CLIGHT_MODULE("INHIBIT_BL"); 20 | 21 | DECLARE_MSG(bl_req, BL_REQ); 22 | DECLARE_MSG(capture_req, CAPTURE_REQ); 23 | DECLARE_MSG(calib_req, NO_AUTOCALIB_REQ); 24 | 25 | static void init(void) { 26 | capture_req.capture.reset_timer = false; // avoid resetting clight internal BACKLIGHT timer 27 | bl_req.bl.new = 1.0; // 100% screen backlight 28 | bl_req.bl.smooth = -1; // use conf values 29 | 30 | /* Subscribe to inhibit state */ 31 | M_SUB(INHIBIT_UPD); 32 | } 33 | 34 | static void receive(const msg_t *msg, const void *userdata) { 35 | switch (MSG_TYPE()) { 36 | case INHIBIT_UPD: { 37 | inhibit_upd *up = (inhibit_upd *)MSG_DATA(); 38 | calib_req.nocalib.new = up->new; 39 | if (up->new) { 40 | M_PUB(&calib_req); // stop backlight autocalibration 41 | M_PUB(&bl_req); // set 100% screen backlight 42 | INFO("We are now inhibited! Set 100%% screen backlight.\n"); 43 | } else { 44 | INFO("We're not inhibited anymore. Do a quick backlight calibration.\n"); 45 | M_PUB(&calib_req); // resume backlight autocalibration 46 | M_PUB(&capture_req); // ask for a quick calibration 47 | } 48 | break; 49 | } 50 | default: 51 | break; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Extra/skeletons/nightmode.skel: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * Small example custom module for Clight. 6 | * 7 | * It just hooks on DAYTIME_UPD updates and it can set different themes based on daytime, 8 | * just replace commented system() lines. 9 | **/ 10 | 11 | /* 12 | * Rename to: nightmode.c 13 | * 14 | * Build with: gcc -shared -fPIC nightmode.c -o nightmode -Wno-unused 15 | * 16 | * Place nightmode in: $HOME/.local/share/clight/modules.d/ OR, globally, in /usr/share/clight/modules.d/ 17 | */ 18 | 19 | CLIGHT_MODULE("NIGHTMODE"); 20 | 21 | static void init(void) { 22 | /* Suscribe to daytime updates */ 23 | M_SUB(DAYTIME_UPD); 24 | } 25 | 26 | /* 27 | * PubSub callback 28 | */ 29 | static void receive(const msg_t *msg, const void *userdata) { 30 | switch (MSG_TYPE()) { 31 | case DAYTIME_UPD: { 32 | daytime_upd *up = (daytime_upd *)MSG_DATA(); 33 | if (up->new == DAY) { 34 | // system("lookandfeeltool -a org.kde.breeze.desktop"); 35 | INFO("We're now during the day!\n"); 36 | } else { 37 | // system("lookandfeeltool -a org.kde.breezedark.desktop"); 38 | INFO("We're now during the night!\n"); 39 | } 40 | break; 41 | } 42 | default: 43 | break; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Extra/skeletons/synctemp_bumblebee.skel: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | * Bumblebee creates a separate X display (":8" by default) where the nvidia gpu does the rendering when using intel-virtual-output 6 | * to create a virtual display in order to be able to use the hdmi port with an external monitor 7 | * (when the hdmi port is hardwired to the nvidia card, thus when using the hdmi port, the nvidia card starts on display :8). 8 | * 9 | * This custom module will keep second X display temperature in sync with primary one calling directly clightd. 10 | * Rename to: synctemp_bumblebee.c 11 | * 12 | * Build with: gcc -shared -fPIC synctemp_bumblebee.c -o synctemp_bumblebee -Wno-unused 13 | * 14 | * Place synctemp_bumblebee in: $HOME/.local/share/clight/modules.d/ OR, globally, in /usr/share/clight/modules.d/ 15 | */ 16 | 17 | CLIGHT_MODULE("SYNCTEMP"); 18 | 19 | #define BUMBLEBEE_X_DISPLAY ":8" 20 | 21 | static const char *xauth; 22 | 23 | static void init(void) { 24 | xauth = getenv("XAUTHORITY"); 25 | if (!xauth) { 26 | WARN("No XAUTHORITY env variable. Destroying useless custom module.\n"); 27 | m_poisonpill(self()); 28 | } else { 29 | DEBUG("Using xauth: %s\n", xauth); 30 | /* Suscribe to temp updates */ 31 | M_SUB(TEMP_UPD); 32 | } 33 | } 34 | 35 | static void receive(const msg_t *msg, const void *userdata) { 36 | switch (MSG_TYPE()) { 37 | case TEMP_UPD: { 38 | temp_upd *up = (temp_upd *)MSG_DATA(); 39 | DEBUG("Syncing temp on %s: %d\n", BUMBLEBEE_X_DISPLAY, up->new); 40 | char cmd[1024]; 41 | snprintf(cmd, sizeof(cmd), "busctl call org.clightd.clightd /org/clightd/clightd/Gamma org.clightd.clightd.Gamma Set \"ssi(buu)\" \"%s\" \"%s\" %d %d %d %d", 42 | BUMBLEBEE_X_DISPLAY, xauth, up->new, up->smooth, up->step, up->timeout); 43 | system(cmd); 44 | break; 45 | } 46 | default: 47 | break; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clight [![CI Build](https://github.com/FedeDP/clight/actions/workflows/ci.yaml/badge.svg)](https://github.com/FedeDP/clight/actions/workflows/ci.yaml) [![CodeQL](https://github.com/FedeDP/clight/actions/workflows/codeql.yaml/badge.svg)](https://github.com/FedeDP/clight/actions/workflows/codeql.yaml) 2 | 3 | [![Packaging status](https://repology.org/badge/vertical-allrepos/clight.svg)](https://repology.org/project/clight/versions) 4 | 5 | Clight is a C user daemon utility that aims to fully manage your display. 6 | It was heavily inspired by [calise](http://calise.sourceforge.net/wordpress/) in its initial intents. 7 | 8 | **For a guide on how to build, features and lots of other infos, head to [Clight Wiki Pages](https://github.com/FedeDP/Clight/wiki).** 9 | **Note that Wiki pages will always refer to master branch.** 10 | *For any other info, please before opening an issue, head to [FAQ](https://github.com/FedeDP/Clight/wiki/FAQ).* 11 | If the issue is not listed there, feel free to open a new one! 12 | 13 | Last, but not least, special thanks to all people involved in Clight! 14 | 15 | ## Features 16 | 17 | Clight allows to match your backlight level to ambient brightness, computed by capturing frames from webcam or Ambient Light Sensors. 18 | It does also support adjusting external monitors and keyboard backlight. 19 | Moreover, it can manage your screen temperature, just like redshift does. 20 | Finally, it can dim your screen after a timeout and manage screen DPMS. 21 | 22 | Note that all its features are available on both **X, Wayland and tty** and can be turned off from its config file. 23 | On wayland Clight requires specific protocols to be implemented by your compositor; have a look at https://github.com/FedeDP/Clight/wiki/Modules#wayland-support. 24 | 25 | ## GUI 26 | 27 | Github user [nullobsi](https://github.com/nullobsi) created a (super nice!) qt gui for clight, with a useful tray applet too. 28 | Remember to check it out: https://github.com/nullobsi/clight-gui! 29 | 30 | ## Developers Corner 31 | 32 | Clight makes use of [Clightd](https://github.com/FedeDP/Clightd), a system DBus service that exposes an [API](https://github.com/FedeDP/Clightd/wiki/Api) to manage various aspects of your screen and allows Webcam/ALS devices captures. 33 | Its API is as generic as possible and it has nothing specifically for Clight; this means anyone can make use of it. 34 | If you are interested, please have a look at its wiki pages too! 35 | Indeed i even developed a super simple Clight clone as an hello world application in Go: https://github.com/FedeDP/GoLight. 36 | It is much simpler than Clight for obvious reasons and i do not expect to develop it further. 37 | 38 | Both Clight and Clightd make use of [libmodule](https://github.com/FedeDP/libmodule), a C library developed with modularity in mind that offers a simple actor framework for C, with an integrated event loop. 39 | 40 | Morever, note that Clight exposes a DBus [API](https://github.com/FedeDP/Clightd/wiki/Api) itself too; it allows quickly testing config values or building scripts around it, some of which you can find in Clight FAQ: https://github.com/FedeDP/Clight/wiki/FAQ#dbus-tricks. 41 | The DBus API first and main user is clight-gui. 42 | Finally, it can also be expanded through [Custom modules](https://github.com/FedeDP/Clight/wiki/Custom-Modules) that enable users to build their own plugins to further customize Clight behaviour. 43 | 44 | ## License 45 | This software is distributed with GPL license, see [COPYING](https://github.com/FedeDP/Clight/blob/master/COPYING) file for more informations. 46 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## 5.0 2 | 3 | ### Generic 4 | - [ ] drop old config file path support (OLDCONFDIR) 5 | 6 | ### Backlight 7 | - [ ] Drop is_smooth option (no need, just specify a step/wait > 0) 8 | - [ ] Move "Capture","IncBl", "DecBl" interface methods to org.clight.clight.Conf.Backlight 9 | 10 | ### Gamma 11 | - [ ] Drop is_smooth option (no need, just specify a step/wait > 0) 12 | 13 | ### Dimmer 14 | - [ ] Drop is_smooth option (no need, just specify a step/wait > 0) 15 | 16 | ### Sensor 17 | - [ ] Allow multiple sensors to be specified in priority order; those sensors will be stored in a list and the first available will be used. 18 | - [ ] Changes: -> is_sensor_available() that will be called on each of the listed sensors; first avaiable will become highest_prio_available_dev_name 19 | - [ ] capture_frames_brightness -> will use highest_prio_available_dev_name 20 | 21 | ## Future 22 | 23 | ### Pinephone 24 | - [ ] Finalize support 25 | 26 | ### Generic 27 | - [ ] Port to libmodule 6.0.0 (?) 28 | - [ ] Add a Dump dbus method (and a DUMP_REQ request) to allow any module to dump their state (module_dump()) to a txt file 29 | -------------------------------------------------------------------------------- /src/commons.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "public.h" 10 | #include "validations.h" 11 | #include "log.h" 12 | #include 13 | #include 14 | 15 | #define UNUSED __attribute__((unused)) 16 | 17 | #define MAX_SIZE_POINTS 50 // max number of points used for polynomial regression 18 | #define DEF_SIZE_POINTS 11 // default number of points used for polynomial regression 19 | #define DEGREE 3 // number of parameters for polynomial regression 20 | 21 | #define IN_EVENT SIZE_STATES // Backlight module has 1 more state: IN_EVENT 22 | 23 | #define LAT_UNDEFINED 91.0 // Undefined (ie: unset) value for latitude 24 | #define LON_UNDEFINED 181.0 // Undefined (ie: unset) value for longitude 25 | #define LOC_DISTANCE_THRS 50 // threshold for location distances before triggering location changed events (km) 26 | 27 | #define MINIMUM_CLIGHTD_VERSION_MAJ 5 // Clightd minimum required maj version 28 | #define MINIMUM_CLIGHTD_VERSION_MIN 5 // Clightd minimum required min version -> org.clightd.clightd.Backlight2 29 | 30 | /** Generic structs **/ 31 | 32 | typedef struct { 33 | int num_points; 34 | double points[MAX_SIZE_POINTS]; 35 | double fit_parameters[DEGREE]; // best-fit parameters 36 | } curve_t; 37 | 38 | typedef struct { 39 | int no_smooth; // disable smooth backlight changes for module 40 | double trans_step; // every backlight transition step value (in pct), used when smooth transitions are enabled 41 | int trans_timeout; // every backlight transition timeout value, used when smooth transitions are enabled 42 | int trans_fixed; // fixed transitions duration 43 | } bl_smooth_t; 44 | 45 | typedef struct { 46 | int num_captures[SIZE_AC]; 47 | char *dev_name; 48 | char *dev_opts; 49 | curve_t default_curve[SIZE_AC]; // points used for regression through libgsl 50 | map_t *specific_curves; // map of monitor-specific curves 51 | } sensor_conf_t; 52 | 53 | typedef struct { 54 | int disabled; 55 | int timeout[SIZE_AC][SIZE_STATES + 1]; 56 | bl_smooth_t smooth; 57 | int no_auto_calib; // disable automatic calibration for both BACKLIGHT and GAMMA 58 | double shutter_threshold; // capture values below this threshold will be considered "shuttered" 59 | int pause_on_lid_closed; // whether clight should inhibit autocalibration on lid closed 60 | int capture_on_lid_opened; // whether to trigger a new capture whenever lid gets opened 61 | int restore; // whether backlight should be restored on Clight exit 62 | int sync_monitors_delay; // delay before syncing gamma and backlight when monitors are hotplugged 63 | } bl_conf_t; 64 | 65 | typedef struct { 66 | int disabled; // disable keyboard backlight automatic calibration (where supported) 67 | int timeout[SIZE_AC]; // kbd dimming timeout 68 | curve_t curve[SIZE_AC]; // curve used to match ambient brightness to certain keyboard backlight level 69 | } kbd_conf_t; 70 | 71 | typedef struct { 72 | int disabled; 73 | int temp[SIZE_STATES]; // screen temperature for each daytime 74 | int no_smooth; // disable smooth gamma changes 75 | int trans_step; // every gamma transition step value, used when smooth GAMMA transitions are enabled 76 | int trans_timeout; // every gamma transition timeout value, used when smooth GAMMA transitions are enabled 77 | int long_transition; // flag to enable a very long smooth transition for gamma (redshift-like) 78 | int ambient_gamma; // enable gamma adjustments based on ambient backlight 79 | int restore; // whether gamma should be restored on Clight exit 80 | } gamma_conf_t; 81 | 82 | typedef struct { 83 | char day_events[SIZE_EVENTS][10]; // sunrise/sunset times passed from cmdline opts (if setted, location module won't be started) 84 | int event_duration; // duration of an event (by default 30mins, ie: it starts 30mins before an event and ends 30mins after) 85 | loc_t loc; // user location as loaded by config 86 | int events_os[SIZE_EVENTS]; // offset for each event 87 | } daytime_conf_t; 88 | 89 | typedef struct { 90 | int disabled; 91 | double dimmed_pct; // pct of max backlight to be used while dimming 92 | int timeout[SIZE_AC]; // dimmer timeout 93 | bl_smooth_t smooth[SIZE_DIM]; 94 | } dimmer_conf_t; 95 | 96 | typedef struct { 97 | int disabled; 98 | int timeout[SIZE_AC]; // dpms timeouts 99 | } dpms_conf_t; 100 | 101 | typedef struct { 102 | int disabled; 103 | double contrib; 104 | int timeout[SIZE_AC]; // screen timeouts 105 | } screen_conf_t; 106 | 107 | typedef struct { 108 | int disabled; 109 | int inhibit_docked; // whether to manage "docked" states as inhibitions. Requires UPower. 110 | int inhibit_pm; // whether handle inhibition suppressing powermanagement too 111 | int inhibit_bl; // whether to inhibit backlight module too 112 | } inh_conf_t; 113 | 114 | /* Struct that holds global config as passed through cmdline args/config file reading */ 115 | typedef struct { 116 | bl_conf_t bl_conf; 117 | sensor_conf_t sens_conf; 118 | kbd_conf_t kbd_conf; 119 | gamma_conf_t gamma_conf; 120 | daytime_conf_t day_conf; 121 | dimmer_conf_t dim_conf; 122 | dpms_conf_t dpms_conf; 123 | screen_conf_t screen_conf; 124 | inh_conf_t inh_conf; 125 | int verbose; // whether verbose mode is enabled 126 | int wizard; // whether wizard mode is enabled 127 | int resumedelay; // delay on resume from suspend 128 | } conf_t; 129 | 130 | /* Global state of program */ 131 | 132 | /* 133 | * Using INT for BOOLeans: https://dbus.freedesktop.org/doc/dbus-specification.html 134 | * "BOOLEAN values are encoded in 32 bits (of which only the least significant bit is used)." 135 | * 136 | * Where bool type is 1B large, using bool would break this convention. 137 | */ 138 | typedef struct { 139 | bool looping; 140 | double screen_br; // screen content brightness 141 | int in_event; // Whether we are in a TIME event +- conf.event_duration 142 | int inhibited; // whether screensaver inhibition is enabled 143 | int pm_inhibited; // whether pm_inhibition is enabled 144 | int sens_avail; // whether a sensor is currently available 145 | int suspended; // whether system is suspended 146 | enum day_states day_time; // whether it is day or night time 147 | enum ac_states ac_state; // is laptop on battery? 148 | enum lid_states lid_state; // current lid state 149 | enum display_states display_state; // current display state 150 | enum day_events next_event; // next daytime event (SUNRISE or SUNSET) 151 | int event_time_range; 152 | int current_temp; // current GAMMA temp; specially useful when used with conf.ambient_gamma enabled 153 | time_t day_events[SIZE_EVENTS]; // today events (sunrise/sunset) 154 | loc_t current_loc; // current user location 155 | double current_bl_pct; // current backlight pct 156 | double current_kbd_pct; // current keyboard backlight pct 157 | double ambient_br; // last ambient brightness captured from CLIGHTD Sensor 158 | const char *clightd_version; // Clightd found version 159 | const char *version; // Clight version 160 | jmp_buf quit_buf; // quit jump called by longjmp 161 | } state_t; 162 | 163 | /** Global state and config data **/ 164 | 165 | extern state_t state; 166 | extern conf_t conf; 167 | -------------------------------------------------------------------------------- /src/conf/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commons.h" 4 | 5 | enum CONFIG { OLD_GLOBAL, GLOBAL, LOCAL, CUSTOM }; 6 | 7 | void init_config_file(enum CONFIG file, char *filename); 8 | int read_config(enum CONFIG file, char *config_file); 9 | int store_config(enum CONFIG file); 10 | -------------------------------------------------------------------------------- /src/conf/opts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "config.h" 4 | #include "bus.h" 5 | 6 | void init_opts(int argc, char *argv[]); 7 | 8 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * 3 | * clight: C daemon utility to automagically adjust screen backlight to match ambient brightness. 4 | * https://github.com/FedeDP/Clight/tree/master/clight 5 | * 6 | * Copyright (C) 2019 Federico Di Pierro 7 | * 8 | * This file is part of clight. 9 | * clight is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * END_COMMON_COPYRIGHT_HEADER */ 23 | 24 | #include 25 | #include "opts.h" 26 | #include "utils.h" 27 | 28 | static void init(int argc, char *argv[]); 29 | static void init_state(void); 30 | static void sigsegv_handler(int signum); 31 | static void check_clightd_version(void); 32 | static void init_user_mod_path(enum CONFIG file, char *filename); 33 | static void load_user_modules(enum CONFIG file); 34 | 35 | state_t state = {0}; 36 | conf_t conf = {0}; 37 | 38 | int main(int argc, char *argv[]) { 39 | int ret = setjmp(state.quit_buf); 40 | if (ret == 0) { 41 | init(argc, argv); 42 | if (conf.bl_conf.disabled && conf.dim_conf.disabled && conf.dpms_conf.disabled && conf.gamma_conf.disabled) { 43 | WARN("No functional module running. Leaving...\n"); 44 | } else { 45 | state.looping = true; 46 | ret = modules_loop(); 47 | state.looping = false; 48 | } 49 | } 50 | close_log(); 51 | free((void *)state.clightd_version); 52 | return ret; 53 | } 54 | 55 | /* 56 | * First of all loads options from both global and 57 | * local config file, and from cmdline options. 58 | * Then init needed modules. 59 | */ 60 | static void init(int argc, char *argv[]) { 61 | /* 62 | * When receiving segfault signal, 63 | * call our sigsegv handler that just logs 64 | * a debug message before dying 65 | */ 66 | signal(SIGSEGV, sigsegv_handler); 67 | 68 | /* 69 | * We want any issue while parsing config to be logged; 70 | * but not in case we are just printing version or help 71 | */ 72 | bool no_log = false; 73 | for (int i = 0; i < argc; i++) { 74 | if (strcmp(argv[i], "-v") == 0 || 75 | strcmp(argv[i], "--version") == 0 || 76 | strcmp(argv[i], "-?") == 0 || 77 | strcmp(argv[i], "--help") == 0 || 78 | strcmp(argv[i], "--usage") == 0) { 79 | 80 | no_log = true; 81 | } 82 | } 83 | 84 | if (!no_log) { 85 | open_log(); 86 | } 87 | init_opts(argc, argv); 88 | log_conf(); 89 | 90 | if (!conf.wizard) { 91 | /* We want any error while checking Clightd required version to be logged AFTER conf logging */ 92 | check_clightd_version(); 93 | init_state(); 94 | /* 95 | * Load user custom modules after opening log (thus this information is logged). 96 | * Note that local (ie: placed in $HOME) modules have higher priority, 97 | * thus one can override a global module (placed in /usr/share/clight/modules.d/) 98 | * by creating a module with same name in $HOME. 99 | * 100 | * Clight internal modules cannot be overridden. 101 | */ 102 | load_user_modules(LOCAL); 103 | load_user_modules(GLOBAL); 104 | } 105 | } 106 | 107 | static void init_state(void) { 108 | state.version = VERSION; 109 | memcpy(&state.current_loc, &conf.day_conf.loc, sizeof(loc_t)); 110 | 111 | /* 112 | * Initial states -> undefined; 113 | */ 114 | state.sens_avail = -1; 115 | state.next_event = -1; 116 | state.day_time = -1; 117 | state.ac_state = -1; 118 | state.lid_state = -1; 119 | } 120 | 121 | /* 122 | * If received a sigsegv, log a message, destroy lock then 123 | * set sigsegv signal handler to default (SIG_DFL), 124 | * and send again the signal to the process. 125 | */ 126 | static void sigsegv_handler(int signum) { 127 | WARN("Received sigsegv signal. Aborting.\n"); 128 | close_log(); 129 | signal(signum, SIG_DFL); 130 | raise(signum); 131 | } 132 | 133 | static void check_clightd_version(void) { 134 | SYSBUS_ARG(vers_args, CLIGHTD_SERVICE, "/org/clightd/clightd", "org.clightd.clightd", "Version"); 135 | 136 | int r = get_property(&vers_args, "s", &state.clightd_version); 137 | if (r < 0 || is_string_empty(state.clightd_version)) { 138 | ERROR("No clightd found. Clightd is a mandatory dep.\n"); 139 | } else { 140 | int maj_val = atoi(state.clightd_version); 141 | int min_val = atoi(strchr(state.clightd_version, '.') + 1); 142 | if (maj_val < MINIMUM_CLIGHTD_VERSION_MAJ || (maj_val == MINIMUM_CLIGHTD_VERSION_MAJ && min_val < MINIMUM_CLIGHTD_VERSION_MIN)) { 143 | ERROR("Clightd must be updated. Required version: %d.%d.\n", MINIMUM_CLIGHTD_VERSION_MAJ, MINIMUM_CLIGHTD_VERSION_MIN); 144 | } else { 145 | INFO("Clightd found, version: %s.\n", state.clightd_version); 146 | } 147 | } 148 | } 149 | 150 | static void init_user_mod_path(enum CONFIG file, char *filename) { 151 | switch (file) { 152 | case LOCAL: 153 | if (getenv("XDG_DATA_HOME")) { 154 | snprintf(filename, PATH_MAX, "%s/clight/modules.d/*", getenv("XDG_DATA_HOME")); 155 | } else { 156 | snprintf(filename, PATH_MAX, "%s/.local/share/clight/modules.d/*", getpwuid(getuid())->pw_dir); 157 | } 158 | break; 159 | case GLOBAL: 160 | snprintf(filename, PATH_MAX, "%s/modules.d/*", DATADIR); 161 | break; 162 | default: 163 | break; 164 | } 165 | } 166 | 167 | static void load_user_modules(enum CONFIG file) { 168 | char modules_path[PATH_MAX + 1]; 169 | init_user_mod_path(file, modules_path); 170 | 171 | glob_t gl = {0}; 172 | if (glob(modules_path, GLOB_NOSORT | GLOB_ERR, NULL, &gl) == 0) { 173 | for (int i = 0; i < gl.gl_pathc; i++) { 174 | if (m_load(gl.gl_pathv[i]) == MOD_OK) { 175 | INFO("'%s' loaded.\n", gl.gl_pathv[i]); 176 | } else { 177 | WARN("'%s' failed to load.\n", gl.gl_pathv[i]); 178 | } 179 | } 180 | globfree(&gl); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/modules/bus.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include "utils.h" 3 | 4 | #define GET_BUS(a) sd_bus *tmp = a->bus; if (!tmp) { tmp = a->type == USER_BUS ? userbus : sysbus; } if (!tmp) { return -1; } 5 | 6 | static void free_bus_structs(sd_bus_error *err, sd_bus_message *m, sd_bus_message *reply); 7 | static int check_err(int *r, sd_bus_error *err, const char *caller); 8 | static int proxy_async_request(struct sd_bus_message *m, void *userdata, sd_bus_error *err); 9 | 10 | static sd_bus *sysbus, *userbus; 11 | 12 | MODULE("BUS"); 13 | 14 | static void module_pre_start(void) { 15 | sd_bus_default_system(&sysbus); 16 | sd_bus_default_user(&userbus); 17 | } 18 | 19 | static void init(void) { 20 | if (!sysbus) { 21 | ERROR("BUS: Failed to connect to system bus.\n"); 22 | } 23 | 24 | if (!userbus) { 25 | ERROR("BUS: Failed to connect to user bus\n"); 26 | } 27 | 28 | sd_bus_process(sysbus, NULL); 29 | sd_bus_process(userbus, NULL); 30 | 31 | int bus_fd = sd_bus_get_fd(sysbus); 32 | int userbus_fd = sd_bus_get_fd(userbus); 33 | 34 | m_register_fd(dup(bus_fd), true, sysbus); 35 | m_register_fd(dup(userbus_fd), true, userbus); 36 | } 37 | 38 | static bool check(void) { 39 | return true; 40 | } 41 | 42 | static bool evaluate(void) { 43 | return true; 44 | } 45 | 46 | static void destroy(void) { 47 | if (sysbus) { 48 | sysbus = sd_bus_flush_close_unref(sysbus); 49 | } 50 | } 51 | 52 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 53 | switch (MSG_TYPE()) { 54 | case FD_UPD: { 55 | sd_bus *b = (sd_bus *)msg->fd_msg->userptr; 56 | int r; 57 | do { 58 | r = sd_bus_process(b, NULL); 59 | } while (r > 0); 60 | if (r == -ENOTCONN || r == -ECONNRESET) { 61 | modules_quit(r); 62 | } 63 | break; 64 | } 65 | default: 66 | break; 67 | } 68 | } 69 | 70 | /* 71 | * Call a method on bus and store its result of type userptr_type in userptr. 72 | */ 73 | int call(const bus_args *a, const char *signature, ...) { 74 | sd_bus_error error = SD_BUS_ERROR_NULL; 75 | sd_bus_message *m = NULL, *reply = NULL; 76 | GET_BUS(a); 77 | 78 | int r = sd_bus_message_new_method_call(tmp, &m, a->service, a->path, a->interface, a->member); 79 | if (check_err(&r, &error, a->caller)) { 80 | goto finish; 81 | } 82 | 83 | r = sd_bus_message_set_expect_reply(m, a->reply_cb != NULL); 84 | if (check_err(&r, &error, a->caller)) { 85 | goto finish; 86 | } 87 | 88 | if (!is_string_empty(signature)) { 89 | va_list args; 90 | va_start(args, signature); 91 | sd_bus_message_appendv(m, signature, args); 92 | va_end(args); 93 | } 94 | 95 | /* Check if we need to wait for a response message */ 96 | if (a->reply_cb != NULL) { 97 | if (!a->async) { 98 | r = sd_bus_call(tmp, m, 0, &error, &reply); 99 | } else { 100 | r = sd_bus_call_async(tmp, NULL, m, proxy_async_request, (void *)a, 0); 101 | } 102 | if (check_err(&r, &error, a->caller)) { 103 | goto finish; 104 | } 105 | if (!a->async) { 106 | r = a->reply_cb(reply, a->member, a->reply_userdata); 107 | } 108 | } else { 109 | r = sd_bus_send(tmp, m, NULL); 110 | } 111 | check_err(&r, &error, a->caller); 112 | 113 | finish: 114 | free_bus_structs(&error, m, reply); 115 | return r; 116 | } 117 | 118 | /* 119 | * Add a match on bus on certain signal for cb callback 120 | */ 121 | int add_match(const bus_args *a, sd_bus_slot **slot, sd_bus_message_handler_t cb) { 122 | GET_BUS(a); 123 | 124 | #if LIBSYSTEMD_VERSION >= 237 125 | int r = sd_bus_match_signal(tmp, slot, a->service, a->path, a->interface, a->member, cb, NULL); 126 | #else 127 | char match[500] = {0}; 128 | snprintf(match, sizeof(match), "type='signal', sender='%s', interface='%s', member='%s', path='%s'", a->service, a->interface, a->member, a->path); 129 | int r = sd_bus_add_match(tmp, slot, match, cb, NULL); 130 | #endif 131 | return check_err(&r, NULL, a->caller); 132 | } 133 | 134 | int set_property(const bus_args *a, const char *type, const uintptr_t value) { 135 | GET_BUS(a); 136 | sd_bus_error error = SD_BUS_ERROR_NULL; 137 | 138 | int r = -EINVAL; 139 | if (type) { 140 | r = sd_bus_set_property(tmp, a->service, a->path, a->interface, a->member, &error, type, value); 141 | } 142 | check_err(&r, &error, a->caller); 143 | free_bus_structs(&error, NULL, NULL); 144 | return r; 145 | } 146 | 147 | int get_property(const bus_args *a, const char *type, void *userptr) { 148 | sd_bus_error error = SD_BUS_ERROR_NULL; 149 | sd_bus_message *m = NULL; 150 | GET_BUS(a); 151 | 152 | int r = -EINVAL; 153 | if (type) { 154 | switch (*type) { 155 | case SD_BUS_TYPE_STRING: 156 | case SD_BUS_TYPE_OBJECT_PATH: { 157 | r = sd_bus_get_property(tmp, a->service, a->path, a->interface, a->member, &error, &m, type); 158 | const char *obj = NULL; 159 | r = sd_bus_message_read(m, type, &obj); 160 | if (r >= 0) { 161 | *((char **)userptr) = strdup(obj); // must be freed by caller 162 | } 163 | break; 164 | } 165 | default: 166 | r = sd_bus_get_property_trivial(tmp, a->service, a->path, a->interface, a->member, &error, *type, userptr); 167 | break; 168 | } 169 | } 170 | check_err(&r, NULL, a->caller); 171 | free_bus_structs(&error, m, NULL); 172 | return r; 173 | } 174 | 175 | static void free_bus_structs(sd_bus_error *err, sd_bus_message *m, sd_bus_message *reply) { 176 | if (err) { 177 | sd_bus_error_free(err); 178 | } 179 | if (m) { 180 | sd_bus_message_unref(m); 181 | } 182 | if (reply) { 183 | sd_bus_message_unref(reply); 184 | } 185 | } 186 | 187 | static int check_err(int *r, sd_bus_error *err, const char *caller) { 188 | if (*r < 0) { 189 | DEBUG("%s(): %s\n", caller, err && err->message ? err->message : strerror(-*r)); 190 | } 191 | 192 | /* -1 on error, 0 ok */ 193 | *r = -(*r < 0); 194 | return *r; 195 | } 196 | 197 | static int proxy_async_request(struct sd_bus_message *m, void *userdata, sd_bus_error *err) { 198 | if (sd_bus_message_is_method_error(m, NULL)) { 199 | WARN("Error in async req: %s\n", err->message ? err->message : "unknown"); 200 | return 0; 201 | } 202 | bus_args *a = (bus_args *)userdata; 203 | return a->reply_cb(m, a->member, a->reply_userdata); 204 | } 205 | 206 | sd_bus *get_user_bus(void) { 207 | return userbus; 208 | } 209 | -------------------------------------------------------------------------------- /src/modules/bus.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "timer.h" 5 | 6 | #define CLIGHTD_SERVICE "org.clightd.clightd" 7 | 8 | /* Bus types */ 9 | enum bus_type { SYSTEM_BUS, USER_BUS }; 10 | 11 | /* Bus reply read callback */ 12 | typedef int(*bus_recv_cb)(sd_bus_message *reply, const char *member, void *userdata); 13 | 14 | /* 15 | * Object wrapper for bus calls 16 | */ 17 | typedef struct { 18 | const char *service; 19 | const char *path; 20 | const char *interface; 21 | const char *member; 22 | enum bus_type type; 23 | bus_recv_cb reply_cb; 24 | void *reply_userdata; 25 | const char *caller; 26 | sd_bus *bus; 27 | bool async; // ASYNC requests NEED a static/heap memory bus_args!! 28 | } bus_args; 29 | 30 | #define BUS_ARG(name, ...) bus_args name = { __VA_ARGS__, __func__ }; 31 | 32 | /* Define a bus_args local variable to actually parse message response */ 33 | #define USERBUS_ARG_REPLY(name, cb, userdata, ...) BUS_ARG(name, __VA_ARGS__, USER_BUS, cb, userdata); 34 | #define SYSBUS_ARG_REPLY(name, cb, userdata, ...) BUS_ARG(name, __VA_ARGS__, SYSTEM_BUS, cb, userdata); 35 | 36 | /* Define a bus_args local variable to discard message response (ie: not needed to be parsed) */ 37 | #define USERBUS_ARG(name, ...) USERBUS_ARG_REPLY(name, NULL, NULL, __VA_ARGS__); 38 | #define SYSBUS_ARG(name, ...) SYSBUS_ARG_REPLY(name, NULL, NULL, __VA_ARGS__); 39 | 40 | 41 | int call(const bus_args *a, const char *signature, ...); 42 | int add_match(const bus_args *a, sd_bus_slot **slot, sd_bus_message_handler_t cb); 43 | int set_property(const bus_args *a, const char *type, const uintptr_t value); 44 | int get_property(const bus_args *a, const char *type, void *userptr); 45 | sd_bus *get_user_bus(void); 46 | -------------------------------------------------------------------------------- /src/modules/dimmer.c: -------------------------------------------------------------------------------- 1 | #include "idler.h" 2 | #include "utils.h" 3 | 4 | static void receive_waiting_acstate(const msg_t *msg, UNUSED const void *userdata); 5 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata); 6 | static int on_new_idle(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 7 | static void timeout_callback(void); 8 | static void pause_dimmer(const bool pause, enum mod_pause reason); 9 | 10 | static sd_bus_slot *slot; 11 | static char client[PATH_MAX + 1]; 12 | static const sd_bus_vtable conf_dimmer_vtable[] = { 13 | SD_BUS_VTABLE_START(0), 14 | SD_BUS_WRITABLE_PROPERTY("NoSmoothEnter", "b", NULL, NULL, offsetof(dimmer_conf_t, smooth[ENTER].no_smooth), 0), 15 | SD_BUS_WRITABLE_PROPERTY("NoSmoothExit", "b", NULL, NULL, offsetof(dimmer_conf_t, smooth[EXIT].no_smooth), 0), 16 | SD_BUS_WRITABLE_PROPERTY("DimmedPct", "d", NULL, NULL, offsetof(dimmer_conf_t, dimmed_pct), 0), 17 | SD_BUS_WRITABLE_PROPERTY("TransStepEnter", "d", NULL, NULL, offsetof(dimmer_conf_t, smooth[ENTER].trans_step), 0), 18 | SD_BUS_WRITABLE_PROPERTY("TransStepExit", "d", NULL, NULL, offsetof(dimmer_conf_t, smooth[EXIT].trans_step), 0), 19 | SD_BUS_WRITABLE_PROPERTY("TransDurationEnter", "i", NULL, NULL, offsetof(dimmer_conf_t, smooth[ENTER].trans_timeout), 0), 20 | SD_BUS_WRITABLE_PROPERTY("TransDurationExit", "i", NULL, NULL, offsetof(dimmer_conf_t, smooth[EXIT].trans_timeout), 0), 21 | SD_BUS_WRITABLE_PROPERTY("TransFixedEnter", "i", NULL, NULL, offsetof(dimmer_conf_t, smooth[ENTER].trans_fixed), 0), 22 | SD_BUS_WRITABLE_PROPERTY("TransFixedExit", "i", NULL, NULL, offsetof(dimmer_conf_t, smooth[EXIT].trans_fixed), 0), 23 | SD_BUS_WRITABLE_PROPERTY("AcTimeout", "i", NULL, set_timeouts, offsetof(dimmer_conf_t, timeout[ON_AC]), 0), 24 | SD_BUS_WRITABLE_PROPERTY("BattTimeout", "i", NULL, set_timeouts, offsetof(dimmer_conf_t, timeout[ON_BATTERY]), 0), 25 | SD_BUS_VTABLE_END 26 | }; 27 | 28 | DECLARE_MSG(display_req, DISPLAY_REQ); 29 | DECLARE_MSG(bl_req, BL_REQ); 30 | 31 | API(Dimmer, conf_dimmer_vtable, conf.dim_conf); 32 | MODULE_WITH_PAUSE("DIMMER"); 33 | 34 | static void init(void) { 35 | M_SUB(UPOWER_UPD); 36 | M_SUB(INHIBIT_UPD); 37 | M_SUB(SUSPEND_UPD); 38 | M_SUB(DIMMER_TO_REQ); 39 | M_SUB(SIMULATE_REQ); 40 | m_become(waiting_acstate); 41 | 42 | init_Dimmer_api(); 43 | } 44 | 45 | static bool check(void) { 46 | return true; 47 | } 48 | 49 | static bool evaluate(void) { 50 | return !conf.dim_conf.disabled; 51 | } 52 | 53 | static void receive_waiting_acstate(const msg_t *msg, UNUSED const void *userdata) { 54 | switch (MSG_TYPE()) { 55 | case UPOWER_UPD: { 56 | int r = idle_init(client, &slot, on_new_idle); 57 | if (r != 0) { 58 | WARN("Failed to init. Killing module.\n"); 59 | module_deregister((self_t **)&self()); 60 | } else { 61 | m_unbecome(); 62 | 63 | // Eventually pause dimmer if initial timeout is <= 0, else set the initial timeout 64 | timeout_callback(); 65 | } 66 | break; 67 | } 68 | default: 69 | break; 70 | } 71 | } 72 | 73 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 74 | switch (MSG_TYPE()) { 75 | case UPOWER_UPD: 76 | timeout_callback(); 77 | break; 78 | case INHIBIT_UPD: 79 | pause_dimmer(state.inhibited, INHIBIT); 80 | break; 81 | case SUSPEND_UPD: 82 | pause_dimmer(state.suspended, SUSPEND); 83 | break; 84 | case DIMMER_TO_REQ: { 85 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 86 | if (VALIDATE_REQ(up)) { 87 | conf.dim_conf.timeout[up->state] = up->new; 88 | if (up->state == state.ac_state) { 89 | timeout_callback(); 90 | } 91 | } 92 | break; 93 | } 94 | case SIMULATE_REQ: { 95 | /* Validation is useless here; only for coherence */ 96 | if (VALIDATE_REQ((void *)msg->ps_msg->message)) { 97 | idle_client_reset(client, conf.dim_conf.timeout[state.ac_state]); 98 | } 99 | break; 100 | } 101 | default: 102 | break; 103 | } 104 | } 105 | 106 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata) { 107 | switch (MSG_TYPE()) { 108 | case UPOWER_UPD: 109 | timeout_callback(); 110 | break; 111 | case INHIBIT_UPD: 112 | pause_dimmer(state.inhibited, INHIBIT); 113 | break; 114 | case SUSPEND_UPD: 115 | pause_dimmer(state.suspended, SUSPEND); 116 | break; 117 | case DIMMER_TO_REQ: { 118 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 119 | if (VALIDATE_REQ(up)) { 120 | conf.dim_conf.timeout[up->state] = up->new; 121 | if (up->state == state.ac_state) { 122 | timeout_callback(); 123 | } 124 | } 125 | break; 126 | } 127 | default: 128 | /* SIMULATE_REQ is not handled while paused */ 129 | break; 130 | } 131 | } 132 | 133 | static void destroy(void) { 134 | idle_client_destroy(client); 135 | if (slot) { 136 | slot = sd_bus_slot_unref(slot); 137 | } 138 | deinit_Dimmer_api(); 139 | } 140 | 141 | static int on_new_idle(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 142 | int idle; 143 | 144 | /* Unused in requests! */ 145 | display_req.display.old = state.display_state; 146 | sd_bus_message_read(m, "b", &idle); 147 | if (idle) { 148 | display_req.display.new = DISPLAY_DIMMED; 149 | } else { 150 | display_req.display.new = DISPLAY_ON; 151 | } 152 | M_PUB(&display_req); 153 | return 0; 154 | } 155 | 156 | static void timeout_callback(void) { 157 | if (conf.dim_conf.timeout[state.ac_state] <= 0) { 158 | pause_dimmer(true, TIMEOUT); 159 | } else { 160 | pause_dimmer(false, TIMEOUT); 161 | idle_set_timeout(client, conf.dim_conf.timeout[state.ac_state]); 162 | } 163 | } 164 | 165 | static void pause_dimmer(const bool pause, enum mod_pause reason) { 166 | if (CHECK_PAUSE(pause, reason)) { 167 | if (!pause) { 168 | idle_client_start(client, conf.dim_conf.timeout[state.ac_state]); 169 | m_unbecome(); 170 | } else { 171 | idle_client_stop(client); 172 | m_become(paused); 173 | } 174 | } 175 | } 176 | 177 | void dimmer_publish_bl_req(const double pct, enum dim_trans trans) { 178 | bl_req.bl.new = pct; 179 | bl_req.bl.smooth = -2 - trans; 180 | M_PUB(&bl_req); 181 | } 182 | -------------------------------------------------------------------------------- /src/modules/display.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include "utils.h" 3 | 4 | static int hook_logind_idle_signal(void); 5 | static int on_idle_hint_changed(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error); 6 | 7 | static sd_bus_slot *slot; 8 | 9 | DECLARE_MSG(display_msg, DISPLAY_UPD); 10 | DECLARE_MSG(display_req, DISPLAY_REQ); 11 | 12 | MODULE("DISPLAY"); 13 | 14 | extern void dimmer_publish_bl_req(const double pct, enum dim_trans trans); 15 | extern void set_dpms(bool enable); 16 | 17 | static void init(void) { 18 | display_req.display.no_backlight = true; 19 | M_SUB(DISPLAY_REQ); 20 | 21 | /* 22 | * Try to hook logind IdleHint property, only if dimmer mod is disabled: 23 | * this guarantees us that only a single DIMMED state producer is enabled. 24 | * Moreover, when dimmer module is enabled, there is no real use of managing IdleHint. 25 | * Note that we won't touch backlight as user disabled DIMMER module; 26 | * we will only manage display state as dimmed, eventually pausing backlight and other modules. 27 | */ 28 | if (conf.dim_conf.disabled) { 29 | hook_logind_idle_signal(); 30 | } 31 | } 32 | 33 | static bool check(void) { 34 | return true; 35 | } 36 | 37 | static bool evaluate(void) { 38 | return true; 39 | } 40 | 41 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 42 | static double old_pct = -1.0; 43 | 44 | switch (MSG_TYPE()) { 45 | case DISPLAY_REQ: { 46 | display_upd *up = (display_upd *)MSG_DATA(); 47 | if (VALIDATE_REQ(up)) { 48 | display_msg.display.old = state.display_state; 49 | switch (up->new) { 50 | case DISPLAY_DIMMED: 51 | state.display_state |= DISPLAY_DIMMED; 52 | DEBUG("Entering dimmed state...\n"); 53 | if (!up->no_backlight) { 54 | // Message arrives from DIMMER module (or external plugin) 55 | if (state.current_bl_pct > conf.dim_conf.dimmed_pct) { 56 | old_pct = state.current_bl_pct; 57 | dimmer_publish_bl_req(conf.dim_conf.dimmed_pct, ENTER); 58 | } else { 59 | DEBUG("A lower than dimmer_pct backlight level is already set. Avoid changing it.\n"); 60 | } 61 | } 62 | break; 63 | case DISPLAY_OFF: 64 | state.display_state |= DISPLAY_OFF; 65 | DEBUG("Entering dpms state...\n"); 66 | if (!up->no_backlight) { 67 | set_dpms(true); 68 | } 69 | break; 70 | case DISPLAY_ON: 71 | if (state.display_state & DISPLAY_OFF) { 72 | state.display_state &= ~DISPLAY_OFF; 73 | DEBUG("Leaving dpms state...\n"); 74 | if (!up->no_backlight) { 75 | set_dpms(false); 76 | } 77 | } 78 | if (state.display_state & DISPLAY_DIMMED) { 79 | state.display_state &= ~DISPLAY_DIMMED; 80 | DEBUG("Leaving dimmed state...\n"); 81 | if (!up->no_backlight && old_pct >= 0.0) { 82 | dimmer_publish_bl_req(old_pct, EXIT); 83 | old_pct = -1.0; 84 | } 85 | } 86 | break; 87 | default: 88 | break; 89 | } 90 | display_msg.display.new = state.display_state; 91 | M_PUB(&display_msg); 92 | 93 | /* Properly set the IdleHint on logind where available */ 94 | SYSBUS_ARG(args, "org.freedesktop.login1", "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session", "SetIdleHint"); 95 | call(&args, "b", state.display_state); 96 | } 97 | break; 98 | } 99 | default: 100 | break; 101 | } 102 | } 103 | 104 | static void destroy(void) { 105 | if (slot) { 106 | slot = sd_bus_slot_unref(slot); 107 | } 108 | } 109 | 110 | static int hook_logind_idle_signal(void) { 111 | SYSBUS_ARG(args, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.DBus.Properties", "PropertiesChanged"); 112 | return add_match(&args, &slot, on_idle_hint_changed); 113 | } 114 | 115 | static int on_idle_hint_changed(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 116 | int idle; 117 | 118 | /* Unused in requests! */ 119 | display_req.display.old = state.display_state; 120 | // This comes from logind! 121 | SYSBUS_ARG(idle_args, "org.freedesktop.login1", "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session", "IdleHint"); 122 | get_property(&idle_args, "b", &idle); 123 | if (idle) { 124 | display_req.display.new = DISPLAY_DIMMED; 125 | } else { 126 | display_req.display.new = DISPLAY_ON; 127 | } 128 | M_PUB(&display_req); 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /src/modules/dpms.c: -------------------------------------------------------------------------------- 1 | #include "idler.h" 2 | #include "utils.h" 3 | 4 | static void receive_waiting_acstate(const msg_t *msg, UNUSED const void *userdata); 5 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata); 6 | static int on_new_idle(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 7 | static void timeout_callback(void); 8 | static void pause_dpms(const bool pause, enum mod_pause reason); 9 | 10 | static sd_bus_slot *slot, *dpms_slot; 11 | static char client[PATH_MAX + 1]; 12 | static const sd_bus_vtable conf_dpms_vtable[] = { 13 | SD_BUS_VTABLE_START(0), 14 | SD_BUS_WRITABLE_PROPERTY("AcTimeout", "i", NULL, set_timeouts, offsetof(dpms_conf_t, timeout[ON_AC]), 0), 15 | SD_BUS_WRITABLE_PROPERTY("BattTimeout", "i", NULL, set_timeouts, offsetof(dpms_conf_t, timeout[ON_BATTERY]), 0), 16 | SD_BUS_VTABLE_END 17 | }; 18 | 19 | DECLARE_MSG(display_req, DISPLAY_REQ); 20 | 21 | API(Dpms, conf_dpms_vtable, conf.dpms_conf); 22 | MODULE_WITH_PAUSE("DPMS"); 23 | 24 | static void init(void) { 25 | M_SUB(UPOWER_UPD); 26 | M_SUB(INHIBIT_UPD); 27 | M_SUB(SUSPEND_UPD); 28 | M_SUB(DPMS_TO_REQ); 29 | M_SUB(SIMULATE_REQ); 30 | m_become(waiting_acstate); 31 | 32 | init_Dpms_api(); 33 | } 34 | 35 | static bool check(void) { 36 | return true; 37 | } 38 | 39 | static bool evaluate(void) { 40 | return !conf.dpms_conf.disabled; 41 | } 42 | 43 | static void receive_waiting_acstate(const msg_t *msg, UNUSED const void *userdata) { 44 | switch (MSG_TYPE()) { 45 | case UPOWER_UPD: { 46 | int r = idle_init(client, &slot, on_new_idle); 47 | if (r != 0) { 48 | WARN("Failed to init. Killing module.\n"); 49 | module_deregister((self_t **)&self()); 50 | } else { 51 | m_unbecome(); 52 | 53 | SYSBUS_ARG(args, CLIGHTD_SERVICE, "/org/clightd/clightd/Dpms", "org.clightd.clightd.Dpms", "Changed"); 54 | add_match(&args, &dpms_slot, on_new_idle); 55 | 56 | // Eventually pause dpms if initial timeout is <= 0, else set the initial timeout 57 | timeout_callback(); 58 | } 59 | break; 60 | } 61 | default: 62 | break; 63 | } 64 | } 65 | 66 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 67 | switch (MSG_TYPE()) { 68 | case UPOWER_UPD: 69 | timeout_callback(); 70 | break; 71 | case INHIBIT_UPD: 72 | pause_dpms(state.inhibited, INHIBIT); 73 | break; 74 | case SUSPEND_UPD: 75 | pause_dpms(state.suspended, SUSPEND); 76 | break; 77 | case DPMS_TO_REQ: { 78 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 79 | if (VALIDATE_REQ(up)) { 80 | conf.dpms_conf.timeout[up->state] = up->new; 81 | if (up->state == state.ac_state) { 82 | timeout_callback(); 83 | } 84 | } 85 | break; 86 | } 87 | case SIMULATE_REQ: { 88 | /* Validation is useless here; only for coherence */ 89 | if (VALIDATE_REQ((void *)msg->ps_msg->message)) { 90 | idle_client_reset(client, conf.dpms_conf.timeout[state.ac_state]); 91 | } 92 | break; 93 | } 94 | default: 95 | break; 96 | } 97 | } 98 | 99 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata) { 100 | switch (MSG_TYPE()) { 101 | case UPOWER_UPD: 102 | timeout_callback(); 103 | break; 104 | case INHIBIT_UPD: 105 | pause_dpms(state.inhibited, INHIBIT); 106 | break; 107 | case SUSPEND_UPD: 108 | pause_dpms(state.suspended, SUSPEND); 109 | break; 110 | case DPMS_TO_REQ: { 111 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 112 | if (VALIDATE_REQ(up)) { 113 | conf.dpms_conf.timeout[up->state] = up->new; 114 | if (up->state == state.ac_state) { 115 | timeout_callback(); 116 | } 117 | } 118 | break; 119 | } 120 | default: 121 | /* SIMULATE_REQ is not handled while paused */ 122 | break; 123 | } 124 | } 125 | 126 | static void destroy(void) { 127 | idle_client_destroy(client); 128 | if (slot) { 129 | slot = sd_bus_slot_unref(slot); 130 | } 131 | if (dpms_slot) { 132 | dpms_slot = sd_bus_slot_unref(dpms_slot); 133 | } 134 | deinit_Dpms_api(); 135 | } 136 | 137 | static int on_new_idle(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 138 | int idle = -1; 139 | 140 | /* Only account for our display for Dpms.Changed signals */ 141 | if (!strcmp(sd_bus_message_get_member(m), "Changed")) { 142 | const char *display = NULL; 143 | int level; 144 | sd_bus_message_read(m, "si", &display, &level); 145 | if (own_display(display)) { 146 | idle = level > 0; 147 | } 148 | } else { 149 | sd_bus_message_read(m, "b", &idle); 150 | } 151 | 152 | if (idle != -1) { 153 | /* Unused in requests! */ 154 | display_req.display.old = state.display_state; 155 | if (idle) { 156 | display_req.display.new = DISPLAY_OFF; 157 | } else { 158 | display_req.display.new = DISPLAY_ON; 159 | } 160 | M_PUB(&display_req); 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | static void timeout_callback(void) { 167 | if (conf.dpms_conf.timeout[state.ac_state] <= 0) { 168 | pause_dpms(true, TIMEOUT); 169 | } else { 170 | pause_dpms(false, TIMEOUT); 171 | idle_set_timeout(client, conf.dpms_conf.timeout[state.ac_state]); 172 | } 173 | } 174 | 175 | static void pause_dpms(const bool pause, enum mod_pause reason) { 176 | if (CHECK_PAUSE(pause, reason)) { 177 | if (!pause) { 178 | idle_client_start(client, conf.dpms_conf.timeout[state.ac_state]); 179 | m_unbecome(); 180 | } else { 181 | idle_client_stop(client); 182 | m_become(paused); 183 | } 184 | } 185 | } 186 | 187 | void set_dpms(bool enable) { 188 | SYSBUS_ARG(args, CLIGHTD_SERVICE, "/org/clightd/clightd/Dpms", "org.clightd.clightd.Dpms", "Set"); 189 | call(&args, "ssi", fetch_display(), fetch_env(), enable); 190 | } 191 | -------------------------------------------------------------------------------- /src/modules/inhibit.c: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | 3 | static void on_inhibit_req(inhibit_upd *up); 4 | 5 | static const sd_bus_vtable conf_inh_vtable[] = { 6 | SD_BUS_VTABLE_START(0), 7 | SD_BUS_WRITABLE_PROPERTY("InhibitDocked", "b", NULL, NULL, offsetof(inh_conf_t, inhibit_docked), 0), 8 | SD_BUS_WRITABLE_PROPERTY("InhibitPM", "b", NULL, NULL, offsetof(inh_conf_t, inhibit_pm), 0), 9 | SD_BUS_WRITABLE_PROPERTY("InhibitBL", "b", NULL, NULL, offsetof(inh_conf_t, inhibit_bl), 0), 10 | SD_BUS_VTABLE_END 11 | }; 12 | 13 | API(Inhibit, conf_inh_vtable, conf.inh_conf); 14 | MODULE("INHIBIT"); 15 | 16 | static void init(void) { 17 | M_SUB(INHIBIT_REQ); 18 | 19 | init_Inhibit_api(); 20 | } 21 | 22 | static bool check(void) { 23 | return true; 24 | } 25 | 26 | static bool evaluate() { 27 | return !conf.inh_conf.disabled; 28 | } 29 | 30 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 31 | switch (MSG_TYPE()) { 32 | case INHIBIT_REQ: { 33 | inhibit_upd *up = (inhibit_upd *)MSG_DATA(); 34 | on_inhibit_req(up); 35 | break; 36 | } 37 | default: 38 | break; 39 | } 40 | } 41 | 42 | static void destroy(void) { 43 | deinit_Inhibit_api(); 44 | } 45 | 46 | static void on_inhibit_req(inhibit_upd *up) { 47 | static int inhibition_ctr; 48 | if (VALIDATE_REQ(up)) { 49 | /* Drop an inhibition from our counter */ 50 | if (!up->new) { 51 | if (!up->force) { 52 | inhibition_ctr--; 53 | DEBUG("ScreenSaver inhibition released.\n"); 54 | } else { 55 | inhibition_ctr = 0; 56 | DEBUG("ScreenSaver inhibition forcefully cleared.\n"); 57 | } 58 | } 59 | 60 | if (up->new || inhibition_ctr == 0) { 61 | DECLARE_HEAP_MSG(inh_msg, INHIBIT_UPD); 62 | inh_msg->inhibit.old = state.inhibited; 63 | state.inhibited = up->new; 64 | inh_msg->inhibit.new = state.inhibited; 65 | M_PUB(inh_msg); 66 | } 67 | } 68 | /* Count currently held inhibitions */ 69 | if (up->new) { 70 | inhibition_ctr++; 71 | DEBUG("ScreenSaver inhibition grabbed.\n"); 72 | } 73 | DEBUG("Inhibitions ctr: %d\n", inhibition_ctr); 74 | } 75 | -------------------------------------------------------------------------------- /src/modules/interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bus.h" 4 | 5 | #define VALIDATE_PARAMS(m, signature, ...) \ 6 | int r = sd_bus_message_read(m, signature, __VA_ARGS__); \ 7 | if (r < 0) { \ 8 | WARN("Failed to parse parameters: %s\n", strerror(-r)); \ 9 | return r; \ 10 | } 11 | 12 | #define API_CONCAT(prefix, apiName, suffix) prefix##_##apiName##_##suffix 13 | #define API(apiName, vtable, config) \ 14 | static sd_bus_slot *API_CONCAT(apiName, api, slot); \ 15 | static void API_CONCAT(init, apiName, api)(void) { \ 16 | const char conf_path[] = "/org/clight/clight/Conf/" # apiName; \ 17 | const char conf_interface[] = "org.clight.clight.Conf." # apiName; \ 18 | sd_bus *userbus = get_user_bus(); \ 19 | int r = sd_bus_add_object_vtable(userbus, \ 20 | &API_CONCAT(apiName, api, slot), \ 21 | conf_path, \ 22 | conf_interface, \ 23 | vtable, \ 24 | &config); \ 25 | if (r < 0) { \ 26 | WARN("Could not create dbus interface '%s': %s\n", conf_interface, strerror(-r)); \ 27 | } \ 28 | } \ 29 | static void API_CONCAT(deinit, apiName, api)(void) { \ 30 | if (API_CONCAT(apiName, api, slot)) { sd_bus_slot_unrefp(&API_CONCAT(apiName, api, slot)); } \ 31 | } 32 | 33 | int set_timeouts(sd_bus *bus, const char *path, const char *interface, const char *property, 34 | sd_bus_message *value, void *userdata, sd_bus_error *error); 35 | int get_curve(sd_bus *bus, const char *path, const char *interface, const char *property, 36 | sd_bus_message *reply, void *userdata, sd_bus_error *error); 37 | int set_curve(sd_bus *bus, const char *path, const char *interface, const char *property, 38 | sd_bus_message *value, void *userdata, sd_bus_error *error); 39 | int get_location(sd_bus *bus, const char *path, const char *interface, const char *property, 40 | sd_bus_message *reply, void *userdata, sd_bus_error *error); 41 | int set_location(sd_bus *bus, const char *path, const char *interface, const char *property, 42 | sd_bus_message *value, void *userdata, sd_bus_error *error); 43 | -------------------------------------------------------------------------------- /src/modules/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | #include "utils.h" 3 | #include "my_math.h" 4 | 5 | static void receive_waiting_init(const msg_t *const msg, UNUSED const void* userdata); 6 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata); 7 | static int init_kbd_backlight(void); 8 | static void on_screen_bl_update(bl_upd *up); 9 | static void set_keyboard_level(double level); 10 | static void set_keyboard_timeout(void); 11 | static void on_curve_req(double *regr_points, int num_points, enum ac_states s); 12 | static void pause_kbd(const bool pause, enum mod_pause reason); 13 | 14 | static const sd_bus_vtable conf_kbd_vtable[] = { 15 | SD_BUS_VTABLE_START(0), 16 | SD_BUS_WRITABLE_PROPERTY("AcTimeout", "i", NULL, set_timeouts, offsetof(kbd_conf_t, timeout[ON_AC]), 0), 17 | SD_BUS_WRITABLE_PROPERTY("BattTimeout", "i", NULL, set_timeouts, offsetof(kbd_conf_t, timeout[ON_BATTERY]), 0), 18 | SD_BUS_WRITABLE_PROPERTY("AcPoints", "ad", get_curve, set_curve, offsetof(kbd_conf_t, curve[ON_AC]), 0), 19 | SD_BUS_WRITABLE_PROPERTY("BattPoints", "ad", get_curve, set_curve, offsetof(kbd_conf_t, curve[ON_BATTERY]), 0), 20 | SD_BUS_VTABLE_END 21 | }; 22 | 23 | DECLARE_MSG(kbd_msg, KBD_BL_UPD); 24 | DECLARE_MSG(kbd_req, KBD_BL_REQ); 25 | 26 | API(Kbd, conf_kbd_vtable, conf.kbd_conf); 27 | MODULE_WITH_PAUSE("KEYBOARD"); 28 | 29 | static void init(void) { 30 | if (init_kbd_backlight() == 0) { 31 | M_SUB(DISPLAY_UPD); 32 | M_SUB(BL_UPD); 33 | M_SUB(KBD_BL_REQ); 34 | M_SUB(UPOWER_UPD); 35 | M_SUB(KBD_TO_REQ); 36 | M_SUB(SUSPEND_UPD); 37 | M_SUB(KBD_CURVE_REQ); 38 | m_become(waiting_init); 39 | 40 | polynomialfit(NULL, &conf.kbd_conf.curve[ON_AC], "AC keyboard backlight"); 41 | polynomialfit(NULL, &conf.kbd_conf.curve[ON_BATTERY], "BATT keyboard backlight"); 42 | 43 | init_Kbd_api(); 44 | } else { 45 | module_deregister((self_t **)&self()); 46 | } 47 | } 48 | 49 | static bool check(void) { 50 | return true; 51 | } 52 | 53 | static bool evaluate() { 54 | return !conf.kbd_conf.disabled; 55 | } 56 | 57 | 58 | static void receive_waiting_init(const msg_t *const msg, UNUSED const void* userdata) { 59 | switch (MSG_TYPE()) { 60 | case UPOWER_UPD: 61 | m_unbecome(); 62 | 63 | // Eventually pause keyboard if current timeout is <= 0 64 | set_keyboard_timeout(); 65 | break; 66 | default: 67 | break; 68 | } 69 | } 70 | 71 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 72 | switch (MSG_TYPE()) { 73 | case BL_UPD: { 74 | on_screen_bl_update((bl_upd *)MSG_DATA()); 75 | break; 76 | } 77 | case KBD_BL_REQ: { 78 | bl_upd *up = (bl_upd *)MSG_DATA(); 79 | /* Do not change kbd backlight for dimmed state (ie: when entering dimmed state)! */ 80 | if (VALIDATE_REQ(up) && !state.display_state) { 81 | set_keyboard_level(up->new); 82 | } 83 | break; 84 | } 85 | case KBD_TO_REQ: { 86 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 87 | if (VALIDATE_REQ(up)) { 88 | conf.kbd_conf.timeout[up->state] = up->new; 89 | if (up->state == state.ac_state) { 90 | set_keyboard_timeout(); 91 | } 92 | } 93 | break; 94 | } 95 | case DISPLAY_UPD: 96 | pause_kbd(state.display_state, DISPLAY); 97 | break; 98 | case UPOWER_UPD: 99 | set_keyboard_timeout(); 100 | break; 101 | case SUSPEND_UPD: 102 | pause_kbd(state.suspended, SUSPEND); 103 | break; 104 | case KBD_CURVE_REQ: { 105 | curve_upd *up = (curve_upd *)MSG_DATA(); 106 | if (VALIDATE_REQ(up)) { 107 | on_curve_req(up->regression_points, up->num_points, up->state); 108 | } 109 | break; 110 | } 111 | default: 112 | break; 113 | } 114 | } 115 | 116 | static void receive_paused(const msg_t *const msg, UNUSED const void* userdata) { 117 | switch (MSG_TYPE()) { 118 | case DISPLAY_UPD: 119 | pause_kbd(state.display_state, DISPLAY); 120 | break; 121 | case UPOWER_UPD: 122 | set_keyboard_timeout(); 123 | break; 124 | case SUSPEND_UPD: 125 | pause_kbd(state.suspended, SUSPEND); 126 | break; 127 | case KBD_CURVE_REQ: { 128 | curve_upd *up = (curve_upd *)MSG_DATA(); 129 | if (VALIDATE_REQ(up)) { 130 | on_curve_req(up->regression_points, up->num_points, up->state); 131 | } 132 | break; 133 | } 134 | default: 135 | break; 136 | } 137 | } 138 | 139 | static void destroy(void) { 140 | deinit_Kbd_api(); 141 | } 142 | 143 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 144 | const char *service_list; 145 | int r = sd_bus_message_read(reply, "s", &service_list); 146 | if (r >= 0) { 147 | // Check if /org/clightd/clightd/KbdBacklight has some nodes (it means we have got kbd backlight) 148 | if (strstr(service_list, "new, &conf.kbd_conf.curve[state.ac_state]); 167 | /* 168 | * Only log for first BL_UPD message received: 169 | * * either the one with up->smooth = true 170 | * * or the only one sent when conf.bl_conf.smooth is disabled 171 | */ 172 | if (up->smooth || conf.bl_conf.smooth.no_smooth) { 173 | // Less verbose: only log real kbdbacklight changes, unless we are in verbose mode 174 | if (new_kbd_pct != state.current_kbd_pct || conf.verbose || first_time) { 175 | INFO("Screen backlight: %.3lf -> Keyboard backlight: %.3lf.\n", up->new, new_kbd_pct); 176 | first_time = false; 177 | } 178 | } 179 | 180 | /* 181 | * Only actually act for: 182 | * * Smooth steps 183 | * * Non-smooth target 184 | */ 185 | if (!up->smooth) { 186 | kbd_req.bl.new = new_kbd_pct; 187 | M_PUB(&kbd_req); 188 | } 189 | } 190 | 191 | static void set_keyboard_level(double level) { 192 | SYSBUS_ARG(kbd_args, CLIGHTD_SERVICE, "/org/clightd/clightd/KbdBacklight", "org.clightd.clightd.KbdBacklight", "Set"); 193 | kbd_msg.bl.old = state.current_kbd_pct; 194 | if (call(&kbd_args, "d", level) == 0) { 195 | state.current_kbd_pct = level; 196 | kbd_msg.bl.new = state.current_kbd_pct; 197 | M_PUB(&kbd_msg); 198 | } 199 | } 200 | 201 | static void set_keyboard_timeout(void) { 202 | pause_kbd(conf.kbd_conf.timeout[state.ac_state] <= 0, TIMEOUT); 203 | if (conf.kbd_conf.timeout[state.ac_state] > 0) { 204 | SYSBUS_ARG(kbd_args, CLIGHTD_SERVICE, "/org/clightd/clightd/KbdBacklight", "org.clightd.clightd.KbdBacklight", "SetTimeout"); 205 | call(&kbd_args, "i", conf.kbd_conf.timeout[state.ac_state]); 206 | } 207 | } 208 | 209 | /* Callback on "AcCurvePoints" and "BattCurvePoints" bus exposed writable properties */ 210 | static void on_curve_req(double *regr_points, int num_points, enum ac_states s) { 211 | curve_t *c = &conf.kbd_conf.curve[s]; 212 | if (regr_points) { 213 | memcpy(c->points, 214 | regr_points, num_points * sizeof(double)); 215 | c->num_points = num_points; 216 | } 217 | polynomialfit(NULL, c, s == ON_AC ? "AC keyboard backlight" : "BATT keyboard backlight"); 218 | } 219 | 220 | static void pause_kbd(const bool pause, enum mod_pause reason) { 221 | if (CHECK_PAUSE(pause, reason)) { 222 | if (!pause) { 223 | m_unbecome(); 224 | // Set correct level for current backlight 225 | set_keyboard_level(1.0 - state.current_bl_pct); 226 | } else { 227 | // Switch off keyboard backlight 228 | set_keyboard_level(0.0); 229 | m_become(paused); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/modules/location.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include "utils.h" 3 | 4 | /** 5 | * LOCATION module will be deregistered if: 6 | * * geoclue is not present 7 | * * geoclue is present but fail to start correctly 8 | * * geoclue is present and started correctly, but it is not responding (killed after GEOCLUE_TIMEOUT seconds) 9 | */ 10 | 11 | #define LOC_TIME_THRS 600 // time threshold (seconds) before triggering location changed events (10mins) 12 | #define GEOCLUE_TIMEOUT 30 // timeout for waiting on geoclue to give us a position; otherwise kills module 13 | 14 | typedef enum { GEOCLUE_NONE, GEOCLUE_PRESENT, GEOCLUE_STARTED, GEOCLUE_FAILED } geoclue_state; 15 | 16 | static void fail_geoclue(geoclue_state st); 17 | static void load_cache_location(void); 18 | static void init_cache_file(void); 19 | static int geoclue_init(void); 20 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 21 | static int geoclue_ping(void); 22 | static int geoclue_get_client(void); 23 | static int geoclue_hook_update(void); 24 | static int on_geoclue_new_location(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 25 | static int geoclue_client_start(void); 26 | static void geoclue_client_delete(void); 27 | static void cache_location(void); 28 | static void publish_location(double new_lat, double new_lon, message_t *l); 29 | 30 | static sd_bus_slot *slot; 31 | static int timeout_fd = -1; 32 | static geoclue_state geoclue_st; 33 | static char client[PATH_MAX + 1], cache_file[PATH_MAX + 1]; 34 | 35 | DECLARE_MSG(loc_msg, LOC_UPD); 36 | DECLARE_MSG(loc_req, LOCATION_REQ); 37 | 38 | MODULE("LOCATION"); 39 | 40 | static void init(void) { 41 | init_cache_file(); 42 | M_SUB(LOCATION_REQ); 43 | 44 | // Give 30s of time to geoclue to give us a position before killing module 45 | timeout_fd = start_timer(CLOCK_BOOTTIME, GEOCLUE_TIMEOUT, 0); 46 | m_register_fd(timeout_fd, true, NULL); 47 | } 48 | 49 | static bool check(void) { 50 | return true; 51 | } 52 | 53 | static bool evaluate(void) { 54 | /* 55 | * Only start when neither a location nor fixed times 56 | * for both events are specified in conf 57 | */ 58 | return !conf.wizard && 59 | ((conf.day_conf.loc.lat == LAT_UNDEFINED || conf.day_conf.loc.lon == LON_UNDEFINED) && 60 | (is_string_empty(conf.day_conf.day_events[SUNRISE]) || is_string_empty(conf.day_conf.day_events[SUNSET]))); 61 | } 62 | 63 | /* 64 | * Stop geoclue2 client and store latest location to cache. 65 | */ 66 | static void destroy(void) { 67 | if (!is_string_empty(client)) { 68 | geoclue_client_delete(); 69 | } 70 | /* Destroy this match slot */ 71 | if (slot) { 72 | slot = sd_bus_slot_unref(slot); 73 | } 74 | } 75 | 76 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 77 | switch (MSG_TYPE()) { 78 | case FD_UPD: 79 | read_timer(msg->fd_msg->fd); 80 | /* 81 | * We had no cached location and geoclue client timed out; 82 | * tell other modules an go on dropping location 83 | */ 84 | switch (geoclue_st) { 85 | case GEOCLUE_NONE: 86 | WARN("Failed to init (no geoclue2 present?). Killing module.\n"); 87 | break; 88 | case GEOCLUE_PRESENT: 89 | case GEOCLUE_STARTED: 90 | WARN("Timed out waiting on location provided by Geoclue2. Killing module.\n"); 91 | break; 92 | case GEOCLUE_FAILED: 93 | WARN("Failed to start Geoclue2 client. Killing module.\n"); 94 | break; 95 | } 96 | 97 | if (state.current_loc.lat == LAT_UNDEFINED || state.current_loc.lon == LON_UNDEFINED) { 98 | publish_location(LAT_UNDEFINED, LON_UNDEFINED, &loc_msg); 99 | } 100 | 101 | // if we came here, just kill ourself 102 | module_deregister((self_t **)&self()); 103 | break; 104 | case LOCATION_REQ: { 105 | loc_upd *l = (loc_upd *)MSG_DATA(); 106 | if (VALIDATE_REQ(l)) { 107 | INFO("New location received: %.2lf, %.2lf.\n", l->new.lat, l->new.lon); 108 | // publish location before storing new location as state.current_loc is sent as "old" parameter 109 | publish_location(l->new.lat, l->new.lon, &loc_msg); 110 | memcpy(&state.current_loc, &l->new, sizeof(loc_t)); 111 | } 112 | break; 113 | } 114 | case SYSTEM_UPD: 115 | if (msg->ps_msg->type == LOOP_STARTED) { 116 | load_cache_location(); 117 | if (geoclue_init() != 0) { 118 | /* 119 | * no cached location present and no geoclue; 120 | * fire immediately to take care of module cleanup and deregister 121 | */ 122 | fail_geoclue(GEOCLUE_NONE); 123 | } else { 124 | geoclue_st = GEOCLUE_PRESENT; 125 | } 126 | } else if (msg->ps_msg->type == LOOP_STOPPED) { 127 | cache_location(); 128 | } 129 | break; 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | static void fail_geoclue(geoclue_state st) { 136 | geoclue_st = st; 137 | set_timeout(0, 1, timeout_fd, 0); 138 | } 139 | 140 | static void load_cache_location(void) { 141 | FILE *f = fopen(cache_file, "r"); 142 | if (f) { 143 | double new_lat, new_lon; 144 | if (fscanf(f, "%lf %lf\n", &new_lat, &new_lon) == 2) { 145 | publish_location(new_lat, new_lon, &loc_req); 146 | DEBUG("%.2lf %.2lf loaded from cache file.\n", new_lat, new_lon); 147 | } else { 148 | WARN("Could not load cached location, wrong format.\n"); 149 | } 150 | fclose(f); 151 | } else { 152 | DEBUG("Could not find location cache file.\n"); 153 | } 154 | } 155 | 156 | static void init_cache_file(void) { 157 | if (getenv("XDG_CACHE_HOME")) { 158 | snprintf(cache_file, PATH_MAX, "%s/clight", getenv("XDG_CACHE_HOME")); 159 | } else { 160 | snprintf(cache_file, PATH_MAX, "%s/.cache/clight", getpwuid(getuid())->pw_dir); 161 | } 162 | } 163 | 164 | static int geoclue_init(void) { 165 | // Check if geoclue is available, otherwise die 166 | int r = geoclue_ping(); 167 | if (r >= 0) { 168 | // GetClient is called async, and will later start the client in its callback, see parse_bus_reply 169 | r = geoclue_get_client(); 170 | if (r < 0) { 171 | WARN("Geoclue2 is present but failed to give us a client.\n"); 172 | } 173 | } else { 174 | DEBUG("Geoclue2 is not present.\n"); 175 | } 176 | return -(r < 0); // - 1 on error 177 | } 178 | 179 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 180 | int r = -EINVAL; 181 | /* 182 | * If self is null it means module was already deregistered; 183 | * may be geoclue took too long to respond and timeout_fd killed the module. 184 | * Note that "GetClient" answer is received async thus can be called even after 185 | * module was deregistered. 186 | * In this case, ignore the reply. 187 | */ 188 | if (!strcmp(member, "Ping") || self() == NULL) { 189 | return 0; 190 | } 191 | if (!strcmp(member, "GetClient")) { 192 | const char *cl = NULL; 193 | r = sd_bus_message_read(reply, "o", &cl); 194 | if (r >= 0 && cl) { 195 | strncpy(client, cl, PATH_MAX); 196 | r = geoclue_hook_update(); 197 | if (r >= 0) { 198 | r = geoclue_client_start(); 199 | } 200 | } 201 | if (r < 0 || !cl) { 202 | fail_geoclue(GEOCLUE_FAILED); 203 | } else { 204 | geoclue_st = GEOCLUE_STARTED; 205 | } 206 | } 207 | return r; 208 | } 209 | 210 | static int geoclue_ping(void) { 211 | SYSBUS_ARG_REPLY(args, parse_bus_reply, NULL, "org.freedesktop.GeoClue2", "/org/freedesktop/GeoClue2", "org.freedesktop.DBus.Peer", "Ping"); 212 | return call(&args, NULL); 213 | } 214 | 215 | /* 216 | * Store Client object path in client (static) global var 217 | */ 218 | static int geoclue_get_client(void) { 219 | // Make it async! We need a static lifetime object! 220 | static SYSBUS_ARG_REPLY(args, parse_bus_reply, NULL, "org.freedesktop.GeoClue2", "/org/freedesktop/GeoClue2/Manager", "org.freedesktop.GeoClue2.Manager", "GetClient"); 221 | args.async = true; 222 | return call(&args, NULL); 223 | } 224 | 225 | /* 226 | * Hook our geoclue_new_location callback to PropertiesChanged dbus signals on GeoClue2 service. 227 | */ 228 | static int geoclue_hook_update(void) { 229 | SYSBUS_ARG(args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "LocationUpdated"); 230 | return add_match(&args, &slot, on_geoclue_new_location); 231 | } 232 | 233 | /* 234 | * On new location callback: retrieve new_location object, 235 | * then retrieve latitude and longitude from that object and store them in our conf struct. 236 | */ 237 | static int on_geoclue_new_location(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 238 | const char *new_location; 239 | sd_bus_message_read(m, "oo", NULL, &new_location); 240 | 241 | double new_lat, new_lon; 242 | 243 | SYSBUS_ARG(lat_args, "org.freedesktop.GeoClue2", new_location, "org.freedesktop.GeoClue2.Location", "Latitude"); 244 | SYSBUS_ARG(lon_args, "org.freedesktop.GeoClue2", new_location, "org.freedesktop.GeoClue2.Location", "Longitude"); 245 | int r = get_property(&lat_args, "d", &new_lat) + 246 | get_property(&lon_args, "d", &new_lon); 247 | if (!r) { 248 | DEBUG("%.2lf %.2lf received from Geoclue2.\n", new_lat, new_lon); 249 | publish_location(new_lat, new_lon, &loc_req); 250 | if (timeout_fd != -1) { 251 | // disable timeout_fd as geoclue is responding! 252 | m_deregister_fd(timeout_fd); 253 | timeout_fd = -1; 254 | } 255 | } 256 | return 0; 257 | } 258 | 259 | /* 260 | * Start our geoclue2 client after having correctly set needed properties. 261 | */ 262 | static int geoclue_client_start(void) { 263 | SYSBUS_ARG(call_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "Start"); 264 | SYSBUS_ARG(id_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "DesktopId"); 265 | SYSBUS_ARG(thres_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "DistanceThreshold"); 266 | SYSBUS_ARG(time_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "TimeThreshold"); 267 | SYSBUS_ARG(accuracy_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "RequestedAccuracyLevel"); 268 | 269 | /* It now needs proper /usr/share/applications/clightc.desktop name */ 270 | int r = set_property(&id_args, "s", (uintptr_t)"clightc"); 271 | set_property(&time_args, "u", LOC_TIME_THRS); 272 | set_property(&thres_args, "u", LOC_DISTANCE_THRS * 1000); 273 | r += set_property(&accuracy_args, "u", 2); // https://www.freedesktop.org/software/geoclue/docs/geoclue-gclue-enums.html#GClueAccuracyLevel -> GCLUE_ACCURACY_LEVEL_CITY 274 | return r + call(&call_args, NULL); 275 | } 276 | 277 | /* 278 | * Stop and delete geoclue2 client. 279 | */ 280 | static void geoclue_client_delete(void) { 281 | SYSBUS_ARG(stop_args, "org.freedesktop.GeoClue2", client, "org.freedesktop.GeoClue2.Client", "Stop"); 282 | call(&stop_args, NULL); 283 | 284 | SYSBUS_ARG(del_args, "org.freedesktop.GeoClue2", "/org/freedesktop/GeoClue2/Manager", "org.freedesktop.GeoClue2.Manager", "DeleteClient"); 285 | call(&del_args, "o", client); 286 | } 287 | 288 | static void cache_location(void) { 289 | if (state.current_loc.lat != LAT_UNDEFINED && state.current_loc.lon != LON_UNDEFINED) { 290 | FILE *f = fopen(cache_file, "w"); 291 | if (f) { 292 | fprintf(f, "%lf %lf\n", state.current_loc.lat, state.current_loc.lon); 293 | DEBUG("Latest location stored in cache file!\n"); 294 | fclose(f); 295 | } else { 296 | WARN("Caching location failed: %s.\n", strerror(errno)); 297 | } 298 | } 299 | } 300 | 301 | static void publish_location(double new_lat, double new_lon, message_t *l) { 302 | l->loc.old.lat = state.current_loc.lat; 303 | l->loc.old.lon = state.current_loc.lon; 304 | l->loc.new.lat = new_lat; 305 | l->loc.new.lon = new_lon; 306 | M_PUB(l); 307 | } 308 | -------------------------------------------------------------------------------- /src/modules/pm.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include 3 | 4 | static int hook_suspend_signal(void); 5 | static int session_active_listener_init(void); 6 | static int on_new_suspend(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error); 7 | static int on_session_change(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 8 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 9 | static void publish_pm_msg(const bool old, const bool new); 10 | static void publish_susp_req(const bool new); 11 | static void on_pm_req(const bool new); 12 | static void on_suspend_req(suspend_upd *up); 13 | 14 | MODULE("PM"); 15 | 16 | static sd_bus_slot *slot; 17 | static unsigned int pm_inh_token; 18 | static int delayed_resume_fd; 19 | 20 | static enum 21 | { 22 | NONE = 0, 23 | SYSTEMD, 24 | POWERMANAGEMENT 25 | } inh_api; 26 | 27 | static void init(void) { 28 | pm_inh_token = -1; // UINT MAX 29 | 30 | M_SUB(PM_REQ); 31 | M_SUB(INHIBIT_UPD); 32 | M_SUB(SUSPEND_REQ); 33 | 34 | hook_suspend_signal(); 35 | session_active_listener_init(); 36 | 37 | delayed_resume_fd = start_timer(CLOCK_BOOTTIME, 0, 0); 38 | } 39 | 40 | static bool check(void) { 41 | return true; 42 | } 43 | 44 | static bool evaluate() { 45 | return !conf.inh_conf.disabled; 46 | } 47 | 48 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 49 | switch (MSG_TYPE()) { 50 | case FD_UPD: { 51 | /* We are reading a message delayed of conf.resumedelay */ 52 | message_t *suspend_msg = (message_t *)msg->fd_msg->userptr; 53 | read_timer(delayed_resume_fd); 54 | M_PUB(suspend_msg); 55 | m_deregister_fd(delayed_resume_fd); 56 | break; 57 | } 58 | case INHIBIT_UPD: { 59 | if (conf.inh_conf.inhibit_pm) { 60 | DECLARE_HEAP_MSG(pm_req, PM_REQ); 61 | pm_req->pm.old = !state.inhibited; 62 | pm_req->pm.new = state.inhibited; 63 | M_PUB(pm_req); 64 | } 65 | break; 66 | } 67 | case PM_REQ: { 68 | pm_upd *up = (pm_upd *)MSG_DATA(); 69 | if (VALIDATE_REQ(up)) { 70 | on_pm_req(up->new); 71 | } 72 | break; 73 | } 74 | case SYSTEM_UPD: 75 | /* Release any PowerManagement inhibition upon leaving as we're not PM manager */ 76 | if (msg->ps_msg->type == LOOP_STOPPED && pm_inh_token != -1) { 77 | on_pm_req(false); 78 | } 79 | break; 80 | case SUSPEND_REQ: { 81 | suspend_upd *up = (suspend_upd *)MSG_DATA(); 82 | // VALIDATE_REQ is called inside 83 | on_suspend_req(up); 84 | break; 85 | } 86 | default: 87 | break; 88 | } 89 | } 90 | 91 | static void destroy(void) { 92 | /* Destroy this match slot */ 93 | if (slot) { 94 | slot = sd_bus_slot_unref(slot); 95 | } 96 | close(delayed_resume_fd); 97 | } 98 | 99 | static int hook_suspend_signal(void) { 100 | SYSBUS_ARG(args, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PrepareForSleep"); 101 | return add_match(&args, &slot, on_new_suspend); 102 | } 103 | 104 | static int session_active_listener_init(void) { 105 | SYSBUS_ARG(args, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.DBus.Properties", "PropertiesChanged"); 106 | return add_match(&args, &slot, on_session_change); 107 | } 108 | 109 | static int on_new_suspend(sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 110 | int going_suspend; 111 | sd_bus_message_read(m, "b", &going_suspend); 112 | 113 | publish_susp_req(going_suspend); 114 | return 0; 115 | } 116 | 117 | /* Listener on logind session.Active for current session */ 118 | static int on_session_change(UNUSED sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 119 | static int active = 1; 120 | SYSBUS_ARG(args, "org.freedesktop.login1", "/org/freedesktop/login1/session/auto", "org.freedesktop.login1.Session", "Active"); 121 | int old_active = active; 122 | int r = get_property(&args, "b", &active); 123 | if (!r) { 124 | if (active != old_active) { 125 | publish_susp_req(!active); 126 | } 127 | } 128 | return 0; 129 | } 130 | 131 | static int parse_bus_reply(sd_bus_message *reply, const char *member, 132 | void *userdata) { 133 | switch (inh_api) { 134 | case SYSTEMD: { 135 | unsigned int f; 136 | int r = sd_bus_message_read(reply, "h", &f); 137 | if (r >= 0) { 138 | pm_inh_token = fcntl(f, F_DUPFD_CLOEXEC, 3); 139 | } 140 | return r; 141 | } 142 | case POWERMANAGEMENT: { 143 | return sd_bus_message_read(reply, "u", userdata); 144 | } 145 | default: 146 | return -EINVAL; 147 | } 148 | } 149 | 150 | static void publish_pm_msg(const bool old, const bool new) { 151 | DECLARE_HEAP_MSG(pm_msg, PM_UPD); 152 | pm_msg->pm.old = old; 153 | pm_msg->pm.new = new; 154 | M_PUB(pm_msg); 155 | state.pm_inhibited = new; 156 | } 157 | 158 | static void publish_susp_req(const bool new) { 159 | DECLARE_HEAP_MSG(suspend_req, SUSPEND_REQ); 160 | suspend_req->suspend.new = new; 161 | M_PUB(suspend_req); 162 | } 163 | 164 | static bool acquire_systemd_lock(void) { 165 | inh_api = SYSTEMD; 166 | SYSBUS_ARG_REPLY(pm_args, parse_bus_reply, &pm_inh_token, 167 | "org.freedesktop.login1", "/org/freedesktop/login1", 168 | "org.freedesktop.login1.Manager", "Inhibit"); 169 | int ret = call(&pm_args, "ssss", "idle", "Clight", "Idle inhibitor.", "block"); 170 | if (ret == 0 && pm_inh_token > 0) { 171 | DEBUG("Holding inhibition with systemd-inhibit.\n"); 172 | return true; 173 | } 174 | 175 | inh_api = NONE; 176 | if (ret < 0) { 177 | DEBUG("Failed to parse systemd-inhibit D-Bus response.\n"); 178 | } else { 179 | DEBUG("Failed to copy lock file\n"); 180 | } 181 | return false; 182 | } 183 | 184 | static bool acquire_pm_lock(void) { 185 | inh_api = POWERMANAGEMENT; 186 | SYSBUS_ARG_REPLY(pm_args, parse_bus_reply, &pm_inh_token, 187 | "org.freedesktop.PowerManagement.Inhibit", 188 | "/org/freedesktop/PowerManagement/Inhibit", 189 | "org.freedesktop.PowerManagement.Inhibit", 190 | "Inhibit"); 191 | if (call(&pm_args, "ss", "Clight", "Idle inhibitor.", "block") == 0) { 192 | DEBUG("Holding inhibition with PowerManagement.\n"); 193 | return true; 194 | } 195 | inh_api = NONE; 196 | DEBUG("Failed to parse PowerManagement D-Bus response.\n"); 197 | return false; 198 | } 199 | 200 | static bool release_lock(void) { 201 | switch (inh_api) { 202 | case SYSTEMD: { 203 | close(pm_inh_token); 204 | DEBUG("Released systemd-inhibit idle inhibition.\n"); 205 | pm_inh_token = -1; 206 | return true; 207 | } 208 | case POWERMANAGEMENT: { 209 | USERBUS_ARG(pm_args, "org.freedesktop.PowerManagement.Inhibit", 210 | "/org/freedesktop/PowerManagement/Inhibit", 211 | "org.freedesktop.PowerManagement.Inhibit", "UnInhibit"); 212 | if (call(&pm_args, "u", pm_inh_token) == 0) { 213 | DEBUG("Released PowerManagement idle inhibition.\n"); 214 | pm_inh_token = -1; 215 | return true; 216 | } 217 | } 218 | default: 219 | return false; 220 | } 221 | } 222 | 223 | static void on_pm_req(const bool new) { 224 | if (new && pm_inh_token == -1) { 225 | if (acquire_systemd_lock() || acquire_pm_lock()) { 226 | publish_pm_msg(false, true); 227 | } 228 | } else if (!new && pm_inh_token != -1) { 229 | if (release_lock()) { 230 | publish_pm_msg(true, false); 231 | inh_api = NONE; 232 | } 233 | } 234 | } 235 | 236 | static void on_suspend_req(suspend_upd *up) { 237 | static int suspend_ctr = 0; 238 | if (VALIDATE_REQ(up)) { 239 | /* Drop a suspend source from our counter */ 240 | if (!up->new) { 241 | if (!up->force) { 242 | suspend_ctr--; 243 | } else { 244 | suspend_ctr = 0; 245 | } 246 | } 247 | 248 | if (up->new || suspend_ctr == 0) { 249 | DECLARE_HEAP_MSG(suspend_msg, SUSPEND_UPD); 250 | suspend_msg->suspend.old = state.suspended; 251 | state.suspended = up->new; 252 | suspend_msg->suspend.new = up->new; 253 | /* 254 | * NOTE: resuming from suspend can be delayed up to 30s 255 | * because in some reported cases, clight is too fast to 256 | * sync screen temperature, failing because Xorg is still not fully resumed. 257 | */ 258 | if (!up->new && conf.resumedelay > 0) { 259 | set_timeout(conf.resumedelay, 0, delayed_resume_fd, 0); 260 | m_register_fd(delayed_resume_fd, false, suspend_msg); 261 | } else { 262 | M_PUB(suspend_msg); 263 | } 264 | } 265 | } 266 | /* Count currently held suspends */ 267 | if (up->new) { 268 | suspend_ctr++; 269 | } 270 | DEBUG("Suspend ctr: %d\n", suspend_ctr); 271 | } 272 | -------------------------------------------------------------------------------- /src/modules/screen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "interface.h" 3 | #include "my_math.h" 4 | #include "utils.h" 5 | 6 | static void receive_waiting_state(const msg_t *msg, UNUSED const void *userdata); 7 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 8 | static int get_screen_brightness(bool emit); 9 | static void timeout_callback(int old_val, bool reset); 10 | static void pause_screen(bool pause, enum mod_pause type, bool reset_screen_br); 11 | static int set_contrib(sd_bus *bus, const char *path, const char *interface, const char *property, 12 | sd_bus_message *value, void *userdata, sd_bus_error *error); 13 | 14 | static int screen_fd = -1; 15 | static enum msg_type curr_msg; 16 | static const sd_bus_vtable conf_screen_vtable[] = { 17 | SD_BUS_VTABLE_START(0), 18 | SD_BUS_WRITABLE_PROPERTY("Contrib", "d", NULL, set_contrib, offsetof(screen_conf_t, contrib), 0), 19 | SD_BUS_WRITABLE_PROPERTY("AcTimeout", "i", NULL, set_timeouts, offsetof(screen_conf_t, timeout[ON_AC]), 0), 20 | SD_BUS_WRITABLE_PROPERTY("BattTimeout", "i", NULL, set_timeouts, offsetof(screen_conf_t, timeout[ON_BATTERY]), 0), 21 | SD_BUS_VTABLE_END 22 | }; 23 | 24 | DECLARE_MSG(contrib_req, CONTRIB_REQ); 25 | DECLARE_MSG(screen_msg, SCREEN_BR_UPD); 26 | 27 | API(Screen, conf_screen_vtable, conf.screen_conf); 28 | MODULE_WITH_PAUSE("SCREEN"); 29 | 30 | static void init(void) { 31 | M_SUB(AMBIENT_BR_UPD); 32 | M_SUB(SCR_TO_REQ); 33 | M_SUB(UPOWER_UPD); 34 | M_SUB(DISPLAY_UPD); 35 | M_SUB(SENS_UPD); 36 | M_SUB(LID_UPD); 37 | M_SUB(SUSPEND_UPD); 38 | M_SUB(NO_AUTOCALIB_UPD); 39 | M_SUB(INHIBIT_UPD); 40 | M_SUB(CONTRIB_REQ); 41 | 42 | if (get_screen_brightness(false) != 0) { 43 | // We are on an unsupported wayland compositor; kill ourself immediately without further message processing 44 | WARN("Failed to init. Killing module.\n"); 45 | module_deregister((self_t **)&self()); 46 | } else { 47 | m_become(waiting_state); 48 | init_Screen_api(); 49 | } 50 | } 51 | 52 | static bool check(void) { 53 | return true; 54 | } 55 | 56 | static bool evaluate(void) { 57 | return !conf.screen_conf.disabled; 58 | } 59 | 60 | static void destroy(void) { 61 | if (screen_fd >= 0) { 62 | close(screen_fd); 63 | } 64 | deinit_Screen_api(); 65 | } 66 | 67 | static void receive_waiting_state(const msg_t *msg, UNUSED const void *userdata) { 68 | switch (MSG_TYPE()) { 69 | case UPOWER_UPD: { 70 | m_unbecome(); 71 | 72 | /* Start paused if screen timeout for current ac state is <= 0 */ 73 | screen_fd = start_timer(CLOCK_BOOTTIME, conf.screen_conf.timeout[state.ac_state], 0); 74 | m_register_fd(screen_fd, false, NULL); 75 | timeout_callback(-1, false); 76 | 77 | pause_screen(conf.screen_conf.contrib == 0.0f, CONTRIB, true); 78 | break; 79 | } 80 | default: 81 | break; 82 | } 83 | } 84 | 85 | static void receive(const msg_t *msg, UNUSED const void *userdata) { 86 | curr_msg = MSG_TYPE(); 87 | switch (MSG_TYPE()) { 88 | case AMBIENT_BR_UPD: 89 | pause_screen(state.ambient_br < conf.bl_conf.shutter_threshold, CLOGGED, false); 90 | if (!paused_state) { 91 | get_screen_brightness(true); 92 | } 93 | break; 94 | case FD_UPD: 95 | read_timer(screen_fd); 96 | get_screen_brightness(true); 97 | set_timeout(conf.screen_conf.timeout[state.ac_state], 0, screen_fd, 0); 98 | break; 99 | case UPOWER_UPD: { 100 | upower_upd *up = (upower_upd *)MSG_DATA(); 101 | timeout_callback(conf.screen_conf.timeout[up->old], true); 102 | break; 103 | } 104 | case DISPLAY_UPD: 105 | pause_screen(state.display_state, DISPLAY, false); 106 | break; 107 | case SENS_UPD: 108 | pause_screen(!state.sens_avail, SENSOR, true); 109 | break; 110 | case LID_UPD: 111 | if (conf.bl_conf.pause_on_lid_closed) { 112 | pause_screen(state.lid_state, LID, false); 113 | } 114 | break; 115 | case SUSPEND_UPD: 116 | pause_screen(state.suspended, SUSPEND, false); 117 | break; 118 | case SCR_TO_REQ: { 119 | timeout_upd *up = (timeout_upd *)MSG_DATA(); 120 | if (VALIDATE_REQ(up)) { 121 | const int old = conf.screen_conf.timeout[up->state]; 122 | conf.screen_conf.timeout[up->state] = up->new; 123 | if (up->state == state.ac_state) { 124 | timeout_callback(old, true); 125 | } 126 | } 127 | break; 128 | } 129 | case NO_AUTOCALIB_UPD: { 130 | calib_upd *up = (calib_upd *)MSG_DATA(); 131 | pause_screen(up->new, AUTOCALIB, true); 132 | break; 133 | } 134 | case INHIBIT_UPD: { 135 | pause_screen(state.inhibited && conf.inh_conf.inhibit_bl, INHIBIT, true); 136 | break; 137 | } 138 | case CONTRIB_REQ: { 139 | contrib_upd *up = (contrib_upd *)MSG_DATA(); 140 | if (VALIDATE_REQ(up)) { 141 | conf.screen_conf.contrib = up->new; 142 | pause_screen(conf.screen_conf.contrib == 0.0f, CONTRIB, true); 143 | // Refresh current screen brightness with new contrib! 144 | if (!paused_state) { 145 | M_PUB(&screen_msg); 146 | } 147 | } 148 | break; 149 | } 150 | default: 151 | break; 152 | } 153 | } 154 | 155 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 156 | int r = -EINVAL; 157 | if (!strcmp(member, "GetEmittedBrightness")) { 158 | r = sd_bus_message_read(reply, "d", userdata); 159 | } 160 | return r; 161 | } 162 | 163 | static int get_screen_brightness(bool emit) { 164 | if (paused_state) { 165 | return 0; 166 | } 167 | 168 | double new_br = 0.0f; 169 | SYSBUS_ARG_REPLY(args, parse_bus_reply, &new_br, CLIGHTD_SERVICE, "/org/clightd/clightd/Screen", "org.clightd.clightd.Screen", "GetEmittedBrightness"); 170 | 171 | screen_msg.bl.old = state.screen_br; 172 | int ret = call(&args, "ss", fetch_display(), fetch_env()); 173 | if (ret == 0 && emit) { 174 | state.screen_br = new_br; 175 | screen_msg.bl.new = state.screen_br; 176 | M_PUB(&screen_msg); 177 | } 178 | return ret; 179 | } 180 | 181 | static void timeout_callback(int old_val, bool reset) { 182 | if (conf.screen_conf.timeout[state.ac_state] <= 0) { 183 | pause_screen(true, TIMEOUT, true); 184 | } else { 185 | pause_screen(false, TIMEOUT, false); 186 | if (reset) { 187 | reset_timer(screen_fd, old_val, conf.screen_conf.timeout[state.ac_state]); 188 | } 189 | } 190 | } 191 | 192 | static void pause_screen(bool pause, enum mod_pause type, bool reset_screen_br) { 193 | if (CHECK_PAUSE(pause, type)) { 194 | if (pause) { 195 | /* Stop capturing snapshots */ 196 | m_deregister_fd(screen_fd); 197 | } else { 198 | /* Resume capturing */ 199 | m_register_fd(screen_fd, false, NULL); 200 | } 201 | } 202 | 203 | /* 204 | * Even if we were already paused: 205 | * account anyway for new paused request and 206 | * reset screen br if requested. 207 | */ 208 | if (pause && reset_screen_br) { 209 | state.screen_br = 0.0f; 210 | } 211 | } 212 | 213 | static int set_contrib(sd_bus *bus, const char *path, const char *interface, const char *property, 214 | sd_bus_message *value, void *userdata, sd_bus_error *error) { 215 | VALIDATE_PARAMS(value, "d", &contrib_req.contrib.new); 216 | 217 | M_PUB(&contrib_req); 218 | return r; 219 | } 220 | -------------------------------------------------------------------------------- /src/modules/signal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "commons.h" 4 | 5 | DECLARE_MSG(suspend_req, SUSPEND_REQ); 6 | DECLARE_MSG(capture_req, CAPTURE_REQ); 7 | 8 | MODULE("SIGNAL"); 9 | 10 | /* 11 | * Set signals handler for SIGINT, SIGTERM and SIGHUP (using a signalfd) 12 | * SIGHUP is sent by systemd upon leaving user session! 13 | * See: https://fedoraproject.org/wiki/Changes/KillUserProcesses_by_default 14 | */ 15 | static void init(void) { 16 | capture_req.capture.reset_timer = false; 17 | capture_req.capture.capture_only = false; 18 | 19 | sigset_t mask; 20 | 21 | sigemptyset(&mask); 22 | sigaddset(&mask, SIGINT); 23 | sigaddset(&mask, SIGTERM); 24 | sigaddset(&mask, SIGHUP); 25 | sigaddset(&mask, SIGTSTP); 26 | sigaddset(&mask, SIGCONT); 27 | sigaddset(&mask, SIGUSR1); 28 | sigprocmask(SIG_BLOCK, &mask, NULL); 29 | 30 | int fd = signalfd(-1, &mask, 0); 31 | m_register_fd(fd, true, NULL); 32 | } 33 | 34 | static bool check(void) { 35 | return true; 36 | } 37 | 38 | static bool evaluate() { 39 | return true; 40 | } 41 | 42 | 43 | static void destroy(void) { 44 | /* Skeleton function needed for modules interface */ 45 | } 46 | 47 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 48 | switch (MSG_TYPE()) { 49 | case FD_UPD: { 50 | struct signalfd_siginfo fdsi; 51 | ssize_t s; 52 | 53 | s = read(msg->fd_msg->fd, &fdsi, sizeof(struct signalfd_siginfo)); 54 | if (s != sizeof(struct signalfd_siginfo)) { 55 | ERROR("An error occurred while getting signalfd data.\n"); 56 | } 57 | switch (fdsi.ssi_signo) { 58 | case SIGTSTP: 59 | suspend_req.suspend.new = true; 60 | M_PUB(&suspend_req); 61 | break; 62 | case SIGCONT: 63 | suspend_req.suspend.new = false; 64 | M_PUB(&suspend_req); 65 | break; 66 | case SIGUSR1: 67 | M_PUB(&capture_req); 68 | break; 69 | default: 70 | INFO("Received %d. Leaving.\n", fdsi.ssi_signo); 71 | modules_quit(EXIT_SUCCESS); 72 | break; 73 | } 74 | break; 75 | } 76 | default: 77 | break; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/modules/upower.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | 3 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 4 | static int upower_check(void); 5 | static int upower_init(void); 6 | static int on_upower_change(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); 7 | static void publish_upower(int new, message_t *up); 8 | static void publish_lid(bool new, message_t *up); 9 | static void publish_inh(bool new); 10 | 11 | static sd_bus_slot *slot; 12 | 13 | DECLARE_MSG(upower_msg, UPOWER_UPD); 14 | DECLARE_MSG(upower_req, UPOWER_REQ); 15 | DECLARE_MSG(lid_msg, LID_UPD); 16 | DECLARE_MSG(lid_req, LID_REQ); 17 | DECLARE_MSG(inh_req, INHIBIT_REQ); 18 | 19 | MODULE("UPOWER"); 20 | 21 | static void init(void) { 22 | M_SUB(UPOWER_REQ); 23 | M_SUB(LID_REQ); 24 | } 25 | 26 | static bool check(void) { 27 | return true; 28 | } 29 | 30 | static bool evaluate(void) { 31 | return !conf.wizard; 32 | } 33 | 34 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 35 | switch (MSG_TYPE()) { 36 | case SYSTEM_UPD: { 37 | if (msg->ps_msg->type == LOOP_STARTED) { 38 | if (upower_check() != 0 || upower_init() != 0) { 39 | /* Upower not available. Let's assume ON_AC and LID OPEN! */ 40 | publish_upower(ON_AC, &upower_msg); 41 | state.ac_state = ON_AC; 42 | 43 | publish_lid(OPEN, &lid_msg); 44 | state.lid_state = OPEN; 45 | 46 | WARN("Failed to retrieve AC state; fallback to ON_AC and OPEN lid.\n"); 47 | module_deregister((self_t **)&self()); 48 | } 49 | } 50 | break; 51 | } 52 | case UPOWER_REQ: { 53 | upower_upd *up = (upower_upd *)MSG_DATA(); 54 | if (VALIDATE_REQ(up)) { 55 | INFO("AC cable %s.\n", up->new == ON_AC ? "connected" : "disconnected"); 56 | // publish upower before storing new ac state as state.ac_state is sent as "old" parameter 57 | publish_upower(up->new, &upower_msg); 58 | state.ac_state = up->new; 59 | } 60 | break; 61 | } 62 | case LID_REQ: { 63 | lid_upd *up = (lid_upd *)MSG_DATA(); 64 | if (VALIDATE_REQ(up)) { 65 | if (up->new != DOCKED) { 66 | INFO("Laptop lid %s.\n", up->new == CLOSED ? "closed" : "opened"); 67 | } else { 68 | INFO("Laptop docked.\n"); 69 | } 70 | // publish lid before storing new lid state as state.lid_closed is sent as "old" parameter 71 | publish_lid(up->new, &lid_msg); 72 | state.lid_state = up->new; 73 | } 74 | break; 75 | } 76 | default: 77 | break; 78 | } 79 | } 80 | 81 | static void destroy(void) { 82 | /* Destroy this match slot */ 83 | if (slot) { 84 | slot = sd_bus_slot_unref(slot); 85 | } 86 | } 87 | 88 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 89 | int r = -EINVAL; 90 | if (!strcmp(member, "Ping")) { 91 | r = 0; 92 | } 93 | return r; 94 | } 95 | 96 | static int upower_check(void) { 97 | SYSBUS_ARG_REPLY(args, parse_bus_reply, NULL, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.DBus.Peer", "Ping"); 98 | int r = call(&args, NULL); 99 | if (r >= 0) { 100 | on_upower_change(NULL, NULL, NULL); 101 | } else { 102 | INFO("UPower not present. Killing UPower module.\n"); 103 | } 104 | return -(r < 0); 105 | } 106 | 107 | static int upower_init(void) { 108 | SYSBUS_ARG(args, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.DBus.Properties", "PropertiesChanged"); 109 | return add_match(&args, &slot, on_upower_change); 110 | } 111 | 112 | /* 113 | * Callback on upower changes: recheck "OnBattery" and "LidIsClosed" boolean values 114 | */ 115 | static int on_upower_change(UNUSED sd_bus_message *m, UNUSED void *userdata, UNUSED sd_bus_error *ret_error) { 116 | SYSBUS_ARG(batt_args, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "OnBattery"); 117 | SYSBUS_ARG(lid_close_args, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", "LidIsClosed"); 118 | /* 119 | * Store last ac_state in old struct to be matched against new one 120 | * as we cannot be sure that a OnBattery changed signal has been really sent: 121 | * our match will receive these signals: 122 | * .DaemonVersion property s "0.99.5" emits-change 123 | * .LidIsClosed property b true emits-change 124 | * .LidIsPresent property b true emits-change 125 | * .OnBattery property b false emits-change 126 | */ 127 | int ac_state; 128 | int r = get_property(&batt_args, "b", &ac_state); 129 | if (!r && state.ac_state != ac_state) { 130 | publish_upower(ac_state, &upower_req); 131 | } 132 | 133 | enum lid_states lid_state; 134 | r = get_property(&lid_close_args, "b", &lid_state); 135 | if (!r && (state.lid_state == -1 || !!state.lid_state != lid_state)) { 136 | if (conf.inh_conf.inhibit_docked) { 137 | 138 | /* 139 | * Only if lid is closed: check for DOCKED state (if inhibit_docked is enabled) 140 | */ 141 | int docked = false; 142 | if (lid_state) { 143 | SYSBUS_ARG(docked_args, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "Docked"); 144 | 145 | r = get_property(&docked_args, "b", &docked); 146 | if (!r) { 147 | lid_state += docked; 148 | if (docked) { 149 | DEBUG("Docked laptop."); 150 | } 151 | } 152 | } 153 | publish_inh(docked); 154 | } 155 | publish_lid(lid_state, &lid_req); 156 | } 157 | return 0; 158 | } 159 | 160 | static void publish_upower(int new, message_t *up) { 161 | up->upower.old = state.ac_state; 162 | up->upower.new = new; 163 | M_PUB(up); 164 | } 165 | 166 | static void publish_lid(bool new, message_t *up) { 167 | up->lid.old = state.lid_state; 168 | up->lid.new = new; 169 | M_PUB(up); 170 | } 171 | 172 | static void publish_inh(bool new) { 173 | inh_req.inhibit.old = state.inhibited; 174 | inh_req.inhibit.new = new; 175 | M_PUB(&inh_req); 176 | } 177 | -------------------------------------------------------------------------------- /src/modules/wizard.c: -------------------------------------------------------------------------------- 1 | #include "bus.h" 2 | #include "my_math.h" 3 | 4 | #define WIZ_IN_POINTS 5 5 | #define WIZ_OUT_POINTS 11 6 | 7 | static void receive_waiting_sens(const msg_t *const msg, UNUSED const void* userdata); 8 | static void receive_capturing(const msg_t *const msg, UNUSED const void* userdata); 9 | static void receive_calibrating(const msg_t *const msg, UNUSED const void* userdata); 10 | static int get_first_available_backlight(void); 11 | static char read_char(int fd); 12 | static void next_step(void); 13 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 14 | static int get_backlight(void); 15 | static void compute(void); 16 | static void expand_regr_points(void); 17 | 18 | DECLARE_MSG(capture_req, CAPTURE_REQ); 19 | 20 | MODULE("WIZARD"); 21 | 22 | typedef enum { 23 | WIZ_START, 24 | WIZ_CAPTURE_NIGHTLIGHT, 25 | WIZ_CALIB_NIGHTLIGHT, 26 | WIZ_CAPTURE_DARK_ROOM, 27 | WIZ_CALIB_DARK_ROOM, 28 | WIZ_CAPTURE_NORMAL_ROOM, 29 | WIZ_CALIB_NORMAL_ROOM, 30 | WIZ_CAPTURE_BRIGHT_ROOM, 31 | WIZ_CALIB_BRIGHT_ROOM, 32 | WIZ_CAPTURE_DAYLIGHT, 33 | WIZ_CALIB_DAYLIGHT, 34 | WIZ_COMPUTE 35 | } wiz_states; 36 | 37 | static wiz_states wiz_st; 38 | static double amb_brs[WIZ_IN_POINTS]; 39 | static int capture_idx; 40 | static curve_t curve; 41 | static const char *bl_obj_path; 42 | 43 | static void init(void) { 44 | curve.num_points = WIZ_IN_POINTS; 45 | 46 | setbuf(stdout, NULL); // disable line buffer 47 | capture_req.capture.reset_timer = false; 48 | capture_req.capture.capture_only = true; 49 | 50 | M_SUB(SENS_UPD); 51 | 52 | INFO("Welcome to Clight wizard. Press ctrl-c to quit at any moment.\n"); 53 | 54 | if (get_first_available_backlight() == 0) { 55 | INFO("Wizard will use '%s' screen.\n", strrchr(bl_obj_path, '/') + 1); 56 | } else { 57 | ERROR("No screen found. Leaving.\n"); 58 | } 59 | 60 | INFO("Waiting for sensor...\n"); 61 | m_become(waiting_sens); 62 | } 63 | 64 | static bool check(void) { 65 | return true; 66 | } 67 | 68 | static bool evaluate() { 69 | return conf.wizard; 70 | } 71 | 72 | static void receive(const msg_t *const msg, UNUSED const void* userdata) { 73 | switch (MSG_TYPE()) { 74 | case FD_UPD: { 75 | char c = read_char(msg->fd_msg->fd); 76 | switch (c) { 77 | case 'y': 78 | case 'Y': 79 | case 10: 80 | INFO("\n"); 81 | next_step(); 82 | break; 83 | default: 84 | modules_quit(0); 85 | break; 86 | } 87 | break; 88 | } 89 | default: 90 | break; 91 | } 92 | } 93 | 94 | static void receive_waiting_sens(const msg_t *const msg, UNUSED const void* userdata) { 95 | switch (MSG_TYPE()) { 96 | case SENS_UPD: { 97 | if (state.sens_avail) { 98 | M_SUB(AMBIENT_BR_UPD); 99 | m_register_fd(STDIN_FILENO, false, NULL); 100 | INFO("Sensor available. Start? [Y/n]: > "); 101 | m_unbecome(); 102 | } else { 103 | INFO("No sensors available. Plug it in and restart wizard.\n"); 104 | modules_quit(EXIT_FAILURE); 105 | } 106 | break; 107 | } 108 | default: 109 | break; 110 | } 111 | } 112 | 113 | static void receive_capturing(const msg_t *const msg, UNUSED const void* userdata) { 114 | switch (MSG_TYPE()) { 115 | case FD_UPD: { 116 | char c = read_char(msg->fd_msg->fd); 117 | switch (c) { 118 | case 'y': 119 | case 'Y': 120 | case 10: 121 | if (!amb_brs[capture_idx] || c != 10) { 122 | M_PUB(&capture_req); 123 | break; 124 | } 125 | default: 126 | INFO("Set desired screen backlight then press ENTER... > "); 127 | next_step(); 128 | break; 129 | } 130 | break; 131 | } 132 | case AMBIENT_BR_UPD: 133 | amb_brs[capture_idx] = state.ambient_br; 134 | INFO("Ambient brightness is currently %.3lf; redo capture? [y/N]: > ", state.ambient_br); 135 | break; 136 | default: 137 | break; 138 | } 139 | } 140 | 141 | static void receive_calibrating(const msg_t *const msg, UNUSED const void* userdata) { 142 | switch (MSG_TYPE()) { 143 | case FD_UPD: { 144 | char c = read_char(msg->fd_msg->fd); 145 | switch (c) { 146 | case 10: 147 | if (get_backlight() != 0) { 148 | WARN("Failed to get backlight pct.\n"); 149 | modules_quit(-1); 150 | } else { 151 | INFO("Backlight level is: %.3lf\n\n", curve.points[capture_idx - 1]); 152 | next_step(); 153 | } 154 | break; 155 | default: 156 | break; 157 | } 158 | break; 159 | } 160 | default: 161 | break; 162 | } 163 | } 164 | 165 | static void destroy(void) { 166 | free((void *)bl_obj_path); 167 | } 168 | 169 | static int get_first_available_backlight(void) { 170 | SYSBUS_ARG_REPLY(args, parse_bus_reply, NULL, CLIGHTD_SERVICE, "/org/clightd/clightd/Backlight2", "org.clightd.clightd.Backlight2", "Get"); 171 | return call(&args, NULL); 172 | } 173 | 174 | static char read_char(int fd) { 175 | char c[10] = {0}; 176 | read(fd, c, 9); 177 | return c[0]; 178 | } 179 | 180 | static void next_step(void) { 181 | wiz_st++; 182 | if (wiz_st < WIZ_COMPUTE) { 183 | if (wiz_st % 2 == 0) { 184 | m_become(calibrating); 185 | } else { 186 | switch (wiz_st) { 187 | case WIZ_CAPTURE_DAYLIGHT: 188 | INFO("Move to daylight-like light.\n"); 189 | break; 190 | case WIZ_CAPTURE_BRIGHT_ROOM: 191 | INFO("Move to a bright room.\n"); 192 | break; 193 | case WIZ_CAPTURE_NORMAL_ROOM: 194 | INFO("Move to normal-light room.\n"); 195 | break; 196 | case WIZ_CAPTURE_DARK_ROOM: 197 | INFO("Move to a dark room.\n"); 198 | break; 199 | case WIZ_CAPTURE_NIGHTLIGHT: 200 | INFO("Move to nightlight-like light.\n"); 201 | break; 202 | default: 203 | break; 204 | } 205 | INFO("Are you ready? [Y/n]\n"); 206 | m_become(capturing); 207 | } 208 | } else { 209 | INFO("Computing new backlight curve...\n"); 210 | compute(); 211 | INFO("Computing new regression points...\n"); 212 | expand_regr_points(); 213 | INFO("Don't forget to set these points in clight conf file!\nBye!\n"); 214 | modules_quit(0); 215 | } 216 | } 217 | 218 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 219 | if (userdata) { 220 | // called by get_backlight() 221 | return sd_bus_message_read(reply, "d", &curve.points[capture_idx++]); 222 | } 223 | // called by get_first_available_backlight() 224 | int r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(sd)"); 225 | if (r == 1) { 226 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_STRUCT, "sd"); 227 | if (r == 1) { 228 | const char *sysname = NULL; 229 | r = sd_bus_message_read(reply, "sd", &sysname, NULL); 230 | if (r >= 0) { 231 | char obj_path[PATH_MAX + 1]; 232 | snprintf(obj_path, sizeof(obj_path), "/org/clightd/clightd/Backlight2/%s", sysname); 233 | bl_obj_path = strdup(obj_path); 234 | } 235 | sd_bus_message_exit_container(reply); 236 | } 237 | sd_bus_message_exit_container(reply); 238 | } 239 | return r; 240 | } 241 | 242 | static int get_backlight(void) { 243 | SYSBUS_ARG_REPLY(args, parse_bus_reply, (void *)bl_obj_path, CLIGHTD_SERVICE, bl_obj_path, "org.clightd.clightd.Backlight2.Server", "Get"); 244 | return call(&args, NULL); 245 | } 246 | 247 | static void compute(void) { 248 | for (int i = 0; i < WIZ_IN_POINTS; i++) { 249 | DEBUG("%.3lf -> %.3lf\n", amb_brs[i], curve.points[i]); 250 | } 251 | polynomialfit(amb_brs, &curve, "Wizard screen backlight"); 252 | } 253 | 254 | static void expand_regr_points(void) { 255 | INFO("[ "); 256 | for (int i = 0; i < WIZ_OUT_POINTS; i++) { 257 | const double perc = (double)i / (WIZ_OUT_POINTS - 1); 258 | const double b = curve.fit_parameters[0] + curve.fit_parameters[1] * perc + curve.fit_parameters[2] * pow(perc, 2); 259 | const double new_br_pct = clamp(b, 1, 0); 260 | INFO("%.3lf%s ", new_br_pct, i < WIZ_OUT_POINTS - 1 ? ", " : " "); 261 | } 262 | INFO("]\n"); 263 | } 264 | -------------------------------------------------------------------------------- /src/public.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** Libmodule wrapping macros, for convenience **/ 9 | 10 | #define CLIGHT_MODULE(name, ...) MODULE(name); \ 11 | static bool check(void) { return true; } \ 12 | static bool evaluate(void) { return true; } \ 13 | static void destroy(void) { } \ 14 | extern int errno /* Declare errno to force a semicolon */ 15 | 16 | /** PubSub Macros **/ 17 | 18 | #define ASSERT_MSG(type); _Static_assert(type >= LOC_UPD && type < MSGS_SIZE, "Wrong MSG type."); 19 | 20 | #define MSG_FLAGS_MASK ((1 << 16) - 1) 21 | #define MSG_FLAG_HEAP (1 << 16) 22 | 23 | #define MSG_TYPE() (msg->is_pubsub ? (msg->ps_msg->type == USER ? ((message_t *)msg->ps_msg->message)->type & MSG_FLAGS_MASK : SYSTEM_UPD) : FD_UPD) 24 | #define MSG_DATA() ((uint8_t *)msg->ps_msg->message + offsetof(message_t, loc)) // offsetof any of the internal data structure to actually account for padding 25 | 26 | // Declare a single, file private, stack allocated msg with name "name" and type "type" 27 | #define DECLARE_MSG(name, type) ASSERT_MSG(type); static message_t name = { type } 28 | 29 | // Declare a unique, AUTOFREED, heap allocated msg with name "name" and type "t". 30 | #define DECLARE_HEAP_MSG(name, t) ASSERT_MSG(t); message_t *name = calloc(1, sizeof(message_t)); *((int *)&name->type) = t | MSG_FLAG_HEAP; 31 | 32 | #define M_PUB(ptr) m_publish(topics[(ptr)->type & MSG_FLAGS_MASK], ptr, sizeof(message_t), (ptr)->type & MSG_FLAG_HEAP); 33 | #define M_SUB(type) ASSERT_MSG(type); m_subscribe(topics[type]); 34 | 35 | /** Log Macros **/ 36 | 37 | #define LOG_DEBUG 'D' 38 | #define LOG_INFO 'I' 39 | #define LOG_WARN 'W' 40 | 41 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 42 | 43 | #define DEBUG(msg, ...) log_message(__FILENAME__, __LINE__, LOG_DEBUG, msg, ##__VA_ARGS__) 44 | #define INFO(msg, ...) log_message(__FILENAME__, __LINE__, LOG_INFO, msg, ##__VA_ARGS__) 45 | #define WARN(msg, ...) log_message(__FILENAME__, __LINE__, LOG_WARN, msg, ##__VA_ARGS__) 46 | 47 | /** Generic Enums **/ 48 | 49 | /* 50 | * List of states clight can be through: 51 | * day between sunrise and sunset 52 | * night between sunset and sunrise 53 | */ 54 | enum day_states { DAY, NIGHT, SIZE_STATES }; 55 | 56 | /* List of events: sunrise and sunset */ 57 | enum day_events { SUNRISE, SUNSET, SIZE_EVENTS }; 58 | 59 | /* Whether laptop is on battery or connected to ac */ 60 | enum ac_states { ON_AC, ON_BATTERY, SIZE_AC }; 61 | 62 | /* Display states */ 63 | enum display_states { DISPLAY_ON, DISPLAY_DIMMED, DISPLAY_OFF, DISPLAY_SIZE }; 64 | 65 | /* Dimming transition states */ 66 | enum dim_trans { ENTER, EXIT, SIZE_DIM }; 67 | 68 | /* Lid states */ 69 | enum lid_states { OPEN, CLOSED, DOCKED, SIZE_LID }; 70 | 71 | /* Type of pubsub messages */ 72 | 73 | /* You should only subscribe on _UPD, and publish on _REQ */ 74 | enum mod_msg_types { 75 | SYSTEM_UPD = -2, // Used internally by Clight 76 | FD_UPD = -1, // Used internally by Clight 77 | LOC_UPD, // Subscribe to receive new locations. 78 | UPOWER_UPD, // Subscribe to receive new AC states 79 | INHIBIT_UPD, // Subscribe to receive new PowerManagement states 80 | DISPLAY_UPD, // Subscribe to receive new display states (on/dimmed/off) 81 | DAYTIME_UPD, // Subscribe to receive new daytime states (day/night) 82 | IN_EVENT_UPD, // Subscribe to receive new InEvent states 83 | SUNRISE_UPD, // Subscribe to receive new Sunrise times 84 | SUNSET_UPD, // Subscribe to receive new Sunset times 85 | TEMP_UPD, // Subscribe to receive new gamma temperatures. NOTE: for smooth changes, a first TEMP_UPD will be emitted with target temp and smooth params, then for each individual step TEMP_UPD will be emitted too, with 0-set smooth params 86 | AMBIENT_BR_UPD, // Subscribe to receive new ambient brightness values 87 | BL_UPD, // Subscribe to receive new backlight level values. NOTE: for smooth changes, a first BL_UPD will be emitted with target backlight and smooth params, then for each individual step BL_UPD will be emitted too, with 0-set smooth params 88 | KBD_BL_UPD, // Subscribe to receive new keyboard backlight values 89 | SCR_BL_UPD, // Subscribe to receive new screen-emitted brightness values 90 | LOCATION_REQ, // Publish to set a new location 91 | UPOWER_REQ, // Publish to set a new UPower state 92 | INHIBIT_REQ, // Publish to set a new PowerManagement state 93 | DISPLAY_REQ, // Publish to set a new Display state 94 | SUNRISE_REQ, // Publish to set a new fixed Sunrise 95 | SUNSET_REQ, // Publish to set a new fixed Sunset 96 | TEMP_REQ, // Publish to set a new gamma temp 97 | BL_REQ, // Publish to set a new backlight level 98 | KBD_BL_REQ, // Publish to set a new keyboard backlight level 99 | DIMMER_TO_REQ, // Publish to set a new dimmer timeout 100 | DPMS_TO_REQ, // Publish to set a new dpms timeout 101 | SCR_TO_REQ, // Publish to set a new screen timeout 102 | BL_TO_REQ, // Publish to set a new backlight timeout 103 | CAPTURE_REQ, // Publish to set a new backlight calibration 104 | CURVE_REQ, // Publish to set a new backlight curve for given ac state 105 | NO_AUTOCALIB_REQ, // Publish to set a new no_autocalib value for BACKLIGHT 106 | CONTRIB_REQ, // Publish to set a new screen-emitted compensation value 107 | SIMULATE_REQ, // Publish to simulate user activity (resetting both dimmer and dpms timeouts) 108 | LID_UPD, // Subscribe to receive new lid states 109 | LID_REQ, // Publish to set a new lid state 110 | PM_UPD, // Subscribe to receive new PowerManagement inhibition states 111 | PM_REQ, // Publish to set a new PowerManagement inhibition state 112 | SENS_UPD, // Subscribe to receive "SensorAvail" states 113 | NEXT_DAYEVT_UPD, // Subscribe to receive notifications about next day event (ie: sunrise or sunset) 114 | SUSPEND_UPD, // Subscribe to receive new system suspended states 115 | SUSPEND_REQ, // Publish to set a new system suspend state (this won't suspend your system! It just 'fakes' a suspended state) 116 | KBD_TO_REQ, // Publish to require a new keyboard timeout 117 | AMB_GAMMA_REQ, // Publish to require a new AmbientGamma state 118 | KBD_CURVE_REQ, // Publish to set a new keyboard backlight curve for given ac state 119 | SCREEN_BR_UPD, // Subscribe to receive new screen brightness values 120 | NO_AUTOCALIB_UPD, // Subscribe to receive no_autocalib updates 121 | MSGS_SIZE 122 | }; 123 | 124 | typedef struct { 125 | double lat; 126 | double lon; 127 | } loc_t; 128 | 129 | /** PubSub Messages **/ 130 | 131 | typedef struct { 132 | loc_t old; // Valued in updates. Useless for requests 133 | loc_t new; // Mandatory for requests. Valued in updates 134 | } loc_upd; 135 | 136 | typedef struct { 137 | enum ac_states old; // Valued in updates. Useless for requests 138 | enum ac_states new; // Mandatory for requests. Valued in updates 139 | } upower_upd; 140 | 141 | typedef struct { 142 | enum lid_states old; // Valued in updates. Useless for requests 143 | enum lid_states new; // Mandatory for requests. Valued in updates 144 | } lid_upd; 145 | 146 | typedef struct { 147 | bool old; // Valued in updates. Useless for requests 148 | bool new; // Mandatory for requests. Valued in updates 149 | bool force; // True to force UnInhibit (ie: set current inhibition counter to 0 and immediately drop inhibit) 150 | } inhibit_upd; 151 | 152 | typedef struct { 153 | bool old; // Valued in updates. Useless for requests 154 | bool new; // Mandatory for requests. Valued in updates 155 | } pm_upd; 156 | 157 | typedef struct { 158 | bool old; // Valued in updates. Useless for requests 159 | bool new; // Mandatory for requests. Valued in updates. True when entering suspend mode. False when resuming. 160 | bool force; // True to forcefully resume (ie: set current suspends counter to 0 and immediately resume) 161 | } suspend_upd; 162 | 163 | typedef struct { 164 | enum display_states old; // Valued in updates 165 | /* 166 | * Use DISPLAY_DIMMED to dim display. 167 | * Use DISPLAY_OFF to enter DPMS state. 168 | * Use DISPLAY_ON to restore normal state (ie: not DIMMED and not DPMS). 169 | */ 170 | enum display_states new; // Mandatory for requests. Valued in updates 171 | bool no_backlight; // True to avoid touching backlight, only managing the new state internally (eg: pausing any module that require a DISPLAY_ON state). Optional for requests. Unused in updates. 172 | } display_upd; 173 | 174 | typedef struct { 175 | enum day_states old; // Valued in updates 176 | enum day_states new; // Valued in updates 177 | } daytime_upd; 178 | 179 | typedef struct { 180 | time_t old; // Valued in updates. Useless for requests 181 | time_t new; // Valued in updates. Useless for requests 182 | char event[10]; // Mandatory for SUNRISE/SUNSET requests. Empty on updates 183 | } evt_upd; 184 | 185 | typedef struct { 186 | enum day_events old; // Valued in updates 187 | enum day_events new; // Valued in updates 188 | } nextdayevt_upd; 189 | 190 | typedef struct { 191 | enum day_states daytime; // Mandatory for requests. Valued in updates. Special value: -1 -> current daytime 192 | int old; // Valued in updates. Useless for requests 193 | int new; // Mandatory for requests. Valued in updates. Special value 0 -> use conf value 194 | int smooth; // Mandatory for requests. Valued in updates. Special value: -1 -> use conf values 195 | int step; // Only useful for requests. Valued in updates 196 | int timeout; // Only useful for requests. Valued in updates 197 | } temp_upd; 198 | 199 | typedef struct { 200 | int new; // Mandatory for requests 201 | enum ac_states state; // Mandatory for requests. Special value: -1 -> use current ac state 202 | enum day_states daytime; // Mandatory for BL_TO_REQ only. Special value: -1 -> use current daytime 203 | } timeout_upd; 204 | 205 | typedef struct { 206 | enum ac_states state; // Mandatory for requests. Special value: -1 -> use current ac state 207 | int num_points; // Mandatory for requests 208 | double *regression_points; // Mandatory for requests 209 | } curve_upd; 210 | 211 | typedef struct { 212 | bool new; // Mandatory for requests 213 | } calib_upd; 214 | 215 | typedef struct { 216 | bool reset_timer; // Mandatory for requests. Whether to reset BACKLIGHT module internal capture timer after the capture 217 | bool capture_only; // Mandatory for requests. Whether clight should update backlight after capture. 218 | } capture_upd; 219 | 220 | typedef struct { 221 | double old; // Valued in updates. Useless for requests 222 | double new; // Mandatory for requests. Valued in updates 223 | int smooth; // Mandatory for BL_REQ requests. Valued in updates. Special values: -1 -> use backlight conf values; -2/-3 -> use dimmer {ENTER, EXIT} conf values 224 | int timeout; // Only useful for BL_REQ requests. Valued in updates 225 | double step; // Only useful for BL_REQ requests. Valued in updates 226 | } bl_upd; 227 | 228 | typedef struct { 229 | double new; // Mandatory for requests 230 | } contrib_upd; 231 | 232 | typedef struct { 233 | bool old; // Valued in updates. No requests available 234 | bool new; // Valued in updates. No requests available 235 | } sens_upd; 236 | 237 | typedef struct { 238 | bool old; // Valued in updates. No requests available 239 | bool new; // Valued in updates. No requests available 240 | } ambgamma_upd; 241 | 242 | typedef struct { 243 | const enum mod_msg_types type; 244 | union { 245 | loc_upd loc; /* LOCATION_UPD/LOCATION_REQ */ 246 | upower_upd upower; /* UPOWER_UPD/UPOWER_REQ */ 247 | lid_upd lid; /* LID_UPD/LID_REQ */ 248 | inhibit_upd inhibit; /* INHIBIT_UPD/INHIBIT_REQ */ 249 | pm_upd pm; /* PM_UPD/PM_REQ */ 250 | suspend_upd suspend; /* SUSPEND_UPD/SUSPEND_REQ */ 251 | display_upd display; /* DISPLAY_UPD/DISPLAY_REQ */ 252 | daytime_upd day_time; /* TIME_UPD/IN_EVENT_UPD */ 253 | evt_upd event; /* SUNRISE_UPD/SUNSET_UPD/SUNRISE_REQ/SUNSET_REQ */ 254 | nextdayevt_upd nextevt; /* NEXT_DAYEVT_UPD */ 255 | temp_upd temp; /* TEMP_UPD/TEMP_REQ */ 256 | timeout_upd to; /* DIMMER_TO_REQ/DPMS_TO_REQ/SCR_TO_REQ/BL_TO_REQ/KBD_TO_REQ */ 257 | curve_upd curve; /* CURVE_REQ/KBD_CURVE_REQ */ 258 | calib_upd nocalib; /* NO_AUTOCALIB_REQ/NO_AUTOCALIB_UPD */ 259 | bl_upd bl; /* AMBIENT_BR_UPD/BL_UPD/KBD_BL_UPD/SCR_BL_UPD/BL_REQ/KBD_BL_REQ/SCREEN_BR_UPD */ 260 | contrib_upd contrib; /* CONTRIB_REQ */ 261 | capture_upd capture; /* CAPTURE_REQ */ 262 | sens_upd sens; /* SENS_UPD */ 263 | ambgamma_upd ambgamma; /* AMB_GAMMA_REQ */ 264 | }; 265 | } message_t; 266 | 267 | /** PubSub Topics **/ 268 | extern const char *topics[]; 269 | 270 | /** Log function declaration **/ 271 | 272 | void log_message(const char *filename, int lineno, const char type, const char *log_msg, ...); 273 | -------------------------------------------------------------------------------- /src/pubsub/topics.c: -------------------------------------------------------------------------------- 1 | #include "public.h" 2 | 3 | const char *topics[] = { 4 | "Location", 5 | "AcState", 6 | "Inhibited", 7 | "DisplayState", 8 | "DayTime", 9 | "InEvent", 10 | "Sunrise", 11 | "Sunset", 12 | "Temp", 13 | "AmbientBr", 14 | "BlPct", 15 | "KbdPct", 16 | "ScreenComp", 17 | "ReqLocation", 18 | "ReqAcState", 19 | "ReqInhibit", 20 | "ReqDisplay", 21 | "ReqSunrise", 22 | "ReqSunset", 23 | "ReqTemp", 24 | "ReqBl", 25 | "ReqKbdBl", 26 | "ReqDimmerTo", 27 | "ReqDpmsTo", 28 | "ReqScrTo", 29 | "ReqBlTo", 30 | "ReqCapture", 31 | "ReqCurve", 32 | "ReqAutocalib", 33 | "ReqContrib", 34 | "ReqSimulate", 35 | "LidState", 36 | "ReqLid", 37 | "PmInhibited", 38 | "ReqPm", 39 | "SensorAvail", 40 | "NextEvent", 41 | "Suspended", 42 | "ReqSuspend", 43 | "ReqKbdTo", 44 | "ReqAmbGamma", 45 | "ReqKbdCurve", 46 | "ScreenBr", 47 | "AutocalibUpd", 48 | }; 49 | _Static_assert(sizeof(topics) / sizeof(*topics) == MSGS_SIZE, "Undefined topic."); 50 | -------------------------------------------------------------------------------- /src/pubsub/validations.c: -------------------------------------------------------------------------------- 1 | #include "validations.h" 2 | #include "my_math.h" 3 | 4 | #define BL_FIXED_STEP 0.01 // Backlight fixed step is 1% backlight level 5 | 6 | bool validate_loc(loc_upd *up) { 7 | if (fabs(up->new.lat) < 90.0f && fabs(up->new.lon) < 180.0f && 8 | get_distance(&up->new, &state.current_loc) >= LOC_DISTANCE_THRS) { 9 | 10 | return true; 11 | } 12 | DEBUG("Failed to validate location request.\n"); 13 | return false; 14 | } 15 | 16 | bool validate_upower(upower_upd *up) { 17 | if (state.ac_state != up->new && up->new >= ON_AC && up->new < SIZE_AC) { 18 | return true; 19 | } 20 | DEBUG("Failed to validate upower request.\n"); 21 | return false; 22 | } 23 | 24 | bool validate_timeout(timeout_upd *up) { 25 | if (up->state == -1) { 26 | up->state = state.ac_state; 27 | } 28 | 29 | if (up->daytime == -1) { 30 | up->daytime = state.day_time; 31 | } 32 | 33 | if (up->state >= ON_AC && up->state < SIZE_AC) { 34 | return true; 35 | } 36 | DEBUG("Failed to validate timeout request.\n"); 37 | return false; 38 | } 39 | 40 | bool validate_inhibit(inhibit_upd *up) { 41 | if (state.inhibited != up->new) { 42 | return true; 43 | } 44 | DEBUG("Failed to validate inhibit request.\n"); 45 | return false; 46 | } 47 | 48 | bool validate_contrib(contrib_upd *up) { 49 | if (up->new >= 0.0f && up->new <= 1.0f && conf.screen_conf.contrib != up->new) { 50 | return true; 51 | } 52 | DEBUG("Failed to validate contrib request.\n"); 53 | return false; 54 | } 55 | 56 | bool validate_evt(evt_upd *up) { 57 | struct tm timeinfo; 58 | // Accept 0-len string to reset event 59 | const size_t len = strlen(up->event); 60 | if (len == 0 || 61 | (len < sizeof(conf.day_conf.day_events[SUNRISE]) && 62 | strptime(up->event, "%R", &timeinfo))) { 63 | 64 | return true; 65 | } 66 | DEBUG("Failed to validate sunrise/sunset request.\n"); 67 | return false; 68 | } 69 | 70 | bool validate_temp(temp_upd *up) { 71 | if (up->daytime == -1) { 72 | up->daytime = state.day_time; 73 | } 74 | 75 | if (up->smooth == -1) { 76 | up->smooth = !conf.gamma_conf.no_smooth; 77 | up->step = conf.gamma_conf.trans_step; 78 | up->timeout = conf.gamma_conf.trans_timeout; 79 | } 80 | 81 | if (up->new == 0) { 82 | up->new = conf.gamma_conf.temp[up->daytime]; 83 | } 84 | 85 | /* Validate new temp: check clighd limits */ 86 | if (up->new >= 1000 && up->new <= 10000 && 87 | up->daytime >= DAY && up->daytime < SIZE_STATES) { 88 | 89 | return true; 90 | } 91 | DEBUG("Failed to validate temperature request.\n"); 92 | return false; 93 | } 94 | 95 | bool validate_autocalib(calib_upd *up) { 96 | if (conf.bl_conf.no_auto_calib != up->new) { 97 | return true; 98 | } 99 | DEBUG("Failed to validate autocalib request.\n"); 100 | return false; 101 | } 102 | 103 | bool validate_curve(curve_upd *up) { 104 | if (up->state == -1) { 105 | up->state = state.ac_state; 106 | } 107 | 108 | if (up->state >= ON_AC && up->state < SIZE_AC && 109 | up->num_points > 0 && up->num_points <= MAX_SIZE_POINTS && 110 | up->regression_points != NULL) { 111 | 112 | for (int i = 0; i < up->num_points; i++) { 113 | if (up->regression_points[i] < 0.0 || up->regression_points[i] > 1.0) { 114 | return false; 115 | } 116 | } 117 | return true; 118 | } 119 | DEBUG("Failed to validate curve request.\n"); 120 | return false; 121 | } 122 | 123 | bool validate_backlight(bl_upd *up) { 124 | bl_smooth_t *smooth = NULL; 125 | switch (up->smooth) { 126 | case -1: 127 | smooth = &conf.bl_conf.smooth; 128 | break; 129 | case -2: 130 | smooth = &conf.dim_conf.smooth[ENTER]; 131 | break; 132 | case -3: 133 | smooth = &conf.dim_conf.smooth[EXIT]; 134 | break; 135 | default: 136 | break; 137 | } 138 | 139 | if (smooth) { 140 | up->smooth = !smooth->no_smooth; 141 | if (smooth->trans_fixed <= 0) { 142 | up->step = smooth->trans_step; 143 | up->timeout = smooth->trans_timeout; 144 | } else { 145 | up->step = BL_FIXED_STEP; 146 | double pct_diff = fabs(state.current_bl_pct - up->new); 147 | double n_steps = pct_diff / up->step; 148 | up->timeout = smooth->trans_fixed / n_steps; 149 | } 150 | } 151 | 152 | if (up->new >= 0.0 && up->new <= 1.0) { 153 | return true; 154 | } 155 | DEBUG("Failed to validate backlight level request.\n"); 156 | return false; 157 | } 158 | 159 | bool validate_display(display_upd *up) { 160 | if (up->new >= DISPLAY_ON && up->new < DISPLAY_SIZE // check up->new validity. 161 | && 162 | (((state.display_state & DISPLAY_OFF) && (up->new == DISPLAY_ON)) // if we are in DPMS, always require a DISPLAY_ON request even if we are DIMMED too. 163 | || 164 | (!(state.display_state & DISPLAY_OFF) && up->new != state.display_state))) { // if we are !DPMS (ie: DIMMED or ON), request any value that is different from current one. 165 | 166 | return true; 167 | } 168 | DEBUG("Failed to validate display state request.\n"); 169 | return false; 170 | } 171 | 172 | bool validate_lid(lid_upd *up) { 173 | if (up->new >= OPEN && 174 | up->new <= DOCKED && 175 | state.lid_state != up->new) { 176 | 177 | return true; 178 | } 179 | DEBUG("Failed to validate lid request.\n"); 180 | return false; 181 | } 182 | 183 | bool validate_pm(pm_upd *up) { 184 | if (up->new != state.pm_inhibited) { 185 | return true; 186 | } 187 | DEBUG("Failed to validate pm request.\n"); 188 | return false; 189 | } 190 | 191 | bool validate_suspend(suspend_upd *up) { 192 | if (up->new != state.suspended) { 193 | return true; 194 | } 195 | DEBUG("Failed to validate suspend request.\n"); 196 | return false; 197 | } 198 | 199 | bool validate_ambgamma(ambgamma_upd *up) { 200 | if (up->new != conf.gamma_conf.ambient_gamma) { 201 | return true; 202 | } 203 | DEBUG("Failed to validate ambgamma request.\n"); 204 | return false; 205 | } 206 | 207 | bool validate_nothing(void *up) { 208 | return true; 209 | } 210 | -------------------------------------------------------------------------------- /src/pubsub/validations.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commons.h" 4 | 5 | #define VALIDATE_REQ(X) _Generic((X), \ 6 | loc_upd *: validate_loc, \ 7 | upower_upd *: validate_upower, \ 8 | inhibit_upd *: validate_inhibit, \ 9 | timeout_upd *: validate_timeout, \ 10 | contrib_upd *: validate_contrib, \ 11 | evt_upd *: validate_evt, \ 12 | temp_upd *: validate_temp, \ 13 | calib_upd *: validate_autocalib, \ 14 | curve_upd *: validate_curve, \ 15 | bl_upd *: validate_backlight, \ 16 | display_upd *: validate_display, \ 17 | lid_upd *: validate_lid, \ 18 | pm_upd *: validate_pm, \ 19 | suspend_upd *: validate_suspend, \ 20 | ambgamma_upd *: validate_ambgamma, \ 21 | default: validate_nothing)(X) 22 | 23 | bool validate_loc(loc_upd *up); 24 | bool validate_upower(upower_upd *up); 25 | bool validate_timeout(timeout_upd *up); 26 | bool validate_inhibit(inhibit_upd *up); 27 | bool validate_contrib(contrib_upd *up); 28 | bool validate_evt(evt_upd *up); 29 | bool validate_temp(temp_upd *up); 30 | bool validate_autocalib(calib_upd *up); 31 | bool validate_curve(curve_upd *up); 32 | bool validate_backlight(bl_upd *up); 33 | bool validate_display(display_upd *up); 34 | bool validate_lid(lid_upd *up); 35 | bool validate_pm(pm_upd *up); 36 | bool validate_suspend(suspend_upd *up); 37 | bool validate_ambgamma(ambgamma_upd *up); 38 | bool validate_nothing(void *up); 39 | -------------------------------------------------------------------------------- /src/utils/idler.c: -------------------------------------------------------------------------------- 1 | #include "idler.h" 2 | #include "utils.h" 3 | 4 | #define VALIDATE_CLIENT(client) do { if (is_string_empty(client)) return -1; } while (0); 5 | 6 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata); 7 | static int idle_get_client(char *client); 8 | static int idle_hook_update(char *client, sd_bus_slot **slot, sd_bus_message_handler_t handler); 9 | 10 | int idle_init(char *client, sd_bus_slot **slot, sd_bus_message_handler_t handler) { 11 | int r = idle_get_client(client); 12 | if (r < 0) { 13 | goto end; 14 | } 15 | r = idle_hook_update(client, slot, handler); 16 | 17 | end: 18 | if (r < 0) { 19 | WARN("Clightd idle error.\n"); 20 | *client = '\0'; // reset client making it useless 21 | } 22 | return -(r < 0); // - 1 on error 23 | } 24 | 25 | static int parse_bus_reply(sd_bus_message *reply, const char *member, void *userdata) { 26 | int r = -EINVAL; 27 | if (!strcmp(member, "GetClient")) { 28 | const char *cl; 29 | r = sd_bus_message_read(reply, "o", &cl); 30 | if (r >= 0 && cl) { 31 | strncpy((char *)userdata, cl, PATH_MAX); 32 | } 33 | } 34 | return r; 35 | } 36 | 37 | static int idle_get_client(char *client) { 38 | SYSBUS_ARG_REPLY(args, parse_bus_reply, client, CLIGHTD_SERVICE, "/org/clightd/clightd/Idle", "org.clightd.clightd.Idle", "GetClient"); 39 | return call(&args, NULL); 40 | } 41 | 42 | static int idle_hook_update(char *client, sd_bus_slot **slot, sd_bus_message_handler_t handler) { 43 | SYSBUS_ARG(args, CLIGHTD_SERVICE, client, "org.clightd.clightd.Idle.Client", "Idle"); 44 | return add_match(&args, slot, handler); 45 | } 46 | 47 | int idle_set_timeout(char *client, int timeout) { 48 | VALIDATE_CLIENT(client); 49 | 50 | int r = 0; 51 | if (timeout > 0) { 52 | SYSBUS_ARG(to_args, CLIGHTD_SERVICE, client, "org.clightd.clightd.Idle.Client", "Timeout"); 53 | r = set_property(&to_args, "u", timeout); 54 | if (!state.inhibited) { 55 | /* Only start client if we are not inhibited */ 56 | r += idle_client_start(client, timeout); 57 | } 58 | } else { 59 | r = idle_client_stop(client); 60 | } 61 | return r; 62 | } 63 | 64 | int idle_client_start(char *client, int timeout) { 65 | VALIDATE_CLIENT(client); 66 | 67 | if (timeout > 0) { 68 | SYSBUS_ARG(args, CLIGHTD_SERVICE, client, "org.clightd.clightd.Idle.Client", "Start"); 69 | return call(&args, NULL); 70 | } 71 | return 0; 72 | } 73 | 74 | int idle_client_stop(char *client) { 75 | VALIDATE_CLIENT(client); 76 | 77 | SYSBUS_ARG(args, CLIGHTD_SERVICE, client, "org.clightd.clightd.Idle.Client", "Stop"); 78 | return call(&args, NULL); 79 | } 80 | 81 | int idle_client_reset(char *client, int timeout) { 82 | return idle_client_stop(client) + idle_client_start(client, timeout); 83 | } 84 | 85 | int idle_client_destroy(char *client) { 86 | VALIDATE_CLIENT(client); 87 | 88 | /* Properly stop client */ 89 | idle_client_stop(client); 90 | 91 | SYSBUS_ARG(args, CLIGHTD_SERVICE, "/org/clightd/clightd/Idle", "org.clightd.clightd.Idle", "DestroyClient"); 92 | return call(&args, "o", client); 93 | } 94 | -------------------------------------------------------------------------------- /src/utils/idler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "interface.h" 4 | 5 | int idle_init(char *client, sd_bus_slot **slot, sd_bus_message_handler_t handler); 6 | int idle_set_timeout(char *client, int timeout); 7 | int idle_client_start(char *client, int timeout); 8 | int idle_client_stop(char *client); 9 | int idle_client_reset(char *client, int timeout); 10 | int idle_client_destroy(char *client); 11 | -------------------------------------------------------------------------------- /src/utils/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "utils.h" 4 | 5 | static void log_bl_smooth(bl_smooth_t *smooth, const char *prefix); 6 | static void log_bl_conf(bl_conf_t *bl_conf); 7 | static void log_sens_conf(sensor_conf_t *sens_conf); 8 | static void log_kbd_conf(kbd_conf_t *kbd_conf); 9 | static void log_gamma_conf(gamma_conf_t *gamma_conf); 10 | static void log_daytime_conf(daytime_conf_t *day_conf); 11 | static void log_dim_conf(dimmer_conf_t *dim_conf); 12 | static void log_dpms_conf(dpms_conf_t *dpms_conf); 13 | static void log_scr_conf(screen_conf_t *screen_conf); 14 | static void log_inh_conf(inh_conf_t *inh_conf); 15 | 16 | static FILE *log_file; 17 | 18 | void open_log(void) { 19 | char log_path[PATH_MAX + 1] = {0}; 20 | 21 | if (getenv("XDG_RUNTIME_DIR")) { 22 | snprintf(log_path, PATH_MAX, "%s/clight/", getenv("XDG_RUNTIME_DIR")); 23 | } else if (getenv("XDG_DATA_HOME")) { 24 | snprintf(log_path, PATH_MAX, "%s/clight/", getenv("XDG_DATA_HOME")); 25 | } else { 26 | snprintf(log_path, PATH_MAX, "%s/.local/share/clight/", getpwuid(getuid())->pw_dir); 27 | } 28 | 29 | /* Create log folder if it does not exist! */ 30 | mkdir(log_path, 0755); 31 | 32 | strcat(log_path, "clight.log"); 33 | int fd = open(log_path, O_CREAT | O_WRONLY, 0644); 34 | if (flock(fd, LOCK_EX | LOCK_NB) == -1) { 35 | WARN("%s\n", strerror(errno)); 36 | ERROR("A lock is present on %s. Another clight instance running?\n", log_path); 37 | } 38 | 39 | log_file = fdopen(fd, "w"); 40 | if (!log_file) { 41 | ERROR("%s\n", strerror(errno)); 42 | } 43 | 44 | ftruncate(fd, 0); 45 | } 46 | 47 | static void log_bl_smooth(bl_smooth_t *smooth, const char *prefix) { 48 | fprintf(log_file, "* %sSmooth trans:\t\t%s\n", prefix, smooth->no_smooth ? "Disabled" : "Enabled"); 49 | fprintf(log_file, "* %sSmooth step:\t\t%.2lf\n", prefix, smooth->trans_step); 50 | fprintf(log_file, "* %sSmooth timeout:\t\t%d\n", prefix, smooth->trans_timeout); 51 | fprintf(log_file, "* %sSmooth fixed:\t\t%d\n", prefix, smooth->trans_fixed); 52 | } 53 | 54 | static void log_bl_conf(bl_conf_t *bl_conf) { 55 | fprintf(log_file, "\n### BACKLIGHT ###\n"); 56 | log_bl_smooth(&conf.bl_conf.smooth, ""); 57 | fprintf(log_file, "* Daily timeouts:\t\tAC %d\tBATT %d\n", bl_conf->timeout[ON_AC][DAY], bl_conf->timeout[ON_BATTERY][DAY]); 58 | fprintf(log_file, "* Nightly timeouts:\t\tAC %d\tBATT %d\n", bl_conf->timeout[ON_AC][NIGHT], bl_conf->timeout[ON_BATTERY][NIGHT]); 59 | fprintf(log_file, "* Event timeouts:\t\tAC %d\tBATT %d\n", bl_conf->timeout[ON_AC][SIZE_STATES], bl_conf->timeout[ON_BATTERY][SIZE_STATES]); 60 | fprintf(log_file, "* Shutter threshold:\t\t%.2lf\n", bl_conf->shutter_threshold); 61 | fprintf(log_file, "* Autocalibration:\t\t%s\n", bl_conf->no_auto_calib ? "Disabled" : "Enabled"); 62 | fprintf(log_file, "* Pause on lid closed:\t\t%s\n", bl_conf->pause_on_lid_closed ? "Enabled" : "Disabled"); 63 | fprintf(log_file, "* Capture on lid opened:\t\t%s\n", bl_conf->capture_on_lid_opened ? "Enabled" : "Disabled"); 64 | fprintf(log_file, "* Restore On Exit:\t\t%s\n", bl_conf->restore ? "Enabled" : "Disabled"); 65 | fprintf(log_file, "* Delay on hotplug:\t\t%d\n", bl_conf->sync_monitors_delay); 66 | } 67 | 68 | static void log_sens_conf(sensor_conf_t *sens_conf) { 69 | fprintf(log_file, "\n### SENSOR ###\n"); 70 | fprintf(log_file, "* Captures:\t\tAC %d\tBATT %d\n", sens_conf->num_captures[ON_AC], sens_conf->num_captures[ON_BATTERY]); 71 | fprintf(log_file, "* Device:\t\t%s\n", sens_conf->dev_name ? sens_conf->dev_name : "Unset"); 72 | fprintf(log_file, "* Settings:\t\t%s\n", sens_conf->dev_opts ? sens_conf->dev_opts : "Unset"); 73 | } 74 | 75 | static void log_kbd_conf(kbd_conf_t *kbd_conf) { 76 | fprintf(log_file, "\n### KEYBOARD ###\n"); 77 | fprintf(log_file, "* Timeouts:\t\tAC %d\tBATT %d\n", kbd_conf->timeout[ON_AC], kbd_conf->timeout[ON_BATTERY]); 78 | } 79 | 80 | static void log_gamma_conf(gamma_conf_t *gamma_conf) { 81 | fprintf(log_file, "\n### GAMMA ###\n"); 82 | fprintf(log_file, "* Smooth trans:\t\t%s\n", gamma_conf->no_smooth ? "Disabled" : "Enabled"); 83 | fprintf(log_file, "* Smooth steps:\t\t%d\n", gamma_conf->trans_step); 84 | fprintf(log_file, "* Smooth timeout:\t\t%d\n", gamma_conf->trans_timeout); 85 | fprintf(log_file, "* Daily screen temp:\t\t%d\n", gamma_conf->temp[DAY]); 86 | fprintf(log_file, "* Nightly screen temp:\t\t%d\n", gamma_conf->temp[NIGHT]); 87 | fprintf(log_file, "* Long transition:\t\t%s\n", gamma_conf->long_transition ? "Enabled" : "Disabled"); 88 | fprintf(log_file, "* Ambient gamma:\t\t%s\n", gamma_conf->ambient_gamma ? "Enabled" : "Disabled"); 89 | fprintf(log_file, "* Restore On Exit:\t\t%s\n", gamma_conf->restore ? "Enabled" : "Disabled"); 90 | } 91 | 92 | static void log_daytime_conf(daytime_conf_t *day_conf) { 93 | fprintf(log_file, "\n### DAYTIME ###\n"); 94 | if (day_conf->loc.lat != LAT_UNDEFINED && day_conf->loc.lon != LON_UNDEFINED) { 95 | fprintf(log_file, "* User position:\t\t%.2lf\t%.2lf\n", day_conf->loc.lat, day_conf->loc.lon); 96 | } else { 97 | fprintf(log_file, "* User position:\t\tUnset\n"); 98 | } 99 | fprintf(log_file, "* User set sunrise:\t\t%s\n", is_string_empty(day_conf->day_events[SUNRISE]) ? "Unset" : day_conf->day_events[SUNRISE]); 100 | fprintf(log_file, "* User set sunset:\t\t%s\n", is_string_empty(day_conf->day_events[SUNSET]) ? "Unset" : day_conf->day_events[SUNSET]); 101 | fprintf(log_file, "* Event duration:\t\t%d\n", day_conf->event_duration); 102 | fprintf(log_file, "* Sunrise offset:\t\t%d\n", day_conf->events_os[SUNRISE]); 103 | fprintf(log_file, "* Sunset offset:\t\t%d\n", day_conf->events_os[SUNSET]); 104 | } 105 | 106 | static void log_dim_conf(dimmer_conf_t *dim_conf) { 107 | fprintf(log_file, "\n### DIMMER ###\n"); 108 | log_bl_smooth(&conf.dim_conf.smooth[ENTER], "ENTER "); 109 | log_bl_smooth(&conf.dim_conf.smooth[EXIT], "EXIT "); 110 | fprintf(log_file, "* Timeouts:\t\tAC %d\tBATT %d\n", dim_conf->timeout[ON_AC], dim_conf->timeout[ON_BATTERY]); 111 | fprintf(log_file, "* Backlight pct:\t\t%.2lf\n", dim_conf->dimmed_pct); 112 | } 113 | 114 | static void log_dpms_conf(dpms_conf_t *dpms_conf) { 115 | fprintf(log_file, "\n### DPMS ###\n"); 116 | fprintf(log_file, "* Timeouts:\t\tAC %d\tBATT %d\n", dpms_conf->timeout[ON_AC], dpms_conf->timeout[ON_BATTERY]); 117 | } 118 | 119 | static void log_scr_conf(screen_conf_t *screen_conf) { 120 | fprintf(log_file, "\n### SCREEN ###\n"); 121 | fprintf(log_file, "* Timeouts:\t\tAC %d\tBATT %d\n", screen_conf->timeout[ON_AC], screen_conf->timeout[ON_BATTERY]); 122 | } 123 | 124 | static void log_inh_conf(inh_conf_t *inh_conf) { 125 | fprintf(log_file, "\n### INHIBIT ###\n"); 126 | fprintf(log_file, "* Docked:\t\t%s\n", inh_conf->inhibit_docked ? "Enabled" : "Disabled"); 127 | fprintf(log_file, "* PowerManagement:\t\t%s\n", inh_conf->inhibit_pm ? "Enabled" : "Disabled"); 128 | fprintf(log_file, "* Backlight:\t\t%s\n", inh_conf->inhibit_bl ? "Enabled" : "Disabled"); 129 | } 130 | 131 | void log_conf(void) { 132 | if (log_file) { 133 | time_t t = time(NULL); 134 | 135 | /* Start with a newline if any log is above */ 136 | fprintf(log_file, "%sClight\n", ftell(log_file) != 0 ? "\n" : ""); 137 | fprintf(log_file, "* Software version:\t\t%s\n", VERSION); 138 | fprintf(log_file, "* Global config dir:\t\t%s\n", CONFDIR); 139 | fprintf(log_file, "* Global data dir:\t\t%s\n", DATADIR); 140 | fprintf(log_file, "* Starting time:\t\t%s\n", ctime(&t)); 141 | 142 | fprintf(log_file, "Starting options:\n"); 143 | 144 | fprintf(log_file, "\n### GENERIC ###\n"); 145 | fprintf(log_file, "* Verbose (debug):\t\t%s\n", conf.verbose ? "Enabled" : "Disabled"); 146 | fprintf(log_file, "* ResumeDelay:\t\t%d\n", conf.resumedelay); 147 | 148 | if (!conf.bl_conf.disabled) { 149 | log_bl_conf(&conf.bl_conf); 150 | log_sens_conf(&conf.sens_conf); 151 | } 152 | 153 | if (!conf.kbd_conf.disabled) { 154 | log_kbd_conf(&conf.kbd_conf); 155 | } 156 | 157 | if (!conf.gamma_conf.disabled) { 158 | log_gamma_conf(&conf.gamma_conf); 159 | } 160 | 161 | log_daytime_conf(&conf.day_conf); 162 | 163 | if (!conf.dim_conf.disabled) { 164 | log_dim_conf(&conf.dim_conf); 165 | } 166 | 167 | if (!conf.dpms_conf.disabled) { 168 | log_dpms_conf(&conf.dpms_conf); 169 | } 170 | 171 | if (!conf.screen_conf.disabled) { 172 | log_scr_conf(&conf.screen_conf); 173 | } 174 | 175 | if (!conf.inh_conf.disabled) { 176 | log_inh_conf(&conf.inh_conf); 177 | } 178 | 179 | fprintf(log_file, "\n"); 180 | fflush(log_file); 181 | } 182 | } 183 | 184 | void log_message(const char *filename, int lineno, const char type, const char *log_msg, ...) { 185 | // Debug and plot message only in verbose mode 186 | if ((type != LOG_DEBUG && type != LOG_PLOT) || conf.verbose) { 187 | va_list file_args, args; 188 | 189 | va_start(file_args, log_msg); 190 | va_copy(args, file_args); 191 | 192 | if (log_file) { 193 | if (type != LOG_PLOT) { 194 | time_t t = time(NULL); 195 | struct tm *tm = localtime(&t); 196 | fprintf(log_file, "(%c)[%02d:%02d:%02d]{%s:%d}\t", type, tm->tm_hour, tm->tm_min, tm->tm_sec, filename, lineno); 197 | } 198 | vfprintf(log_file, log_msg, file_args); 199 | fflush(log_file); 200 | } 201 | 202 | /* In case of error, log to stdout too */ 203 | FILE *out = stdout; 204 | if (type == LOG_ERR) { 205 | out = stderr; 206 | } 207 | 208 | vfprintf(out, log_msg, args); 209 | va_end(args); 210 | va_end(file_args); 211 | } 212 | } 213 | 214 | void close_log(void) { 215 | if (log_file) { 216 | flock(fileno(log_file), LOCK_UN); 217 | fclose(log_file); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/utils/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* Internal; not exposed through public API */ 6 | #define LOG_ERR 'E' 7 | #define LOG_PLOT 'P' 8 | 9 | /* ERROR macro will leave clight by calling modules_quit; thus it is not exposed to public header */ 10 | #define ERROR(msg, ...) \ 11 | do { \ 12 | log_message(__FILENAME__, __LINE__, LOG_ERR, msg, ##__VA_ARGS__); \ 13 | if (state.looping) modules_quit(EXIT_FAILURE); \ 14 | else longjmp(state.quit_buf, EXIT_FAILURE); \ 15 | } while (0) 16 | 17 | /* Used to plot backlight curves to log without headers */ 18 | #define PLOT(msg, ...) log_message(__FILENAME__, __LINE__, LOG_PLOT, msg, ##__VA_ARGS__) 19 | 20 | void open_log(void); 21 | void log_conf(void); 22 | void close_log(void); 23 | -------------------------------------------------------------------------------- /src/utils/my_math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "my_math.h" 4 | #include "utils.h" 5 | 6 | #define ZENITH -0.83 7 | 8 | static void plot_poly_curve(curve_t *curve); 9 | static char **init_grid(int num_points); 10 | static void show_grid(char **grid, int num_points); 11 | static void free_grid(char **grid, int num_points); 12 | static float to_hours(const float rad); 13 | static int calculate_sunrise_sunset(const float lat, const float lng, time_t *tt, enum day_events event, int dayshift); 14 | 15 | /* 16 | * Convert degrees to radians 17 | */ 18 | double degToRad(double angleDeg) { 19 | return (M_PI * angleDeg / 180.0); 20 | } 21 | 22 | /* 23 | * Convert radians to degrees 24 | */ 25 | double radToDeg(double angleRad) { 26 | return (180.0 * angleRad / M_PI); 27 | } 28 | 29 | /* 30 | * Compute mean and normalize between 0-1 31 | */ 32 | double compute_average(const double *intensity, int num) { 33 | double mean = gsl_stats_mean(intensity, 1, num); 34 | return mean; 35 | } 36 | 37 | /* 38 | * Big thanks to https://rosettacode.org/wiki/Polynomial_regression#C 39 | */ 40 | void polynomialfit(double *XPoints, curve_t *curve, const char *tag) { 41 | double chisq; 42 | 43 | gsl_matrix *X = gsl_matrix_alloc(curve->num_points, DEGREE); 44 | gsl_vector *y = gsl_vector_alloc(curve->num_points); 45 | gsl_vector *c = gsl_vector_alloc(DEGREE); 46 | gsl_matrix *cov = gsl_matrix_alloc(DEGREE, DEGREE); 47 | 48 | for (int i = 0; i < curve->num_points; i++) { 49 | for (int j = 0; j < DEGREE; j++) { 50 | if (XPoints) { 51 | gsl_matrix_set(X, i, j, pow(XPoints[i], j)); 52 | } else { 53 | gsl_matrix_set(X, i, j, pow(i, j)); 54 | } 55 | } 56 | gsl_vector_set(y, i, curve->points[i]); 57 | } 58 | 59 | gsl_multifit_linear_workspace *ws = gsl_multifit_linear_alloc(curve->num_points, DEGREE); 60 | gsl_multifit_linear(X, y, c, cov, &chisq, ws); 61 | 62 | /* store results */ 63 | for(int i = 0; i < DEGREE; i++) { 64 | curve->fit_parameters[i] = gsl_vector_get(c, i); 65 | } 66 | 67 | gsl_multifit_linear_free(ws); 68 | gsl_matrix_free(X); 69 | gsl_matrix_free(cov); 70 | gsl_vector_free(y); 71 | gsl_vector_free(c); 72 | 73 | DEBUG("%s curve: y = %lf + %lfx + %lfx^2\n", tag, curve->fit_parameters[0], curve->fit_parameters[1], curve->fit_parameters[2]); 74 | plot_poly_curve(curve); 75 | } 76 | 77 | double clamp(double value, double max, double min) { 78 | if (value > max) { 79 | return max; 80 | } 81 | if (value < min) { 82 | return min; 83 | } 84 | return value; 85 | } 86 | 87 | double get_value_from_curve(const double perc, curve_t *curve) { 88 | // Keyboard backlight curves are upside down 89 | const double max = curve->points[curve->num_points - 1] > curve->points[0] ? curve->points[curve->num_points - 1] : curve->points[0]; 90 | const double min = curve->points[0] < curve->points[curve->num_points - 1] ? curve->points[0] : curve->points[curve->num_points - 1]; 91 | 92 | /* y = a0 + a1x + a2x^2 */ 93 | const double real_perc = perc * (curve->num_points - 1); 94 | const double b = curve->fit_parameters[0] 95 | + curve->fit_parameters[1] * real_perc 96 | + curve->fit_parameters[2] * pow(real_perc, 2); 97 | const double value = clamp(b, max, min); 98 | return value; 99 | } 100 | 101 | static void plot_poly_curve(curve_t *curve) { 102 | const int maxY = curve->num_points - 1; 103 | char **grid = init_grid(curve->num_points); 104 | for (int i = 0; i < curve->num_points; i++) { 105 | const double val = get_value_from_curve((double)i / (curve->num_points - 1), curve); 106 | const int y = round(val * (curve->num_points - 1)); 107 | grid[maxY - y][i] = '*'; 108 | } 109 | show_grid(grid, curve->num_points); 110 | free_grid(grid, curve->num_points); 111 | } 112 | 113 | static char **init_grid(int num_points) { 114 | char **grid = calloc(num_points, sizeof(char *)); 115 | for (int i = 0; i < num_points; i++) { 116 | grid[i] = calloc(num_points, sizeof(char)); 117 | } 118 | 119 | for (int y = 0; y < num_points; y++) { 120 | for (int x = 0; x < num_points; x++) { 121 | grid[y][x] = ' '; 122 | } 123 | } 124 | /* draw the axis */ 125 | for(int y = 0; y < num_points; y++) { 126 | grid[y][0] = '|'; 127 | } 128 | for(int x = 0; x < num_points; x++) { 129 | grid[num_points - 1][x] = '-'; 130 | } 131 | grid[num_points - 1][0] = '+'; 132 | return grid; 133 | } 134 | 135 | static void show_grid(char **grid, int num_points) { 136 | PLOT("BL\n^\n"); 137 | for(int y = 0; y < num_points; y++) { 138 | for(int x = 0; x < num_points; x++) { 139 | PLOT("%c", grid[y][x]); 140 | } 141 | 142 | if (y != num_points - 1) { 143 | PLOT("\n"); 144 | } 145 | } 146 | PLOT(">BR\n"); 147 | } 148 | 149 | static void free_grid(char **grid, int num_points) { 150 | for (int i = 0; i < num_points; i++) { 151 | free(grid[i]); 152 | } 153 | free(grid); 154 | } 155 | 156 | static float to_hours(const float rad) { 157 | return rad / 15.0;// 360 degree / 24 hours = 15 degrees/h 158 | } 159 | 160 | /* 161 | * Just a small function to compute sunset/sunrise for today (or tomorrow). 162 | * See: http://stackoverflow.com/questions/7064531/sunrise-sunset-times-in-c 163 | * If conf.events[event] is set, it means "event" time is user-set. 164 | * So, only store in *tt its corresponding time_t values. 165 | */ 166 | static int calculate_sunrise_sunset(const float lat, const float lng, time_t *tt, enum day_events event, int dayshift) { 167 | // 1. compute the day of the year (timeinfo->tm_yday below) 168 | time(tt); 169 | struct tm *timeinfo = localtime(tt); 170 | if (!timeinfo) { 171 | return -1; 172 | } 173 | // if needed, set dayshift 174 | timeinfo->tm_yday += dayshift; 175 | timeinfo->tm_mday += dayshift; 176 | timeinfo->tm_sec = 0; 177 | 178 | /* If user provided a sunrise/sunset time, use them */ 179 | if (!is_string_empty(conf.day_conf.day_events[event])) { 180 | strptime(conf.day_conf.day_events[event], "%R", timeinfo); 181 | *tt = mktime(timeinfo); 182 | return 0; 183 | } 184 | 185 | // 2. convert the longitude to hour value and calculate an approximate time 186 | float lngHour = to_hours(lng); 187 | float t; 188 | if (event == SUNRISE) { 189 | t = timeinfo->tm_yday + (6.0 - lngHour) / 24.0; 190 | } else { 191 | t = timeinfo->tm_yday + (18.0 - lngHour) / 24.0; 192 | } 193 | 194 | // 3. calculate the Sun's mean anomaly 195 | float M = (0.9856 * t) - 3.289; 196 | 197 | // 4. calculate the Sun's true longitude 198 | float L = fmod(M + 1.916 * sin(degToRad(M)) + 0.020 * sin(2 * degToRad(M)) + 282.634, 360.0); 199 | 200 | // 5a. calculate the Sun's right ascension 201 | float RA = fmod(radToDeg(atan(0.91764 * tan(degToRad(L)))), 360.0); 202 | 203 | // 5b. right ascension value needs to be in the same quadrant as L 204 | float Lquadrant = floor(L / 90) * 90; 205 | float RAquadrant = floor(RA / 90) * 90; 206 | RA += (Lquadrant - RAquadrant); 207 | 208 | // 5c. right ascension value needs to be converted into hours 209 | RA = to_hours(RA); 210 | 211 | // 6. calculate the Sun's declination 212 | float sinDec = 0.39782 * sin(degToRad(L)); 213 | float cosDec = cos(asin(sinDec)); 214 | 215 | // 7a. calculate the Sun's local hour angle 216 | float cosH = sin(degToRad(ZENITH)) - (sinDec * sin(degToRad(lat))) / (cosDec * cos(degToRad(lat))); 217 | if ((cosH > 1 && event == SUNRISE) || (cosH < -1 && event == SUNSET)) { 218 | return -2; // no sunrise/sunset today! 219 | } 220 | 221 | // 7b. finish calculating H and convert into hours 222 | float H; 223 | if (event == SUNRISE) { 224 | H = 360.0 - radToDeg(acos(cosH)); 225 | } else { 226 | H = radToDeg(acos(cosH)); 227 | } 228 | H = to_hours(H); 229 | 230 | // 8. calculate local mean time of rising/setting 231 | float T = H + RA - (0.06571 * t) - 6.622; 232 | 233 | // 9. adjust back to UTC 234 | float UT = fmod(24 + fmod(T - lngHour, 24.0), 24.0); 235 | 236 | double hours; 237 | double minutes = modf(UT, &hours) * 60; 238 | 239 | // set correct values 240 | timeinfo->tm_hour = hours; 241 | timeinfo->tm_min = minutes; 242 | 243 | // store in user provided ptr correct data 244 | *tt = timegm(timeinfo); 245 | if (*tt == (time_t) -1) { 246 | return -1; 247 | } 248 | return 0; 249 | } 250 | 251 | int calculate_sunrise(const float lat, const float lng, time_t *tt, int dayshift) { 252 | return calculate_sunrise_sunset(lat, lng, tt, SUNRISE, dayshift); 253 | } 254 | 255 | int calculate_sunset(const float lat, const float lng, time_t *tt, int dayshift) { 256 | return calculate_sunrise_sunset(lat, lng, tt, SUNSET, dayshift); 257 | } 258 | 259 | /* 260 | * Get distance between 2 locations 261 | */ 262 | double get_distance(loc_t *loc1, loc_t *loc2) { 263 | double theta = loc1->lon - loc2->lon; 264 | double dist = sin(degToRad(loc1->lat)) * sin(degToRad(loc2->lat)) + cos(degToRad(loc1->lat)) * cos(degToRad(loc2->lat)) * cos(degToRad(theta)); 265 | dist = acos(dist); 266 | dist = radToDeg(dist); 267 | dist *= 60 * 1.1515; 268 | DEBUG("Loc distance: %.2lf,%.2lf -> %.2lf,%.2lf : %.2lf km.\n", loc1->lat, loc1->lon, loc2->lat, loc2->lon, dist); 269 | return dist; 270 | } 271 | -------------------------------------------------------------------------------- /src/utils/my_math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commons.h" 4 | 5 | double degToRad(double angleDeg); 6 | double radToDeg(double angleRad); 7 | double compute_average(const double *intensity, int num); 8 | void polynomialfit(double *XPoints, curve_t *curve, const char *tag); 9 | double clamp(double value, double max, double min); 10 | double get_value_from_curve(const double perc, curve_t *curve); 11 | int calculate_sunrise(const float lat, const float lng, time_t *tt, int dayshift); 12 | int calculate_sunset(const float lat, const float lng, time_t *tt, int dayshift); 13 | double get_distance(loc_t *loc1, loc_t *loc2); 14 | -------------------------------------------------------------------------------- /src/utils/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "timer.h" 3 | 4 | static time_t get_timeout_sec(int fd); 5 | 6 | int start_timer(int clockid, int initial_s, int initial_ns) { 7 | int timerfd = timerfd_create(clockid, TFD_NONBLOCK); 8 | if (timerfd == -1) { 9 | ERROR("timerfd_create() failed: %s\n", strerror(errno)); 10 | } 11 | set_timeout(initial_s, initial_ns, timerfd, 0); 12 | return timerfd; 13 | } 14 | 15 | /* 16 | * Helper to set a new trigger on timerfd in sec seconds and nsec nanoseconds 17 | */ 18 | void set_timeout(int sec, int nsec, int fd, int flag) { 19 | struct itimerspec timerValue = {{0}}; 20 | 21 | if (sec < 0) { 22 | sec = 0; 23 | } 24 | timerValue.it_value.tv_sec = sec; 25 | timerValue.it_value.tv_nsec = nsec; 26 | int r = timerfd_settime(fd, flag, &timerValue, NULL); 27 | if (r == -1) { 28 | ERROR("timerfd_settime(%d) failed: %s\n", fd, strerror(errno)); 29 | } 30 | if (flag == 0) { 31 | if (sec != 0 || nsec != 0) { 32 | DEBUG("Set timeout of %ds %dns on fd %d.\n", sec, nsec, fd); 33 | } else { 34 | DEBUG("Disarmed timerfd on fd %d.\n", fd); 35 | } 36 | } 37 | } 38 | 39 | static time_t get_timeout_sec(int fd) { 40 | struct itimerspec curr_value; 41 | if (timerfd_gettime(fd, &curr_value) == 0) { 42 | return curr_value.it_value.tv_sec; 43 | } 44 | WARN("timerfd_gettime(%d) failed: %s\n", fd, strerror(errno)); 45 | return 0; 46 | } 47 | 48 | void reset_timer(int fd, int old_timer, int new_timer) { 49 | if (old_timer <= 0) { 50 | /* We had a paused fd; resume it */ 51 | set_timeout(new_timer, 0, fd, 0); 52 | } else { 53 | time_t fd_timeout = get_timeout_sec(fd); 54 | if (fd_timeout == 0 && new_timer > 0) { 55 | /* 56 | * fd was ready to fire; we are not pausing it 57 | * thus we can safely let it fire. 58 | * Note: this happens after eg: a long night suspend 59 | */ 60 | return; 61 | } 62 | 63 | unsigned int elapsed_time = old_timer - fd_timeout; 64 | /* if we still need to wait some seconds */ 65 | if (new_timer > elapsed_time) { 66 | set_timeout(new_timer - elapsed_time, 0, fd, 0); 67 | } else if (new_timer > 0) { 68 | /* with new timeout, old_timeout would already have elapsed */ 69 | set_timeout(0, 1, fd, 0); 70 | } else { 71 | /* pause fd as a timeout <= 0 has been set */ 72 | set_timeout(0, 0, fd, 0); 73 | } 74 | } 75 | } 76 | 77 | void read_timer(int fd) { 78 | uint64_t t; 79 | read(fd, &t, sizeof(uint64_t)); 80 | } 81 | -------------------------------------------------------------------------------- /src/utils/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commons.h" 4 | 5 | int start_timer(int clockid, int initial_s, int initial_ns); 6 | void set_timeout(int sec, int nsec, int fd, int flag); 7 | void reset_timer(int fd, int old_timer, int new_timer); 8 | void read_timer(int fd); 9 | -------------------------------------------------------------------------------- /src/utils/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | 4 | /* 5 | * On X, use X display variable; 6 | * On Wl, use Wl display variable; 7 | * Otherwise, use NULL device 8 | * (ie: we are neither on x or wayland; try drm plugin) 9 | */ 10 | const char *fetch_display() { 11 | /* 12 | * Use wayland as first case, 13 | * as sometimes on wayland you have $DISPLAY env var too! 14 | */ 15 | const char *wl_display = getenv("WAYLAND_DISPLAY"); 16 | if (!is_string_empty(wl_display)) { 17 | return wl_display; 18 | } 19 | return getenv("DISPLAY"); 20 | } 21 | 22 | /* 23 | * On X, use xauthority 24 | * On Wl, use xdg_runtime_dir 25 | * Otherwise, use NULL (unneeded with drm plugin) 26 | */ 27 | const char *fetch_env() { 28 | const char *xauth = getenv("XAUTHORITY"); 29 | if (!is_string_empty(xauth)) { 30 | return xauth; 31 | } 32 | return getenv("XDG_RUNTIME_DIR"); 33 | } 34 | 35 | /* 36 | * Only used by gamma and dpms -> in case our display is empty, 37 | * ie: no state.display neither state.wl_display are set, 38 | * clight will react to Clightd {Gamma,Dpms}.Changed signals 39 | * for any display id. Not great, not bad. 40 | * It is a very small usecase though, 41 | * ie: starting clight on tty and using clightd to change Dpms or Gamma 42 | * in another graphical session. 43 | */ 44 | bool own_display(const char *display) { 45 | const char *my_display = fetch_display(); 46 | if (is_string_empty(my_display)) { 47 | return true; 48 | } 49 | return strcmp(display, my_display) == 0; 50 | } 51 | 52 | static inline const char *mod_pause_reason_string(enum mod_pause reason) { 53 | assert(reason != UNPAUSED); 54 | 55 | static const char *reasons[] = { 56 | #define X(name, value) #name, 57 | X_FIELDS 58 | #undef X 59 | }; 60 | // this counts number of trailing 0; 61 | // it is undefined for 0 62 | const int idx = __builtin_ctz(reason) + 1; 63 | return reasons[idx]; 64 | } 65 | 66 | bool mod_check_pause(bool pause, int *paused_state, enum mod_pause reason, const char *modname) { 67 | int old_paused = *paused_state; 68 | if (pause) { 69 | *paused_state |= reason; 70 | if (old_paused == UNPAUSED) { 71 | DEBUG("Pausing %s: %s\n", modname, mod_pause_reason_string(reason)); 72 | } 73 | return old_paused == UNPAUSED; 74 | } 75 | *paused_state &= ~reason; 76 | if (old_paused != UNPAUSED && *paused_state == UNPAUSED) { 77 | DEBUG("Resuming %s: %s\n", modname, mod_pause_reason_string(reason)); 78 | return true; 79 | } 80 | return false; 81 | } 82 | 83 | bool is_string_empty(const char *str) { 84 | return str == NULL || str[0] == '\0'; 85 | } 86 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "commons.h" 4 | 5 | #define MODULE_WITH_PAUSE(name) \ 6 | static int paused_state; \ 7 | static const char *_name = name; \ 8 | MODULE(name) 9 | 10 | #define CHECK_PAUSE(pause, reason) mod_check_pause(pause, &paused_state, reason, _name) 11 | 12 | #define X_FIELDS \ 13 | X(UNPAUSED, 0) \ 14 | X(DISPLAY, 1 << 0) \ 15 | X(SENSOR, 1 << 1) \ 16 | X(AUTOCALIB, 1 << 2) \ 17 | X(LID, 1 << 3) \ 18 | X(SUSPEND, 1 << 4) \ 19 | X(TIMEOUT, 1 << 5) \ 20 | X(INHIBIT, 1 << 6) \ 21 | X(CONTRIB, 1 << 7) \ 22 | X(CLOGGED, 1 << 8) 23 | 24 | enum mod_pause { 25 | #define X(name, value) name = value, 26 | X_FIELDS 27 | #undef X 28 | }; 29 | 30 | const char *fetch_display(); 31 | const char *fetch_env(); 32 | bool own_display(const char *display); 33 | bool mod_check_pause(bool pause, int *paused_state, enum mod_pause reason, const char *modname); 34 | bool is_string_empty(const char *str); 35 | --------------------------------------------------------------------------------