├── .clang-format ├── .github ├── gif.gif ├── tile └── tile.png ├── .gitignore ├── LICENCE ├── README.md ├── include ├── astro.h ├── config.h ├── gui.h ├── map.h └── version.h ├── meson.build ├── release └── src ├── astro.c ├── gui.c ├── main.c └── map.c /.clang-format: -------------------------------------------------------------------------------- 1 | AllowShortCaseLabelsOnASingleLine: true 2 | PointerAlignment: Left 3 | IndentWidth: 4 4 | ReflowComments: true 5 | -------------------------------------------------------------------------------- /.github/gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentriz/wlr-sunclock/acd130a4d2655435d960b9c19b52a3fb20e292af/.github/gif.gif -------------------------------------------------------------------------------- /.github/tile: -------------------------------------------------------------------------------- 1 | ocean #F5F3FF earth #6D28D9 2 | ocean #EDE9FE earth #4C1D95 3 | ocean #ECFDF5 earth #831843 4 | ocean #FFFBEB earth #064E3B 5 | -------------------------------------------------------------------------------- /.github/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sentriz/wlr-sunclock/acd130a4d2655435d960b9c19b52a3fb20e292af/.github/tile.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compile_commands.json 2 | build 3 | .clangd/ 4 | .cache/ 5 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](.github/gif.gif) 2 | (demo gif, in reality it updates in real time) 3 | 4 | # wlr-sunclock 5 | 6 | wayland desktop widget to show to the sun's shadows on earth. 7 | uses [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell) and the [layer shell protocol](https://github.com/swaywm/wlr-protocols/blob/master/unstable/wlr-layer-shell-unstable-v1.xml) to render on your desktop, behind your windows 8 | 9 | now with colour support 😎 10 | ![](.github/tile.png) 11 | 12 | ## Installation 13 | 14 | [![](https://repology.org/badge/vertical-allrepos/wlr-sunclock.svg)](https://repology.org/project/wlr-sunclock/versions) 15 | 16 | ### from source 17 | 18 | pacman -S gtk-layer-shell gtk3 wayland librsvg 19 | 20 | meson build 21 | sudo ninja -C build install 22 | 23 | ### from AUR 24 | 25 | please see [wlr-sunclock-git](https://aur.archlinux.org/packages/wlr-sunclock-git/) 26 | 27 | ### from OBS 28 | 29 | please see [X11:Wayland/wlr-sunclock](https://build.opensuse.org/package/show/X11:Wayland/wlr-sunclock) for the openSUSE package 30 | 31 | ### from Fedora Copr 32 | 33 | please see [wef/wlr-sunclock](https://copr.fedorainfracloud.org/coprs/wef/wlr-sunclock/) for a Fedora package 34 | 35 | ## CLI args 36 | 37 | -a, --anchors=ANCHORS string of window anchors (see below) 38 | -i, --monitor-index=MONITOR_INDEX monitor to show window on (starts at 0) 39 | -l, --layer= desktop layer to show the widget on 40 | -m, --margins=MARGINS comma separated margins for window 41 | -w, --width=WIDTH width of the window 42 | -d, --border-width=BORDER_WIDTH width of the window's border 43 | -o, --colour-ocean=COLOUR_OCEAN colour of the ocean 44 | -n, --colour-land=COLOUR_LAND colour of the land 45 | -v, --version print version 46 | 47 | ## Anchors 48 | 49 | -a tl -a tr -a br -a bl -a '' -a tblr 50 | ┌─┬───┐ ┌───┬─┐ ┌─────┐ ┌─────┐ ┌─────┐ ╔═════╗ 51 | ├─┘ │ │ └─┤ │ │ │ │ │ ┌─┐ │ ║ ║ 52 | │ │ │ │ │ ┌─┤ ├─┐ │ │ └─┘ │ ║ ║ 53 | └─────┘ └─────┘ └───┴─┘ └─┴───┘ └─────┘ ╚═════╝ 54 | 55 | ## Start on boot with sway example 56 | 57 | # ~/.config/sway/config 58 | 59 | exec wlr-sunclock \ 60 | --margins "0,16,0,16" \ 61 | --width 800 \ 62 | --layer bottom \ 63 | --anchors br \ 64 | --border-width 2 \ 65 | --colour-ocean "#C4B5FD" \ 66 | --colour-land "#6D28D9" 67 | -------------------------------------------------------------------------------- /include/astro.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNCLOCK_ASTRO_H 2 | #define SUNCLOCK_ASTRO_H 3 | 4 | #include 5 | 6 | double astro_gm_time_to_julian(struct tm* t); 7 | long astro_gm_time_to_julian_astro(struct tm* t); 8 | double astro_kepler(double m, double ecc); 9 | void astro_sun_position(double jd, int apparent, double* ra, double* dec, 10 | double* rv, double* slong); 11 | void astro_project_illum(short* wtab, int xdots, int ydots, double dec); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNCLOCK_CONFIG_H 2 | #define SUNCLOCK_CONFIG_H 3 | 4 | enum sunclock_layer { 5 | SUNCLOCK_LAYER_BACKGROUND, 6 | SUNCLOCK_LAYER_BOTTOM, 7 | SUNCLOCK_LAYER_TOP, 8 | SUNCLOCK_LAYER_OVERLAY, 9 | }; 10 | 11 | struct sunclock_gui_settings { 12 | int margins[4]; 13 | int border_width; 14 | 15 | char* title; 16 | enum sunclock_layer layer; 17 | char* anchors; 18 | int monitor_index; 19 | int width; 20 | 21 | char* colour_ocean; 22 | char* colour_land; 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/gui.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNCLOCK_GUI_H 2 | #define SUNCLOCK_GUI_H 3 | 4 | #include "config.h" 5 | #include "gtk-layer-shell.h" 6 | #include 7 | 8 | void sunclock_gui_activate(GtkApplication* app, gpointer psettings); 9 | int sunclock_gui_start(struct sunclock_gui_settings* settings); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /include/map.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNCLOCK_MAP__H 2 | #define SUNCLOCK_MAP__H 3 | 4 | #include 5 | 6 | gchar* sunclock_map_svg(char* colour_ocean, char* colour_land); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/version.h: -------------------------------------------------------------------------------- 1 | #ifndef SUNCLOCK_VERSION_H 2 | #define SUNCLOCK_VERSION_H 3 | 4 | #define SUNCLOCK_VERSION "v1.0.0" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'wlr-sunclock', 3 | 'c', 4 | meson_version : '>=0.46.0', 5 | default_options : ['warning_level=3', 'werror=true'], 6 | version : 'v1.0.0' 7 | ) 8 | 9 | cc = meson.get_compiler('c') 10 | gtk = dependency('gtk+-wayland-3.0', version: '>=3.22.0') 11 | gtk_layer_shell = dependency('gtk-layer-shell-0') 12 | librsvg = dependency('librsvg-2.0') 13 | 14 | add_project_arguments(cc.get_supported_arguments([ 15 | '-Wno-deprecated-declarations', 16 | '-Wno-overlength-strings', 17 | ]), language: 'c') 18 | 19 | executable( 20 | meson.project_name(), 21 | files( 22 | 'src/main.c', 23 | 'src/gui.c', 24 | 'src/astro.c', 25 | 'src/map.c', 26 | ), 27 | include_directories: ['include'], 28 | dependencies: [ 29 | cc.find_library('m'), 30 | gtk, 31 | gtk_layer_shell, 32 | librsvg, 33 | ], 34 | install: true, 35 | link_args: ['-lm'], 36 | ) 37 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # safety checks 4 | if [[ $# -ne 1 ]]; then 5 | echo "usage: $0 " >&2 6 | exit 1 7 | fi 8 | 9 | if [[ "$(git rev-parse --abbrev-ref HEAD)" != 'master' ]]; then 10 | echo "not on the master branch" >&2 11 | exit 1 12 | fi 13 | 14 | # get latest and check if clean 15 | if [[ -n "$(git status --porcelain)" ]]; then 16 | echo "working directory is dirty" >&2 17 | exit 1 18 | fi 19 | 20 | # get the current verison from last git tag into array and 21 | # inc the provided part 22 | semver_expression='s/^v([0-9]+)\.([0-9]+)\.([0-9]+).*$/\1 \2 \3/' 23 | version=( $(git describe --tags | sed -E -e "$semver_expression" ) ) 24 | case "$1" in 25 | major) ((version[0]++)) 26 | version[1]=0 27 | version[2]=0 28 | ;; 29 | minor) ((version[1]++)) 30 | version[2]=0 31 | ;; 32 | patch) ((version[2]++)) 33 | ;; 34 | *) echo 'please provide a valid version in increment' >&2 35 | exit 1 36 | ;; 37 | esac 38 | new_version="v${version[0]}.${version[1]}.${version[2]}" 39 | 40 | # update meson.build 41 | meson rewrite kwargs set project / version "$new_version" 42 | 43 | # write version header 44 | cat > include/version.h < 45 | #include 46 | #include 47 | #include 48 | 49 | #define PI 3.14159265358979323846 50 | #define E 2.7182818284590452354 51 | #define EPSILON 1E-6 52 | 53 | #define abs(x) ((x) < 0 ? (-(x)) : x) /* absolute value */ 54 | #define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* extract sign */ 55 | #define dtr(x) ((x) * (PI / 180.0)) /* degree -> radian */ 56 | #define rtd(x) ((x) / (PI / 180.0)) /* radian -> degree */ 57 | #define fixangle(a) ((a)-360.0 * (floor((a) / 360.0))) /* fix angle */ 58 | 59 | #define TERMINC 100 /* circle segments for terminator */ 60 | #define PROJINT (60 * 10) /* frequency of seasonal recalculation */ 61 | 62 | /* convert internal GMT date and time to Julian day and fraction */ 63 | double astro_gm_time_to_julian(struct tm* t) { 64 | double y = t->tm_year + 1900; 65 | double m = t->tm_mon + 1; 66 | if (m > 2) 67 | m = m - 3; 68 | else { 69 | m = m + 9; 70 | y--; 71 | } 72 | /* compute century */ 73 | double c = y / 100L; 74 | y -= 100L * c; 75 | return t->tm_mday + (c * 146097L) / 4 + (y * 1461L) / 4 + 76 | (m * 153L + 2) / 5 + 1721119L; 77 | } 78 | 79 | /* convert internal GMT date and time to astronomical Julian time (i.e. Julian 80 | * date plus day fraction, expressed as a double) */ 81 | long astro_gm_time_to_julian_astro(struct tm* t) { 82 | return (astro_gm_time_to_julian(t) - 0.5) + 83 | (((long)t->tm_sec) + 60L * (t->tm_min + 60L * t->tm_hour)) / 86400.0; 84 | } 85 | 86 | double astro_kepler(double m, double ecc) { 87 | double delta; 88 | double e = m = dtr(m); 89 | do { 90 | delta = e - ecc * sin(e) - m; 91 | e -= delta / (1 - ecc * cos(e)); 92 | } while (abs(delta) > EPSILON); 93 | return e; 94 | } 95 | 96 | /* calculate position of the Sun. JD is the Julian date of the instant for which 97 | the position is desired and APPARENT should be nonzero if the apparent position 98 | (corrected for nutation and aberration) is desired. The Sun's co-ordinates are 99 | returned in RA and DEC, both specified in degrees (divide RA by 15 to obtain 100 | hours). The radius vector to the Sun in astronomical units is returned in RV 101 | and the Sun's longitude (true or apparent, as desired) is returned as degrees 102 | in SLONG */ 103 | void astro_sun_position(double jd, int apparent, double* ra, double* dec, 104 | double* rv, double* slong) { 105 | /* time in Julian centuries of 36525 ephemeris days, measured from the epoch 106 | * 1900 January 0.5 ET */ 107 | double t = (jd - 2415020.0) / 36525.0; 108 | double t2 = t * t; 109 | double t3 = t2 * t; 110 | 111 | /* geometric mean longitude of the Sun, referred to the mean equinox of the 112 | * date */ 113 | double l = fixangle(279.69668 + 36000.76892 * t + 0.0003025 * t2); 114 | 115 | /* sun's mean anomaly */ 116 | double m = 117 | fixangle(358.47583 + 35999.04975 * t - 0.000150 * t2 - 0.0000033 * t3); 118 | 119 | /* eccentricity of the Earth's orbit */ 120 | double e = 0.01675104 - 0.0000418 * t - 0.000000126 * t2; 121 | 122 | /* eccentric anomaly */ 123 | double ea = astro_kepler(m, e); 124 | 125 | /* true anomaly */ 126 | double v = fixangle(2 * rtd(atan(sqrt((1 + e) / (1 - e)) * tan(ea / 2)))); 127 | 128 | /* sun's true longitude */ 129 | double theta = l + v - m; 130 | 131 | /* obliquity of the ecliptic */ 132 | double eps = 23.452294 - 0.0130125 * t - 0.00000164 * t2 + 0.000000503 * t3; 133 | 134 | /* corrections for Sun's apparent longitude, if desired */ 135 | if (apparent) { 136 | double omega = fixangle(259.18 - 1934.142 * t); 137 | theta = theta - 0.00569 - 0.00479 * sin(dtr(omega)); 138 | eps += 0.00256 * cos(dtr(omega)); 139 | } 140 | 141 | /* return Sun's longitude and radius vector */ 142 | *slong = theta; 143 | *rv = (1.0000002 * (1 - e * e)) / (1 + e * cos(dtr(v))); 144 | 145 | /* determine solar co-ordinates */ 146 | *ra = 147 | fixangle(rtd(atan2(cos(dtr(eps)) * sin(dtr(theta)), cos(dtr(theta))))); 148 | *dec = rtd(asin(sin(dtr(eps)) * sin(dtr(theta)))); 149 | } 150 | 151 | /* greenwich Mean Siderial Time for a given instant expressed as a Julian date 152 | * and fraction */ 153 | double astro_gm_siderial_time(double jd) { 154 | /* time, in Julian centuries of 36525 ephemeris days, measured from the 155 | * epoch 1900 January 0.5 ET */ 156 | double t = ((floor(jd + 0.5) - 0.5) - 2415020.0) / 36525.0; 157 | double theta0 = 6.6460656 + 2400.051262 * t + 0.00002581 * t * t; 158 | 159 | t = (jd + 0.5) - (floor(jd + 0.5)); 160 | theta0 += (t * 24.0) * 1.002737908; 161 | theta0 = (theta0 - 24.0 * (floor(theta0 / 24.0))); 162 | return theta0; 163 | } 164 | 165 | /* project illuminated area on the map */ 166 | void astro_project_illum(short* wtab, int xdots, int ydots, double dec) { 167 | int i, ftf = 1, ilon, ilat, lilon = 0, lilat = 0, xt; 168 | double m, x, y, z, th, lon, lat; 169 | 170 | /* clear unoccupied cells in width table */ 171 | for (i = 0; i < ydots; i++) 172 | wtab[i] = -1; 173 | 174 | /* build transformation for declination */ 175 | double s = sin(-dtr(dec)); 176 | double c = cos(-dtr(dec)); 177 | 178 | /* increment over a semicircle of illumination */ 179 | for (th = -(PI / 2); th <= PI / 2 + 0.001; th += PI / TERMINC) { 180 | /* transform the point through the declination rotation */ 181 | x = -s * sin(th); 182 | y = cos(th); 183 | z = c * sin(th); 184 | 185 | /* transform the resulting co-ordinate through the map projection to 186 | * obtain screen co-ordinates */ 187 | lon = (y == 0 && x == 0) ? 0.0 : rtd(atan2(y, x)); 188 | lat = rtd(asin(z)); 189 | 190 | ilat = ydots - (lat + 90) * (ydots / 180.0); 191 | ilon = lon * (xdots / 360.0); 192 | 193 | if (ftf) { 194 | /* first time. just save start co-ordinate */ 195 | lilon = ilon; 196 | lilat = ilat; 197 | ftf = 0; 198 | continue; 199 | } 200 | 201 | /* trace out the line and set the width table */ 202 | if (lilat == ilat) { 203 | wtab[(ydots - 1) - ilat] = ilon == 0 ? 1 : ilon; 204 | } else { 205 | m = ((double)(ilon - lilon)) / (ilat - lilat); 206 | for (i = lilat; i != ilat; i += sgn(ilat - lilat)) { 207 | xt = lilon + floor((m * (i - lilat)) + 0.5); 208 | wtab[(ydots - 1) - i] = xt == 0 ? 1 : xt; 209 | } 210 | } 211 | lilon = ilon; 212 | lilat = ilat; 213 | } 214 | 215 | /* tweak the widths to generate full illumination for the correct pole */ 216 | if (dec < 0.0) { 217 | ilat = ydots - 1; 218 | lilat = -1; 219 | } else { 220 | ilat = 0; 221 | lilat = 1; 222 | } 223 | 224 | for (i = ilat; i != ydots / 2; i += lilat) { 225 | if (wtab[i] != -1) { 226 | while (1) { 227 | wtab[i] = xdots / 2; 228 | if (i == ilat) 229 | break; 230 | i -= lilat; 231 | } 232 | break; 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/gui.c: -------------------------------------------------------------------------------- 1 | #include "astro.h" 2 | #include "config.h" 3 | #include "gtk-layer-shell.h" 4 | #include "map.h" 5 | #include 6 | #include 7 | #include 8 | 9 | static void sunclock_gui_fill_wtab(short* wtab, int width, int height, 10 | int* gmt_position) { 11 | time_t raw_time; 12 | time(&raw_time); 13 | struct tm* gm_time = gmtime(&raw_time); 14 | 15 | double jt, sunra, sundec, sunrv, sunlong; 16 | jt = astro_gm_time_to_julian_astro(gm_time); 17 | astro_sun_position(jt, FALSE, &sunra, &sundec, &sunrv, &sunlong); 18 | astro_project_illum(wtab, width, height, sundec); 19 | 20 | // note: greenwich is in the middle 21 | int sec = 22 | gm_time->tm_hour * 60 * 60 + gm_time->tm_min * 60 + gm_time->tm_sec; 23 | *gmt_position = width * sec / 86400; 24 | } 25 | 26 | static void sunclock_gui_draw_line(cairo_t* cr, short x1, short y1, short x2, 27 | short y2) { 28 | cairo_move_to(cr, x1, y1); 29 | cairo_line_to(cr, x2, y2); 30 | cairo_stroke(cr); 31 | } 32 | 33 | static gboolean sunclock_gui_draw_shade_timeout(GtkWidget* widget) { 34 | gtk_widget_queue_draw(widget); 35 | return TRUE; 36 | } 37 | 38 | static gboolean sunclock_gui_draw_shade(GtkWidget* widget, cairo_t* cr, 39 | gpointer image) { 40 | int height = gtk_widget_get_allocated_height(widget); 41 | int width = gtk_widget_get_allocated_width(widget); 42 | 43 | GdkPixbuf* image_scaled = gdk_pixbuf_scale_simple( 44 | (GdkPixbuf*)image, width, height, GDK_INTERP_BILINEAR); 45 | gdk_cairo_set_source_pixbuf(cr, image_scaled, 0, 0); 46 | cairo_paint(cr); 47 | g_object_unref(image_scaled); 48 | 49 | GdkRGBA colour; 50 | colour.red = 0.933333; 51 | colour.green = 0.933333; 52 | colour.blue = 0.925490; 53 | colour.alpha = 1.0; 54 | 55 | gdk_cairo_set_source_rgba(cr, &colour); 56 | 57 | // calculate the illuminated area 58 | int gmt_position; 59 | short wtab[height]; 60 | sunclock_gui_fill_wtab(wtab, width, height, &gmt_position); 61 | 62 | // draw illuminated area 63 | cairo_set_line_width(cr, 0.3); 64 | int start, stop; 65 | int middle = width - gmt_position; 66 | for (int y = 0; y < height; y++) { 67 | if (wtab[y] <= 0) 68 | continue; 69 | start = middle - wtab[y]; 70 | stop = middle + wtab[y]; 71 | if (start < 0) { 72 | sunclock_gui_draw_line(cr, 0, y, stop, y); 73 | sunclock_gui_draw_line(cr, width + start, y, width, y); 74 | } else if (stop > width) { 75 | sunclock_gui_draw_line(cr, start, y, width, y); 76 | sunclock_gui_draw_line(cr, 0, y, stop - width, y); 77 | } else 78 | sunclock_gui_draw_line(cr, start, y, stop, y); 79 | } 80 | 81 | return FALSE; 82 | } 83 | 84 | void sunclock_gui_activate(GtkApplication* app, gpointer psettings) { 85 | GtkWindow* gtk_window = GTK_WINDOW(gtk_application_window_new(app)); 86 | gtk_layer_init_for_window(gtk_window); 87 | 88 | struct sunclock_gui_settings* settings = psettings; 89 | 90 | // user setting border, layer 91 | gtk_container_set_border_width(GTK_CONTAINER(gtk_window), 92 | settings->border_width); 93 | gtk_layer_set_layer(gtk_window, (int)settings->layer); 94 | 95 | // user setting margin 96 | for (int i = 0; i < 4; i++) 97 | gtk_layer_set_margin(gtk_window, i, settings->margins[i]); 98 | 99 | // user setting anchor 100 | for (const char* c = settings->anchors; *c; c++) 101 | switch (*c) { 102 | case 'l': gtk_layer_set_anchor(gtk_window, 0, TRUE); break; 103 | case 'r': gtk_layer_set_anchor(gtk_window, 1, TRUE); break; 104 | case 't': gtk_layer_set_anchor(gtk_window, 2, TRUE); break; 105 | case 'b': gtk_layer_set_anchor(gtk_window, 3, TRUE); break; 106 | } 107 | 108 | // user setting monitor index 109 | GdkDisplay* display = gdk_display_get_default(); 110 | GdkMonitor* monitor = 111 | gdk_display_get_monitor(display, settings->monitor_index); 112 | gtk_layer_set_monitor(gtk_window, monitor); 113 | 114 | GtkWidget* canvas = gtk_drawing_area_new(); 115 | gtk_widget_set_hexpand(canvas, TRUE); 116 | gtk_widget_set_vexpand(canvas, TRUE); 117 | 118 | int image_width = settings->width; 119 | int image_height = settings->width / 2; 120 | gtk_widget_set_size_request(canvas, image_width, image_height); 121 | 122 | const gchar* map_svg = 123 | sunclock_map_svg(settings->colour_ocean, settings->colour_land); 124 | glong map_len = g_utf8_strlen(map_svg, -1); 125 | RsvgHandle* map_handle = 126 | rsvg_handle_new_from_data((guint8*)map_svg, map_len, NULL); 127 | GdkPixbuf* map_image = rsvg_handle_get_pixbuf(map_handle); 128 | 129 | // draw canvas now and every 30s 130 | g_signal_connect(canvas, "draw", G_CALLBACK(sunclock_gui_draw_shade), 131 | map_image); 132 | g_timeout_add_seconds(30, G_SOURCE_FUNC(sunclock_gui_draw_shade_timeout), 133 | canvas); 134 | 135 | gtk_container_add(GTK_CONTAINER(gtk_window), canvas); 136 | gtk_widget_show_all(GTK_WIDGET(gtk_window)); 137 | } 138 | 139 | int sunclock_gui_start(struct sunclock_gui_settings* settings) { 140 | GtkApplication* app = gtk_application_new(settings->title, 0); 141 | g_signal_connect(app, "activate", G_CALLBACK(sunclock_gui_activate), 142 | settings); 143 | 144 | int status = g_application_run(G_APPLICATION(app), 0, 0); 145 | g_object_unref(app); 146 | return status; 147 | } 148 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "gui.h" 3 | #include "version.h" 4 | #include 5 | #include 6 | #include 7 | 8 | static enum sunclock_layer layer_from_str(char* in) { 9 | if (strcmp(in, "background") == 0) 10 | return SUNCLOCK_LAYER_BACKGROUND; 11 | if (strcmp(in, "bottom") == 0) 12 | return SUNCLOCK_LAYER_BOTTOM; 13 | if (strcmp(in, "top") == 0) 14 | return SUNCLOCK_LAYER_TOP; 15 | if (strcmp(in, "overlay") == 0) 16 | return SUNCLOCK_LAYER_OVERLAY; 17 | return -1; 18 | } 19 | 20 | // clang-format off 21 | static char doc[] = "Displays a sunclock desktop widget using the layer shell protocol"; 22 | static struct argp_option options[] = { 23 | {"layer", 'l', "", 0, "desktop layer to show the widget on", 1}, 24 | {"width", 'w', "WIDTH", 0, "width of the window", 1}, 25 | {"anchors", 'a', "ANCHORS", 0, "string of window anchors (see readme)", 1}, 26 | {"margins", 'm', "MARGINS", 0, "comma separated margins for window", 1}, 27 | {"monitor-index", 'i', "MONITOR_INDEX", 0, "monitor to show window on (starts at 0)", 1}, 28 | {"border-width", 'd', "BORDER_WIDTH", 0, "width of the window's border", 2}, 29 | {"colour-ocean", 'o', "COLOUR_OCEAN", 0, "colour of the ocean", 3}, 30 | {"colour-land", 'n', "COLOUR_LAND", 0, "colour of the land", 3}, 31 | {"version", 'v', NULL, 0, "print version", 4}, 32 | {0}, 33 | }; 34 | // clang-format on 35 | 36 | static error_t parse_option(int key, char* arg, struct argp_state* state) { 37 | struct sunclock_gui_settings* settings = state->input; 38 | switch (key) { 39 | case 'l': 40 | settings->layer = layer_from_str(arg); 41 | if ((int)settings->layer == -1) { 42 | fprintf(stderr, "invalid layer %s provided\n", arg); 43 | argp_usage(state); 44 | }; 45 | break; 46 | case 'w': settings->width = atoi(arg); break; 47 | case 'a': settings->anchors = arg; break; 48 | case 'm': 49 | for (int i = 0; i < 4; i++) 50 | settings->margins[i] = atoi(strsep(&arg, ",")); 51 | break; 52 | case 'i': settings->monitor_index = atoi(arg); break; 53 | case 'd': settings->border_width = atoi(arg); break; 54 | case 'o': settings->colour_ocean = arg; break; 55 | case 'n': settings->colour_land = arg; break; 56 | case 'v': 57 | fprintf(stderr, "sunclock version %s\n", SUNCLOCK_VERSION); 58 | exit(0); 59 | break; 60 | case ARGP_KEY_ARG: break; 61 | default: return ARGP_ERR_UNKNOWN; 62 | } 63 | return 0; 64 | } 65 | 66 | int main(int argc, char* argv[]) { 67 | struct argp argp = {options, parse_option, NULL, doc, NULL, NULL, NULL}; 68 | struct sunclock_gui_settings settings = { 69 | .title = "xyz.senan.wlr-sunclock", 70 | .layer = SUNCLOCK_LAYER_BOTTOM, 71 | .width = 300, 72 | .anchors = "", 73 | .monitor_index = 0, 74 | .colour_ocean = "#4c446d", 75 | .colour_land = "#726f9e", 76 | }; 77 | argp_parse(&argp, argc, argv, 0, 0, &settings); 78 | return sunclock_gui_start(&settings); 79 | } 80 | -------------------------------------------------------------------------------- /src/map.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // clang-format off 4 | const char* svg = 5 | "" 6 | "" 7 | "" 8 | "" 9 | ""; 10 | // clang-format on 11 | 12 | gchar* sunclock_map_svg(char* colour_ocean, char* colour_land) { 13 | return g_strdup_printf(svg, colour_ocean, colour_land); 14 | } 15 | --------------------------------------------------------------------------------