├── .coafile
├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── actions
│ └── build
│ │ └── action.yml
└── workflows
│ ├── build.yml
│ └── publish.yml
├── .gitignore
├── .gitmodules
├── .pylintrc
├── COPYING
├── Makefile
├── NEWS.md
├── README.md
├── build
├── DEBIAN
│ ├── conffiles
│ ├── control
│ └── postinst
├── Xgreeter.patch
├── ci
│ └── Dockerfile
└── utils.sh
├── circle.yml
├── dist
├── 90-greeter-wrapper.conf
├── Xgreeter
├── com.github.jezerm.web-greeter.svg
├── web-greeter-bash
├── web-greeter-zsh
├── web-greeter.1
├── web-greeter.appdata.xml
├── web-greeter.desktop
├── web-greeter.yml
└── web-xgreeter.desktop
├── docs
├── Greeter.js
├── GreeterConfig.js
├── LightDMObjects.js
├── ThemeUtils.js
└── bootstrap.js
├── requirements.txt
├── src
├── __init__.py
├── __main__.py
├── bindings
│ ├── __init__.py
│ ├── screensaver.c
│ └── screensaver.py
├── bridge
│ ├── Config.py
│ ├── Greeter.py
│ ├── GreeterComm.py
│ ├── ThemeUtils.py
│ └── __init__.py
├── browser
│ ├── __init__.py
│ ├── bridge.py
│ ├── browser.py
│ ├── browser_interfaces.py
│ ├── error_prompt.py
│ ├── interceptor.py
│ ├── url_scheme.py
│ └── window.py
├── config.py
├── globales.py
├── logger.py
├── requirements.txt
├── resources
│ ├── css
│ │ └── style.css
│ ├── js
│ │ ├── GreeterComm.js
│ │ ├── ThemeUtils.js
│ │ ├── bootstrap.js
│ │ └── docs
│ │ │ ├── Greeter.js
│ │ │ ├── GreeterConfig.js
│ │ │ └── LightDMObjects.js
│ └── resources.qrc
└── utils
│ ├── __init__.py
│ ├── acpi.py
│ ├── battery.py
│ └── brightness.py
└── web-greeter.doap
/.coafile:
--------------------------------------------------------------------------------
1 | [Default]
2 | bears = IndentBear,LineLengthBear,SpaceConsistencyBear
3 | max_line_length = 99
4 | use_spaces = False
5 | indent_cli_options = -bad -bap -bbo -nbc -br -brs -brf -c33 -cd33 -cdb -ce -ci4 -cli4 -cbi4 -cdw \
6 | -cp33 -cs -d0 -di1 -fc1 -fca -hnl -i4 -ip0 -l99 -lp -npcs -ut -nprs -npsl \
7 | -saf -sai -saw -sc -nsob -nss -ts4
8 |
9 | [C]
10 | bears = CPPLintBear,ClangASTPrintBear,ClangBear,ClangCloneDetectionBear,ClangFunctionDifferenceBear
11 | files = **/*.(c|h|cpp)
12 | cpplint_ignore = whitespace/tab
13 |
14 | [JavaScript]
15 | bears = JSHintBear
16 | files = **/*.js
17 | ignore = node_modules/
18 |
19 | [Python]
20 | bears=PEP8Bear,PyCommentedCodeBear,PyImportSortBear,PyLintBear,PyUnusedCodeBear
21 | files = **/*.py
22 | ignore = build/**/*.py
23 |
24 | [Docker]
25 | bears= DockerfileLintBear
26 | files= **/Dockerfile
27 |
28 | [Markdown]
29 | bears = AlexBear,MarkdownBear
30 | files = **/*.md
31 |
32 | [JSON]
33 | bears = JSONFormatBear
34 | files = **/*.json
35 | ignore = node_modules/**/*.json
36 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | continuation_indent_size = 4
7 | curly_bracket_next_line = false
8 | end_of_line = lf
9 | indent_brace_style = 1TBS
10 | indent_size = 4
11 | indent_style = space
12 | insert_final_newline = true
13 | max_line_length = 100
14 | tab_width = 4
15 | trim_trailing_whitespace = true
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | **/_vendor/** linguist-vendored
2 | **/docs/** linguist-vendored
3 | **/i18n/** linguist-vendored
4 | **/.tx/** linguist-vendored
5 | **/translations.js linguist-vendored
6 | **/themes/** linguist-vendored
7 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: jezerm
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Environment
11 | - OS: [e.g. Ubuntu 21.04]
12 | - web-greeter version: output of `web-greeter --version`
13 | - LightDM version: output of `lightdm --version`
14 |
15 | ## Bug description
16 | A clear and concise description of what the bug is.
17 |
18 |
25 |
26 | ## Steps to reproduce
27 | Steps to reproduce the behavior.
28 |
29 | ## Expected behavior
30 | A clear and concise description of what you expected to happen.
31 |
32 | ## Screenshots
33 | If applicable, add screenshots to help explain your problem.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Environment
11 | - OS: [e.g. Ubuntu 21.04]
12 | - web-greeter version: output of `web-greeter --version`
13 |
14 | ## Description of feature
15 | A clear and concise description of what you want to happen.
16 |
17 | ## Possible solutions
18 | Add alternatives solutions to this feature or problem.
19 |
--------------------------------------------------------------------------------
/.github/actions/build/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build web-greeter"
2 | description: "Builds web-greeter"
3 | inputs:
4 | target-distro:
5 | required: true
6 | description: "Target distro"
7 | runs:
8 | using: "composite"
9 | steps:
10 | -
11 | name: Checkout
12 | uses: actions/checkout@v2
13 | with:
14 | submodules: recursive
15 | -
16 | name: Install dependencies
17 | shell: bash
18 | run: |
19 | sudo apt update
20 | sudo apt install \
21 | liblightdm-gobject-1-dev \
22 | libgirepository1.0-dev \
23 | libqt5webengine5 \
24 | pyqt5-dev-tools \
25 | python3-gi \
26 | python3-pyqt5 \
27 | python3-ruamel.yaml \
28 | python3-pyinotify \
29 | python3-xlib \
30 | python3-pip \
31 | dpkg
32 | -
33 | name: Build web-greeter
34 | shell: bash
35 | run: make build
36 | -
37 | name: Prepare deb build (common)
38 | shell: bash
39 | run: |
40 | cp -r build/DEBIAN/ build/install_root/
41 | -
42 | name: Prepare deb build (for Debian)
43 | shell: bash
44 | if: ${{ inputs.target-distro == 'debian' }}
45 | run: |
46 | sed -i "s/liblightdm-gobject-1-dev/liblightdm-gobject-dev/g" build/install_root/DEBIAN/control
47 | -
48 | name: Build deb
49 | shell: bash
50 | run: |
51 | cd build/
52 | dpkg-deb --root-owner-group --build install_root "web-greeter.deb"
53 | dpkg --info "./web-greeter.deb"
54 | -
55 | name: 'Upload Artifact'
56 | uses: actions/upload-artifact@v2
57 | with:
58 | name: build-${{ inputs.target-distro }}
59 | path: ./build/web-greeter.deb
60 | retention-days: 7
61 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build Test
2 |
3 | on:
4 | push:
5 | paths:
6 | - "src/**"
7 | - ".github/workflows/build.yml"
8 | - ".github/actions/build/action.yml"
9 | pull_request:
10 | workflow_dispatch:
11 |
12 | jobs:
13 | build-ubuntu:
14 | runs-on: ubuntu-latest
15 | steps:
16 | -
17 | name: Checkout local actions
18 | uses: actions/checkout@v2
19 | with:
20 | submodules: recursive
21 | -
22 | name: Build and install web-greeter
23 | uses: "./.github/actions/build"
24 | with:
25 | target-distro: ubuntu
26 | build-debian:
27 | runs-on: ubuntu-latest
28 | steps:
29 | -
30 | name: Checkout local actions
31 | uses: actions/checkout@v2
32 | with:
33 | submodules: recursive
34 | -
35 | name: Build web-greeter
36 | uses: "./.github/actions/build"
37 | with:
38 | target-distro: debian
39 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 | workflow_dispatch:
8 |
9 | jobs:
10 | publish-ubuntu:
11 | name: Publish ubuntu binaries
12 | runs-on: ubuntu-latest
13 | steps:
14 | -
15 | name: Checkout local actions
16 | uses: actions/checkout@v2
17 | with:
18 | submodules: recursive
19 | -
20 | name: Build and install web-greeter
21 | uses: "./.github/actions/build"
22 | with:
23 | target-distro: ubuntu
24 | - name: Upload binaries to release
25 | uses: svenstaro/upload-release-action@v2
26 | with:
27 | repo_token: ${{ secrets.GITHUB_TOKEN }}
28 | file: build/web-greeter.deb
29 | asset_name: web-greeter-$tag-ubuntu.deb
30 | tag: ${{ github.ref }}
31 | overwrite: true
32 | publish-debian:
33 | name: Publish debian binaries
34 | runs-on: ubuntu-latest
35 | steps:
36 | -
37 | name: Checkout local actions
38 | uses: actions/checkout@v2
39 | with:
40 | submodules: recursive
41 | -
42 | name: Build web-greeter
43 | uses: "./.github/actions/build"
44 | with:
45 | target-distro: debian
46 | - name: Upload binaries to release
47 | uses: svenstaro/upload-release-action@v2
48 | with:
49 | repo_token: ${{ secrets.GITHUB_TOKEN }}
50 | file: build/web-greeter.deb
51 | asset_name: web-greeter-$tag-debian.deb
52 | tag: ${{ github.ref }}
53 | overwrite: true
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 | resources.py
3 | build/**
4 | !build/.gitignore
5 | !build/ci/
6 | !build/utils.sh
7 | !build/DEBIAN/
8 | whither/
9 |
10 | ### JetBrains template
11 | .idea/
12 |
13 |
14 | ### Python template
15 | # Byte-compiled / optimized / DLL files
16 | __pycache__/
17 |
18 | # C extensions
19 | *.so
20 |
21 | # Distribution / packaging
22 | .Python
23 | env/
24 | venv/
25 | develop-eggs/
26 | downloads/
27 | eggs/
28 | .eggs/
29 | lib/
30 | lib64/
31 | parts/
32 | sdist/
33 | var/
34 | web-greeter/dist
35 | *.egg-info/
36 | .installed.cfg
37 | *.egg
38 |
39 | # Installer logs
40 | pip-log.txt
41 | pip-delete-this-directory.txt
42 |
43 | # Translations
44 | *.mo
45 |
46 | ### Autotools template
47 | Makefile.in
48 | /autom4te.cache
49 | /aclocal.m4
50 | /compile
51 | /configure
52 | /depcomp
53 | /install-sh
54 | /missing
55 | /stamp-h1
56 |
57 | # gh-pages
58 | _book/
59 | node_modules/
60 | package-lock.json
61 |
62 | # vscode
63 | .vscode/
64 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "themes"]
2 | path = themes
3 | url = https://github.com/JezerM/web-greeter-themes.git
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | DESTDIR ?= /
2 | PREFIX ?= /usr
3 | ENABLE_BASH_COMPLETION ?= true
4 | ENABLE_ZSH_COMPLETION ?= true
5 |
6 |
7 | BUILD_DIR := $(abspath build)
8 | REPO_DIR := $(abspath ./)
9 | INSTALL_ROOT := $(abspath build/install_root)
10 | INSTALL_PREFIX :=$(abspath $(shell echo ${INSTALL_ROOT}/${PREFIX}))
11 | DESTDIR_PREFIX :=$(abspath $(shell echo ${DESTDIR}/${PREFIX}))
12 |
13 | DISTRO := $(shell [ -e "/etc/os-release" ] && cat /etc/os-release|sed -n -e 's/^ID="\?\(\w*\)"\?/\1/p' )
14 |
15 | ifeq (${ENABLE_BASH_COMPLETION}, true)
16 | ifeq ($(shell pkg-config --exists bash-completion && echo 0), 0)
17 | bashcompletiondir := $(shell pkg-config --variable=completionsdir bash-completion)
18 | endif
19 | endif
20 |
21 | ifeq (${ENABLE_ZSH_COMPLETION}, true)
22 | ifeq ($(shell which zsh >/dev/null 2>&1 && echo 0), 0)
23 | zshcompletiondir := /usr/share/zsh/site-functions/
24 | endif
25 | endif
26 |
27 | ifdef bashcompletiondir
28 | bashcompletiondir_local := ${INSTALL_ROOT}/${bashcompletiondir}
29 | $(info Bash completion to install at: ${bashcompletiondir})
30 | else
31 | $(info No Bash completion)
32 | endif
33 | ifdef zshcompletiondir
34 | zshcompletiondir_local := ${INSTALL_ROOT}/${zshcompletiondir}
35 | $(info ZSH completion to install at: ${zshcompletiondir})
36 | else
37 | $(info No ZSH completion)
38 | endif
39 |
40 | all: build
41 |
42 | # Dist and web-greeter directories
43 | build/dist := ${BUILD_DIR}/dist
44 | build/web-greeter := ${BUILD_DIR}/web-greeter
45 |
46 | $(build/dist): dist/*
47 | @rm -rf "${BUILD_DIR}/dist"
48 | @rsync -a "${REPO_DIR}/dist" "${BUILD_DIR}"
49 | @cp "${REPO_DIR}/NEWS.md" "${BUILD_DIR}/dist/NEWS.md"
50 | @echo "✔ Dist directory created at ${build/dist}"
51 |
52 | $(build/web-greeter): src/*
53 | @rsync -a "${REPO_DIR}/src/" "${BUILD_DIR}/web-greeter" --exclude "dist" --exclude "__pycache__"
54 | @cp "${REPO_DIR}/README.md" "${BUILD_DIR}/web-greeter/"
55 | @echo "✔ Build directory created at ${build/web-greeter}"
56 |
57 | # Resources
58 | bundle.js := ${BUILD_DIR}/web-greeter/resources/js/bundle.js
59 | bundle/ThemeUtils := ${REPO_DIR}/src/resources/js/ThemeUtils.js
60 | bundle/GreeterComm := ${REPO_DIR}/src/resources/js/GreeterComm.js
61 | bundle/bootstrap := ${REPO_DIR}/src/resources/js/bootstrap.js
62 |
63 | $(bundle.js): $(build/web-greeter) $(bundle/ThemeUtils) $(bundle/bootstrap) $(bundle/GreeterComm)
64 | @cd build/web-greeter/resources/js; \
65 | cat ${bundle/ThemeUtils} ${bundle/bootstrap} ${bundle/GreeterComm} > bundle.js
66 |
67 | resources.py := ${BUILD_DIR}/web-greeter/resources.py
68 |
69 | $(resources.py): $(bundle.js)
70 | @pyrcc5 -o ${BUILD_DIR}/web-greeter/resources.py\
71 | ${BUILD_DIR}/web-greeter/resources/resources.qrc
72 | @cp ${resources.py} src/
73 | @echo "✔ Resources compiled with pyrcc5"
74 |
75 | # Install root, where everything will be copied to
76 | $(INSTALL_ROOT):
77 | @for d in man/man1 metainfo doc/web-greeter web-greeter \
78 | xgreeters applications icons/hicolor/scalable/apps; do \
79 | mkdir -p "${INSTALL_PREFIX}/share/$$d"; \
80 | done
81 | @for d in lightdm xdg/lightdm/lightdm.conf.d; do \
82 | mkdir -p "${INSTALL_ROOT}/etc/$$d"; \
83 | done
84 | @for d in lib/web-greeter bin; do \
85 | mkdir -p "${INSTALL_PREFIX}/$$d"; \
86 | done
87 | @echo "✔ Install root created at ${INSTALL_ROOT}"
88 |
89 | # ZSH completion install
90 | $(zshcompletiondir_local): $(INSTALL_ROOT)
91 | @if [ -n "${zshcompletiondir}" ]; then \
92 | mkdir -p "${zshcompletiondir_local}"; \
93 | cp "${BUILD_DIR}/dist/web-greeter-zsh" "${zshcompletiondir_local}/_web-greeter"; \
94 | echo " ZSH completion copied"; \
95 | fi
96 |
97 | # Bash completion install
98 | $(bashcompletiondir_local): $(INSTALL_ROOT)
99 | @if [ -n "${bashcompletiondir}" ]; then \
100 | mkdir -p "${bashcompletiondir_local}"; \
101 | cp "${BUILD_DIR}/dist/web-greeter-bash" "${bashcompletiondir_local}/web-greeter"; \
102 | echo " Bash completion copied"; \
103 | fi
104 |
105 | build_completions: $(zshcompletiondir_local) $(bashcompletiondir_local)
106 |
107 | # Theme installation
108 | THEMES_DIR := $(abspath ${DESTDIR_PREFIX}/share/web-greeter/themes)
109 | THEMES_DIR_LOCAL := $(abspath ${INSTALL_PREFIX}/share/web-greeter/themes)
110 | themes/gruvbox := $(abspath ${THEMES_DIR_LOCAL}/gruvbox)
111 | themes/gruvbox/js := $(abspath ${REPO_DIR}/themes/themes/gruvbox/js)
112 | themes/dracula := $(abspath ${THEMES_DIR_LOCAL}/dracula)
113 | themes/dracula/js := $(abspath ${REPO_DIR}/themes/themes/dracula/js)
114 | themes/simple := $(abspath ${THEMES_DIR_LOCAL}/simple)
115 | themes/_vendor := $(abspath ${INSTALL_PREFIX}/share/web-greeter/_vendor)
116 |
117 | $(THEMES_DIR_LOCAL): $(INSTALL_ROOT)
118 | @mkdir -p "${THEMES_DIR_LOCAL}"
119 |
120 | $(themes/gruvbox/js): themes/themes/gruvbox/ts
121 | @tsc --build themes/themes/gruvbox
122 | @echo " Gruvbox theme compiled"
123 |
124 | $(themes/dracula/js): themes/themes/dracula/ts
125 | @tsc --build themes/themes/dracula
126 | @echo " Dracula theme compiled"
127 |
128 | $(themes/gruvbox): $(THEMES_DIR_LOCAL) $(themes/gruvbox/js) themes/themes/gruvbox
129 | @if [ -d "${themes/gruvbox}" ]; then \
130 | rm -rf "${themes/gruvbox}"; \
131 | fi
132 | @cp -r "${REPO_DIR}/themes/themes/gruvbox" "${themes/gruvbox}"
133 | @echo " Gruvbox theme copied"
134 | $(themes/dracula): $(THEMES_DIR_LOCAL) $(themes/dracula/js) themes/themes/dracula
135 | @if [ -d "${themes/dracula}" ]; then \
136 | rm -rf "${themes/dracula}"; \
137 | fi
138 | @cp -r "${REPO_DIR}/themes/themes/dracula" "${themes/dracula}"
139 | @echo " Dracula theme copied"
140 | $(themes/simple): $(THEMES_DIR_LOCAL) themes/themes/simple/*
141 | @cp -r "${REPO_DIR}/themes/themes/simple" "${themes/simple}"
142 | @echo " Simple theme copied"
143 |
144 | $(themes/_vendor): $(INSTALL_ROOT) themes/themes/_vendor/*
145 | @cp -r "${REPO_DIR}/themes/themes/_vendor" "${themes/_vendor}"
146 | @echo " Theme vendors copied"
147 |
148 | build_themes: $(themes/gruvbox) $(themes/dracula) $(themes/simple) $(themes/_vendor)
149 |
150 | # Dist files
151 | dist/web-greeter.1 := $(abspath ${DESTDIR_PREFIX}/share/man/man1/web-greeter.1.gz)
152 | dist/news := $(abspath ${DESTDIR_PREFIX}/share/doc/web-greeter/NEWS.gz)
153 | dist/metainfo := $(abspath ${DESTDIR_PREFIX}/share/metainfo/web-greeter.appdata.xml)
154 | dist/xg-desktop := $(abspath ${DESTDIR_PREFIX}/share/xgreeters/web-greeter.desktop)
155 | dist/app-desktop := $(abspath ${DESTDIR_PREFIX}/share/applications/web-greeter.desktop)
156 | dist/app-icon-scalable := $(abspath ${DESTDIR_PREFIX}/share/icons/hicolor/scalable/apps/com.github.jezerm.web-greeter.svg)
157 |
158 | dist_local/web-greeter.1 := $(abspath ${INSTALL_PREFIX}/share/man/man1/web-greeter.1.gz)
159 | dist_local/news := $(abspath ${INSTALL_PREFIX}/share/doc/web-greeter/NEWS.gz)
160 | dist_local/metainfo := $(abspath ${INSTALL_PREFIX}/share/metainfo/web-greeter.appdata.xml)
161 | dist_local/xg-desktop := $(abspath ${INSTALL_PREFIX}/share/xgreeters/web-greeter.desktop)
162 | dist_local/app-desktop := $(abspath ${INSTALL_PREFIX}/share/applications/web-greeter.desktop)
163 | dist_local/app-icon-scalable := $(abspath ${INSTALL_PREFIX}/share/icons/hicolor/scalable/apps/com.github.jezerm.web-greeter.svg)
164 |
165 | $(dist_local/web-greeter.1): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/web-greeter.1
166 | @gzip -c9 "${BUILD_DIR}/dist/web-greeter.1" > \
167 | "${dist_local/web-greeter.1}"
168 |
169 | $(dist_local/news): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/NEWS.md
170 | @gzip -c9 "${BUILD_DIR}/dist/NEWS.md" > \
171 | "${dist_local/news}"
172 |
173 | $(dist_local/metainfo): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/web-greeter.appdata.xml
174 | @cp "${BUILD_DIR}/dist/web-greeter.appdata.xml" \
175 | "${dist_local/metainfo}"
176 |
177 | $(dist_local/xg-desktop): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/web-xgreeter.desktop
178 | @cp "${BUILD_DIR}/dist/web-xgreeter.desktop" \
179 | "${dist_local/xg-desktop}"
180 |
181 | $(dist_local/app-desktop): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/web-greeter.desktop
182 | @cp "${BUILD_DIR}/dist/web-greeter.desktop" \
183 | "${dist_local/app-desktop}"
184 |
185 | $(dist_local/app-icon-scalable): $(build/dist) $(INSTALL_ROOT) ${BUILD_DIR}/dist/com.github.jezerm.web-greeter.svg
186 | @cp "${BUILD_DIR}/dist/com.github.jezerm.web-greeter.svg" \
187 | "${dist_local/app-icon-scalable}"
188 |
189 | build_dist_files: $(dist_local/web-greeter.1) $(dist_local/news) $(dist_local/metainfo) $(dist_local/xg-desktop) $(dist_local/app-desktop) $(dist_local/app-icon-scalable)
190 | @echo "✔ Dist files copied"
191 |
192 | # Config files
193 | config/web-greeter := $(abspath ${DESTDIR}/etc/lightdm/web-greeter.yml)
194 | config/lightdm-wrapper := $(abspath ${DESTDIR}/etc/xdg/lightdm/lightdm.conf.d/90-greeter-wrapper.conf)
195 | config/Xgreeter := $(abspath ${DESTDIR}/etc/lightdm/Xgreeter)
196 |
197 | config_local/web-greeter := $(abspath ${INSTALL_ROOT}/etc/lightdm/web-greeter.yml)
198 | config_local/lightdm-wrapper := $(abspath ${INSTALL_ROOT}/etc/xdg/lightdm/lightdm.conf.d/90-greeter-wrapper.conf)
199 | config_local/Xgreeter := $(abspath ${INSTALL_ROOT}/etc/lightdm/Xgreeter)
200 |
201 | $(config_local/web-greeter): $(INSTALL_ROOT) ${BUILD_DIR}/dist/web-greeter.yml
202 | @cp "${BUILD_DIR}/dist/web-greeter.yml" "${config_local/web-greeter}"
203 | $(config_local/lightdm-wrapper): $(INSTALL_ROOT) ${BUILD_DIR}/dist/90-greeter-wrapper.conf
204 | @cp "${BUILD_DIR}/dist/90-greeter-wrapper.conf" "${config_local/lightdm-wrapper}"
205 | $(config_local/Xgreeter): $(INSTALL_ROOT) ${BUILD_DIR}/dist/Xgreeter ${BUILD_DIR}/Xgreeter.patch /etc/os-release
206 | @install -Dm755 "${BUILD_DIR}/dist/Xgreeter" "${config_local/Xgreeter}"
207 | @case "${DISTRO}" in \
208 | fedora) \
209 | patch -bN "${config_local/Xgreeter}" "${BUILD_DIR}/Xgreeter.patch"; \
210 | ;; \
211 | esac
212 |
213 | build_config: $(config_local/web-greeter) $(config_local/lightdm-wrapper) $(config_local/Xgreeter)
214 | @echo "✔ Config copied"
215 |
216 | build_install_root: $(INSTALL_ROOT) build_dist_files build_config build_themes build_completions
217 |
218 | # Binaries
219 | bin/web-greeter := $(abspath ${DESTDIR}/bin/web-greeter)
220 | bin_local/web-greeter := $(abspath ${INSTALL_PREFIX}/bin/web-greeter)
221 |
222 | bin/screensaver.so := ${BUILD_DIR}/web-greeter/bindings/_screensaver.so
223 | bin/screensaver.c := ${BUILD_DIR}/web-greeter/bindings/screensaver.c
224 |
225 | $(bin/screensaver.so): $(build/web-greeter)
226 | @gcc ${bin/screensaver.c} -o ${bin/screensaver.so} -shared -lX11 -lxcb
227 | @cp ${bin/screensaver.so} src/bindings/
228 | @echo "✔ Screensaver.so compiled"
229 |
230 | $(bin_local/web-greeter): build_install_root $(resources.py) $(bin/screensaver.so)
231 | @rm -rf "${INSTALL_PREFIX}/lib/web-greeter/*"
232 | @cp -R "${BUILD_DIR}/web-greeter"/* "${INSTALL_PREFIX}/lib/web-greeter"
233 | @printf "#!/usr/bin/env bash\npython3 ${DESTDIR_PREFIX}/lib/web-greeter \$$@" > \
234 | "${bin_local/web-greeter}"
235 | @chmod +x "${bin_local/web-greeter}"
236 | @echo "✔ web-greeter binary copied"
237 |
238 | # Useful rules
239 | .PHONY: build
240 | build: $(bin_local/web-greeter)
241 | @echo "✔ Build succeeded"
242 |
243 | .PHONY: install
244 | install: build
245 | [ -e "${DESTDIR}" ] || mkdir -p "${DESTDIR}"
246 | cp -R "${INSTALL_ROOT}"/* "${DESTDIR}"
247 | @echo "✔ Install succeeded"
248 |
249 | # Uninstall everything except themes and web-greeter.yml
250 | uninstall_preserve:
251 | @rm -rf "${DESTDIR_PREFIX}/lib/web-greeter/"
252 | @rm -f "${dist/web-greeter.1}"
253 | @rm -f "${dist/app-desktop}"
254 | @rm -f "${dist/xg-desktop}"
255 | @rm -f "${dist/metainfo}"
256 | @rm -f "${dist/news}"
257 | @rm -f "${dist/app-icon-scalable}"
258 | @rm -f "${config/lightdm-wrapper}"
259 | @rm -f "${config/Xgreeter}"
260 | @rm -f "${bin/web-greeter}"
261 | @if [ -n "${bashcompletiondir}" ]; then \
262 | rm -f "${bashcompletiondir}/web-greeter"; \
263 | fi
264 | @if [ -n "${zshcompletiondir}" ]; then \
265 | rm -f "${zshcompletiondir}/_web-greeter"; \
266 | fi
267 |
268 | # Uninstall everything
269 | uninstall_all: uninstall_preserve
270 | @rm -rf "${DESTDIR_PREFIX}/share/web-greeter/"
271 | @rm -f "${config/web-greeter}"
272 |
273 | .PHONY: uninstall
274 | uninstall: uninstall_preserve
275 | @echo -e " Themes are not uninstalled. Remove them manually or use \`make uninstall_all\`:\
276 | \n${DESTDIR_PREFIX}/share/web-greeter"
277 | @echo -e " web-greeter config was not uninstalled. Remove it manually or use \`make uninstall_all\`:\
278 | \n${config/web-greeter}"
279 |
280 | run: $(resources.py)
281 | python3 src
282 |
283 | run_debug: $(resources.py)
284 | python3 src --debug
285 |
286 | clean:
287 | rm -rf ${INSTALL_ROOT} ${BUILD_DIR}/dist ${BUILD_DIR}/web-greeter
288 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
Web Greeter
10 |
11 | A LightDM greeter made with PyQt5
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | A modern, visually appealing greeter for LightDM, that allows to create web based themes with HTML,
21 | CSS and JavaScript.
22 |
23 | This is a fork of the [Antergos web-greeter](https://github.com/Antergos/web-greeter) that tries to
24 | fix and improve this project for a modern and current use. Due to this, some API changes are
25 | needed, which implies that current themes would need to do changes to work correctly.
26 |
27 | Also, check out [nody-greeter][nody-greeter], a greeter made in **Node.js** with **Electron**!
28 | (Actually, faster than Web Greeter)
29 |
30 | ## [See Live Demo][live_demo]
31 |
32 | Gruvbox and Dracula themes!
33 |
34 | ## Features
35 |
36 | - Create themes with HTML, CSS and JavaScript!
37 | - Should work everywhere.
38 | - JavaScript error handling, allowing to load the default theme.
39 | - Themes could be simple, or very complex.
40 | - Battery and brightness control.
41 | - Tab completion for zsh and bash.
42 |
43 | ## Available distro packages
44 |
45 | ### Arch
46 |
47 | - AUR: https://aur.archlinux.org/packages/web-greeter/
48 |
49 | ```sh
50 | yay -S web-greeter
51 | ```
52 |
53 | ### Ubuntu/Debian
54 |
55 | Download from the [latest release](https://github.com/JezerM/web-greeter/releases/latest) and
56 | install with apt.
57 |
58 | ```sh
59 | apt install ./web-greeter-VER-DISTRO.deb
60 | ```
61 |
62 | ## Dependencies
63 | | | arch | ubuntu | fedora | openSUSE | debian |
64 | |--------------------------|----------------------|-------------------------|---------------------|-----------------------|-------------------------|
65 | |**liblightdm-gobject** |lightdm |liblightdm-gobject-1-dev |lightdm-gobject-devel|typelib-1_0-LightDM-1 |liblightdm-gobject-dev |
66 | |**pygobject** |python-gobject |python3-gi |pygobject3 |python3-gobject |python3-gi |
67 | |**pyqt5** |python-pyqt5 |python3-pyqt5 |python3-qt5 |python3-qt5-devel |python3-pyqt5 |
68 | |**pyqt5-webengine** |python-pyqt5-webengine|python3-pyqt5.qtwebengine|python3-qt5-webengine|python3-qtwebengine-qt5|python3-pyqt5.qtwebengine|
69 | |**python-yaml** |python-ruamel-yaml |python3-ruamel.yaml |python3-ruamel-yaml |python3-ruamel.yaml |python3-ruamel.yaml |
70 | |**python-inotify** |python-pyinotify |python3-pyinotify |python3-inotify |python3-pyinotify |python3-pyinotify |
71 | |**qt5-webengine** |qt5-webengine |libqt5webengine5 |qt5-qtwebengine |libqt5-qtwebengine |libqt5webengine5 |
72 | |**gobject-introspection** |gobject-introspection |gobject-introspection |gobject-introspection|gobject-introspection |gobject-introspection |
73 | |**libxcb** |libxcb |libxcb1-dev |libxcb-devel |libxcb |libxcb1-dev |
74 | |**libx11** |libx11 |libx11-dev |libX11-devel |libX11 |libx11-dev |
75 |
76 | > Note: web-greeter does not work in Fedora. See #19
77 |
78 | ### Build dependencies
79 |
80 | - rsync
81 | - make
82 | - tsc (`npm i -g typescript`)
83 | - pyrcc5 (Should be installed with above dependencies)
84 | - base-devel (build-essential)
85 |
86 | ### PIP
87 | - PyGObject
88 | - PyQt5
89 | - PyQtWebEngine
90 | - ruamel.yaml
91 | - pyinotify
92 |
93 | PIP dependencies are no longer required as long as common dependencies are satisfied. However, you
94 | can install PIP dependencies with:
95 |
96 | ```sh
97 | pip install -r requirements.txt
98 | ```
99 |
100 | > ***NOTE*** If using PIP, be sure to install these dependencies as root. Yet, not recommended.
101 |
102 | ## Download & Install
103 | ```sh
104 | git clone --recursive https://github.com/JezerM/web-greeter.git
105 | cd web-greeter
106 | sudo make install
107 | ```
108 |
109 | This will build **web-greeter** and package all the files to be installed.
110 |
111 | See [latest release][releases].
112 |
113 | ### Uninstall
114 |
115 | Use `sudo make uninstall` to uninstall web-greeter, but preserving web-greeter.yml and themes.
116 | Either, use `sudo make uninstall_all` to remove everything related to web-greeter.
117 |
118 | ## Theme JavaScript API
119 | [Antergos][Antergos] documentation is no longer available, although it is accesible through
120 | [Web Archive][WebArchive]. Current and updated documentation is available at [web-greeter-page/docs][web-greeter-docs].
121 |
122 | You can access the man-pages `man web-greeter` for some documentation and explanation. Also, you can
123 | explore the provided [themes](https://github.com/JezerM/web-greeter-themes/tree/master/themes) for real use cases.
124 |
125 | Additionally, you can install the TypeScript types definitions inside your theme with npm:
126 |
127 | ```sh
128 | npm install nody-greeter-types
129 | ```
130 |
131 | ## Additional features
132 |
133 | ### Brightness control
134 | `acpi` is the only tool needed to control the brightness, besides a compatible device.
135 | This functionality is based on [acpilight][acpilight] replacement for `xbacklight`.
136 |
137 | udev rules are needed to be applied before using it, check [acpilight rules][acpilight_rules].
138 | Then, lightdm will need to be allowed to change backlight values, to do so add lightdm user
139 | to the **video** group: `sudo usermod -a -G video lightdm`
140 |
141 | Enable it inside `/etc/lightdm/web-greeter.yml`
142 |
143 | ### Battery status
144 | `acpi` and `acpi_listen` are the only tools you need (and a battery).
145 | This functionality is based on ["bat" widget][bat_widget] from ["lain" awesome-wm library][lain].
146 |
147 | You can enable it inside `/etc/lightdm/web-greeter.yml`
148 |
149 | ## Debugging
150 | You can run the greeter from within your desktop session if you add the following line to the desktop
151 | file for your session located in `/usr/share/xsessions/`: `X-LightDM-Allow-Greeter=true`.
152 |
153 | You have to log out and log back in after adding that line. Then you can run the greeter
154 | from command line.
155 |
156 | Themes can be opened with a debug console if you set `debug_mode` as `true`
157 | inside `/etc/lightdm/web-greeter.yml`. Or, you could run the `web-greeter` with
158 | the parameter `--debug`. I recommend to use the last one, as it is easier and handy.
159 |
160 | ```sh
161 | web-greeter --debug
162 | ```
163 |
164 | Check `web-greeter --help` for more commands.
165 |
166 | > ***Note:*** Do not use `lightdm --test-mode` as it is not supported.
167 |
168 | ## Troubleshooting
169 |
170 | Before setting **web-greeter** as your LightDM Greeter, you should make sure it does work also with LightDM:
171 |
172 | - Run **web-greeter** as root with `--no-sandbox` flag ("Unable to determine socket to daemon" and "XLib" related errors are expected)
173 | - Run `lightdm --test-mode`. Although it's not supported, if it does work then it could help to debug lightdm.
174 |
175 | ### LightDM crashes and tries to recover over and over again
176 |
177 | LightDM does this when the greeter crashes, so it could mean **web-greeter** was not installed
178 | correctly, or some dependencies were updated/removed after a distro update.
179 |
180 | ### Import errors
181 |
182 | If you see something like this: `ImportError: libQt5WebEngineCore.so.5: undefined symbol: _ZNSt12out_of_rangeC1EPKc, version Qt_5`, check out this [StackOverflow response](https://stackoverflow.com/a/68811630).
183 |
184 | With some PyQt5 import errors like `ModuleNotFoundError: No module named 'PyQt5.QtWebEngineWidgets'`, check out this [GitHub response](https://github.com/spyder-ide/spyder/issues/8952#issuecomment-499418456).
185 |
186 | web-greeter related import errors:
187 |
188 | - `AttributeError: module 'globals' has no attribute 'greeter'` means some exception happened inside the Browser constructor, maybe related to LightDM or PyQt5.
189 | - `ModuleNotFoundError: No module named 'resources'` could mean `path/to/web-greeter-clone/web-greeter/resources.py` was not compiled with pyrcc5 in the build/install methods.
190 |
191 | [antergos]: https://github.com/Antergos "Antergos"
192 | [nody-greeter]: https://github.com/JezerM/nody-greeter "Nody Greeter"
193 | [cx_freeze]: https://github.com/marcelotduarte/cx_Freeze "cx_Freeze"
194 | [acpilight]: https://gitlab.com/wavexx/acpilight/ "acpilight"
195 | [acpilight_rules]: https://gitlab.com/wavexx/acpilight/-/blob/master/90-backlight.rules "udev rules"
196 | [bat_widget]: https://github.com/lcpz/lain/blob/master/widget/bat.lua "Battery widget"
197 | [lain]: https://github.com/lcpz/lain "Lain awesome library"
198 | [WebArchive]: https://web.archive.org/web/20190524032923/https://doclets.io/Antergos/web-greeter/stable "Web Archive"
199 | [web-greeter-docs]: https://web-greeter-page.vercel.app "Documentation"
200 | [live_demo]: https://jezerm.github.io/web-greeter-themes/ "Live Demo"
201 | [releases]: https://github.com/JezerM/web-greeter/releases "Releases"
202 |
--------------------------------------------------------------------------------
/build/DEBIAN/conffiles:
--------------------------------------------------------------------------------
1 | /etc/lightdm/web-greeter.yml
2 | /etc/lightdm/Xgreeter
3 | /etc/xdg/lightdm/lightdm.conf.d/90-greeter-wrapper.conf
4 |
--------------------------------------------------------------------------------
/build/DEBIAN/control:
--------------------------------------------------------------------------------
1 | Package: web-greeter
2 | Version: 3.5.3
3 | Provides: lightdm-greeter
4 | Replaces: lightdm-webkit-greeter
5 | Section: x11
6 | Priority: optional
7 | Homepage: https://github.com/JezerM/web-greeter
8 | Installed-Size: 18068
9 | Architecture: amd64
10 | Maintainer: JezerM
11 | Depends: liblightdm-gobject-1-0, liblightdm-gobject-1-dev, python3, python3-gi, python3-pyqt5, pyqt5-dev, python3-pyinotify, libqt5webengine5, python3-pyqt5.qtwebengine, python3-ruamel.yaml, libxcb1, libx11-6
12 | Description: A modern, visually appealing greeter for LightDM.
13 | Web Greeter utilizes themes built with HTML/CSS/JavaScript for it's login screen. Web Greeter
14 | themes provide modern, visually appealing, and feature-rich login screens. Two themes are
15 | included by default. There is also a growing number of 3rd-Party themes available online.
16 |
--------------------------------------------------------------------------------
/build/DEBIAN/postinst:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Link to the binary
4 | if [ -e '/opt/web-greeter' ]; then
5 | ln -sf '/opt/web-greeter/web-greeter' '/usr/bin/web-greeter'
6 | fi
7 |
8 | update-desktop-database /usr/share/applications || true
9 |
--------------------------------------------------------------------------------
/build/Xgreeter.patch:
--------------------------------------------------------------------------------
1 | --- Xgreeter 2022-12-21 15:40:30.120000813 -0600
2 | +++ Xgreeter-new 2022-12-22 16:32:31.520001039 -0600
3 | @@ -4,4 +4,4 @@
4 |
5 | xsetroot -cursor_name left_ptr
6 |
7 | -exec $@
8 | +exec $@ --no-sandbox
9 |
--------------------------------------------------------------------------------
/build/ci/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:20.04
2 | LABEL maintainer Jezer Mejía
3 |
4 | RUN apt-get update
5 |
6 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata
7 |
8 | RUN DEBIAN_FRONTEND=noninteractive apt-get upgrade -y
9 |
10 | RUN DEBIAN_FRONTEND=noninteractive \
11 | apt-get install -y \
12 | build-essential
13 |
14 | RUN DEBIAN_FRONTEND=noninteractive \
15 | apt-get install -y \
16 | liblightdm-gobject-1-dev \
17 | gobject-introspection \
18 | libgirepository1.0-dev \
19 | libqt5webengine5 \
20 | pyqt5-dev-tools \
21 | libxcb1 \
22 | libx11-6 \
23 | libcairo2
24 |
25 | RUN DEBIAN_FRONTEND=noninteractive \
26 | apt-get install -y \
27 | python3-gi \
28 | python3-pyqt5 \
29 | python3-pyqt5.qtwebengine \
30 | python3-ruamel.yaml \
31 | python3-pyinotify \
32 | python3-pip
33 |
34 | RUN DEBIAN_FRONTEND=noninteractive \
35 | apt-get install -y \
36 | rsync \
37 | sudo
38 |
39 | VOLUME /build
40 | WORKDIR /build
41 |
--------------------------------------------------------------------------------
/build/utils.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | BUILD_DIR="$(realpath $(dirname "${BASH_SOURCE[0]}"))"
4 | REPO_DIR="$(dirname "${BUILD_DIR}")"
5 | INSTALL_ROOT="${BUILD_DIR}/install_root"
6 | PKGNAME='web-greeter'
7 | DESTDIR=''
8 | PREFIX=''
9 |
10 | clean_build_dir() {
11 | find "${BUILD_DIR}" -type f ! -path '**/ci/**' ! -name '*.yml' ! -path "**/DEBIAN/**" ! -name \
12 | utils.sh ! -name setup_log -delete
13 | find "${BUILD_DIR}" -type d ! -name build ! -path '**/ci' -delete 2>/dev/null || true
14 | }
15 |
16 | combine_javascript_sources() {
17 | cd "${BUILD_DIR}/${PKGNAME}/resources/js"
18 | cat ThemeUtils.js \
19 | bootstrap.js > bundle.js
20 | }
21 |
22 | do_build() {
23 | cd "${BUILD_DIR}"
24 |
25 | # Compile Resources
26 | (combine_javascript_sources \
27 | && pyrcc5 -o "${BUILD_DIR}/${PKGNAME}/resources.py" ../resources.qrc \
28 | && cp "${BUILD_DIR}/${PKGNAME}/resources.py" "${REPO_DIR}/src")
29 |
30 | # Create "Zip Application"
31 | (cd "${PKGNAME}" \
32 | && zip -rq ../"${PKGNAME}.zip" . -x '**__pycache__**' 'resources/*' \
33 | && cd - >/dev/null \
34 | && mkdir -p "${INSTALL_ROOT}${PREFIX}"/{bin,share} \
35 | && echo '#!/usr/bin/env python3' >> "${INSTALL_ROOT}${PREFIX}/bin/web-greeter" \
36 | && cat web-greeter.zip >> "${INSTALL_ROOT}${PREFIX}/bin/web-greeter" \
37 | && chmod +x "${INSTALL_ROOT}${PREFIX}/bin/web-greeter")
38 | }
39 |
40 | do_build_freeze() {
41 | cd "${BUILD_DIR}"
42 |
43 | echo "Building web-greeter with cx_freeze..."
44 | python3 "${BUILD_DIR}/${PKGNAME}/setup.py" build >& setup_log
45 | echo "setup.py log inside ${BUILD_DIR}/setup_log"
46 |
47 | mkdir -p "${INSTALL_ROOT}"/opt/web-greeter
48 | mv "${BUILD_DIR}/${PKGNAME}"/dist/* "${INSTALL_ROOT}"/opt/web-greeter/
49 | }
50 |
51 | do_install() {
52 | [[ -e "${DESTDIR}" ]] || mkdir -p "${DESTDIR}"
53 | cp -R "${INSTALL_ROOT}"/* "${DESTDIR}"
54 | if [[ -e "${DESTDIR}"/opt/web-greeter ]]; then
55 | opt_web=$(echo "${DESTDIR}"/opt/web-greeter/web-greeter | sed -E 's/\/\//\//g')
56 | dest_bin=$(echo "${DESTDIR}"/"${PREFIX}"/bin/web-greeter | sed -E 's/\/\//\//g')
57 | ln -sf "${opt_web}" "${dest_bin}"
58 | fi
59 | }
60 |
61 | init_build_dir() {
62 | [[ -e "${BUILD_DIR}/web-greeter" ]] && rm -rf "${BUILD_DIR}/web-greeter"
63 | [[ -e "${BUILD_DIR}/dist" ]] && rm -rf "${BUILD_DIR}/dist"
64 | rsync -a "${REPO_DIR}/src/" "${BUILD_DIR}/web-greeter" --exclude "dist" --exclude "__pycache__"
65 | rsync -a "${REPO_DIR}/dist" "${BUILD_DIR}"
66 | cp "${REPO_DIR}/NEWS.md" "${BUILD_DIR}/dist/NEWS.md"
67 | cp "${REPO_DIR}/README.md" "${BUILD_DIR}/web-greeter/"
68 | }
69 |
70 | prepare_install() {
71 | cd "${BUILD_DIR}"
72 | INSTALL_PREFIX=$(echo ${INSTALL_ROOT}/${PREFIX} | sed -E 's/\/\//\//g')
73 | mkdir -p \
74 | "${INSTALL_PREFIX}"/share/{man/man1,metainfo,doc/web-greeter,web-greeter,xgreeters,applications,zsh/vendor-completions,bash-completion/completions} \
75 | "${INSTALL_ROOT}"/etc/{lightdm,xdg/lightdm/lightdm.conf.d} \
76 | "${INSTALL_PREFIX}"/bin
77 |
78 | # Themes
79 | (cp -R "${REPO_DIR}/themes" "${INSTALL_PREFIX}/share/web-greeter" \
80 | && cd "${INSTALL_PREFIX}/share/web-greeter" \
81 | && mv themes/_vendor .)
82 |
83 | # Man Page
84 | gzip -c9 "${BUILD_DIR}/dist/${PKGNAME}.1" > "${INSTALL_PREFIX}/share/man/man1/${PKGNAME}.1.gz"
85 |
86 | # News
87 | gzip -c9 "${BUILD_DIR}/dist/NEWS.md" > "${INSTALL_PREFIX}/share/doc/web-greeter/NEWS.gz"
88 |
89 | # Command line completions
90 | if [[ -f /usr/bin/bash ]]; then
91 | cp "${BUILD_DIR}/dist/${PKGNAME}-bash" "${INSTALL_PREFIX}/share/bash-completion/completions/${PKGNAME}"
92 | fi
93 | if [[ -f /usr/bin/zsh ]]; then
94 | cp "${BUILD_DIR}/dist/${PKGNAME}-zsh" "${INSTALL_PREFIX}/share/zsh/vendor-completions/_${PKGNAME}"
95 | fi
96 |
97 | # Greeter Config
98 | cp "${BUILD_DIR}/dist/${PKGNAME}.yml" "${INSTALL_ROOT}/etc/lightdm"
99 |
100 | # AppData File
101 | cp "${BUILD_DIR}/dist/${PKGNAME}.appdata.xml" "${INSTALL_PREFIX}/share/metainfo"
102 |
103 | # Greeter desktop File
104 | cp "${BUILD_DIR}/dist/web-xgreeter.desktop" "${INSTALL_PREFIX}/share/xgreeters/web-greeter.desktop"
105 |
106 | # Application desktop File
107 | cp "${BUILD_DIR}/dist/web-greeter.desktop" "${INSTALL_PREFIX}/share/applications/web-greeter.desktop"
108 |
109 | # Xgreeter wrapper
110 | cp "${BUILD_DIR}/dist/90-greeter-wrapper.conf" \
111 | "${INSTALL_ROOT}/etc/xdg/lightdm/lightdm.conf.d/90-greeter-wrapper.conf"
112 |
113 | install -Dm755 "${BUILD_DIR}/dist/Xgreeter" "${INSTALL_ROOT}/etc/lightdm/Xgreeter"
114 |
115 | # Don't install hidden files
116 | find "${INSTALL_ROOT}" -type f -name '.git*' -delete
117 | rm -rf "${INSTALL_PREFIX}/share/web-greeter/themes/default/.tx"
118 |
119 | if [[ "${DESTDIR}" != '/' ]]; then
120 | # Save a list of installed files for uninstall command
121 | find "${INSTALL_ROOT}" -fprint /tmp/.installed_files
122 |
123 | while read _file
124 | do
125 | [[ -d "${_file}" && *'/web-greeter/'* != "${_file}" ]] && continue
126 |
127 | echo "${_file##*/install_root}" >> "${INSTALL_PREFIX}/share/web-greeter/.installed_files"
128 |
129 | done < /tmp/.installed_files
130 |
131 | rm /tmp/.installed_files
132 | fi
133 | }
134 |
135 | do_uninstall() {
136 | # Man Page
137 | DESTDIR_PREFIX=$(echo ${DESTDIR}/${PREFIX} | sed -E 's/\/\//\//g')
138 | rm -f ${DESTDIR_PREFIX}/share/man/man1/web-greeter.1
139 |
140 | # Command line completions
141 | if [[ -f /usr/bin/bash ]]; then
142 | rm -f ${DESTDIR_PREFIX}/share/bash-completion/completions/${PKGNAME}
143 | fi
144 | if [[ -f /usr/bin/zsh ]]; then
145 | rm -f ${DESTDIR_PREFIX}/share/zsh/vendor-completions/_${PKGNAME}
146 | fi
147 |
148 | # Greeter Config
149 | #rm ${DESTDIR}/etc/lightdm/${PKGNAME}.yml
150 |
151 | # Themes
152 | #rm -rf ${DESTDIR_PREFIX}/share/web-greeter
153 |
154 | # AppData File
155 | rm -f ${DESTDIR_PREFIX}/share/metainfo/${PKGNAME}.appdata.xml
156 |
157 | # Greeter desktop file
158 | rm -f ${DESTDIR_PREFIX}/share/xgreeters/web-greeter.desktop
159 |
160 | # Application desktop file
161 | rm -f ${DESTDIR_PREFIX}/share/applications/web-greeter.desktop
162 |
163 | # XGreeter wrapper
164 | rm -f ${DESTDIR}/etc/xdg/lightdm/lightdm.conf.d/90-greeter-wrapper.conf
165 | rm -f ${DESTDIR}/etc/lightdm/Xgreeter
166 |
167 | # Binary
168 | rm -f ${DESTDIR_PREFIX}/bin/web-greeter
169 | [[ -e "${DESTDIR}"/opt/web-greeter ]] && rm -rf ${DESTDIR}/opt/web-greeter
170 |
171 | echo "Themes are not uninstalled. Remove them manually:
172 | ${DESTDIR_PREFIX}/share/web-greeter/"
173 | echo "web-greeter config was not uninstalled. Remove it manually:
174 | ${DESTDIR}/etc/lightdm/${PKGNAME}.yml"
175 | }
176 |
177 | set_config() {
178 | [[ -z "$1" || -z "$2" ]] && return 1
179 |
180 | sed -i "s|'@$1@'|$2|g" \
181 | "${BUILD_DIR}/dist/web-greeter.yml"
182 | }
183 |
184 |
185 |
186 | cd "${REPO_DIR}/build" >/dev/null
187 |
188 | case "$1" in
189 | combine-js)
190 | combine_javascript_sources
191 | ;;
192 |
193 | clean)
194 | clean_build_dir
195 | ;;
196 |
197 | build)
198 | PREFIX="$2"
199 | do_build
200 | ;;
201 |
202 | build_freeze)
203 | PREFIX="$2"
204 | do_build_freeze
205 | ;;
206 |
207 | build-init)
208 | init_build_dir
209 | ;;
210 |
211 | gen-pot)
212 | generate_pot_file
213 | ;;
214 |
215 | install)
216 | DESTDIR="$2"
217 | PREFIX="$3"
218 | do_install
219 | clean_build_dir
220 | ;;
221 |
222 | prepare-install)
223 | PREFIX="$2"
224 | prepare_install
225 | ;;
226 |
227 | uninstall)
228 | DESTDIR="$2"
229 | PREFIX="$3"
230 | do_uninstall
231 | ;;
232 |
233 | set-config)
234 | set_config "$2" "$3"
235 | ;;
236 | esac
237 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | timezone: America/Chicago
3 | services:
4 | - docker
5 |
6 | dependencies:
7 | cache_directories:
8 | - ~/docker
9 | override:
10 | - '[[ -e ~/docker/image2.tar ]] && docker load -i ~/docker/image2.tar || true'
11 | - '[[ -e ~/docker/image2.tar ]] || docker build --rm=false -t antergos/ci-ubuntu build/ci'
12 | - '[[ -e ~/docker/image2.tar ]] || mkdir -p ~/docker; docker save antergos/ci-ubuntu > ~/docker/image2.tar'
13 |
14 | test:
15 | override:
16 | - docker run -v ${PWD}:/build:rw antergos/ci-ubuntu /bin/bash -c "make -j1 install"
17 |
--------------------------------------------------------------------------------
/dist/90-greeter-wrapper.conf:
--------------------------------------------------------------------------------
1 | [Seat:*]
2 | greeter-wrapper=/etc/lightdm/Xgreeter
3 |
--------------------------------------------------------------------------------
/dist/Xgreeter:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # LightDM wrapper to run around greeter X sessions.
4 |
5 | xsetroot -cursor_name left_ptr
6 |
7 | exec $@
8 |
--------------------------------------------------------------------------------
/dist/web-greeter-bash:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # bash completion for web-greeter
3 |
4 | _web-greeter() {
5 | local cur="${COMP_WORDS[COMP_CWORD]}"
6 | local last="${COMP_WORDS[COMP_CWORD - 1]}"
7 | local xpat='!*.jpg'
8 | local options='--debug -d --normal -n --mode --list --theme --help -h --version -v --api-version'
9 |
10 | for word in "${COMP_WORDS[@]}"; do
11 | case "${word}" in
12 | --debug | --normal | -d | -n | --mode)
13 | options=$(echo "${options}" | sed 's/--normal\|-n//g')
14 | options=$(echo "${options}" | sed 's/--debug\|-d//g')
15 | options=$(echo "${options}" | sed 's/--mode//g')
16 | ;;
17 | esac
18 | done
19 |
20 | case "${last}" in
21 | --theme)
22 | _filedir
23 | options=$(ls -1d /usr/share/web-greeter/themes/*/ | cut -c 1- |
24 | rev | cut -c 2- | rev | sort | sed 's/\/usr\/share\/web-greeter\/themes\///')
25 | ;;
26 | --mode)
27 | options="debug normal"
28 | ;;
29 | esac
30 |
31 | COMPREPLY+=( $(compgen -W "${options}" -- "${cur}") )
32 | }
33 |
34 | complete -F _web-greeter web-greeter
35 |
--------------------------------------------------------------------------------
/dist/web-greeter-zsh:
--------------------------------------------------------------------------------
1 | #compdef web-greeter
2 |
3 | _webgreeter() {
4 | integer ret=1
5 | local -a args
6 | local themes=$(ls -1d /usr/share/web-greeter/themes/*/ | cut -c 1- |
7 | rev | cut -c 2- | rev | sort | sed 's/\/usr\/share\/web-greeter\/themes\///')
8 | args+=(
9 | "(-d -n --debug --normal)--mode[Set browser mode]:mode:->modes"
10 | "(--debug -d --normal -n --mode)"{--debug,-d}"[Runs the greeter in debug mode]"
11 | "(--normal -n --debug -d --mode)"{--normal,-n}"[Runs in non-debug mode]"
12 | '--list[Lists available themes]'
13 | "--theme[Sets the theme to use]:theme:->themes"
14 | "(--help -h)"{--help,-h}"[Show help]"
15 | "(--version -v)"{--version,-v}"[Print program version]"
16 | "--api-version[Print JavaScript API version number]"
17 | )
18 | _arguments $args[@] && ret=0
19 | case "$state" in
20 | themes)
21 | _files
22 | _values 'themes' "${(uonzf)${themes}}"
23 | ;;
24 | modes)
25 | _values 'modes' "debug" "normal"
26 | ;;
27 | esac
28 | return ret
29 | }
30 |
31 | _webgreeter
32 |
--------------------------------------------------------------------------------
/dist/web-greeter.1:
--------------------------------------------------------------------------------
1 | .TH "web-greeter" "1" "2022.2.10"
2 | .nh
3 | .ad l
4 | .SH "NAME"
5 | web-greeter
6 | .SH "SYNOPSIS"
7 | .PP
8 | LightDM greeter that uses chromium for theming via HTML/JavaScript\&.
9 | .PP
10 | .SH "DESCRIPTION"
11 | .PP
12 | web-greeter is a LightDM greeter that uses chromium for theming\&. Themes can be written
13 | using a combination of HTML and Javascript\&.
14 | .PP
15 | .SH "OPTIONS"
16 | .TP
17 | \fB\-h, \-\-help\fR
18 | Shows the help
19 | .TP
20 | \fB\-v, \-\-version\fR
21 | Print program version
22 | .TP
23 | \fB\-\-debug\fR
24 | Forces the greeter to run in debug mode
25 | .TP
26 | \fB\-\-normal\fR
27 | Forces the greeter to run in normal mode
28 | .TP
29 | \fB\-\-list\fR
30 | Shows the available themes
31 | .TP
32 | \fB\-\-theme\ \fITHEME\fR
33 | Sets the theme to use
34 | .PP
35 | .SH "THEME JAVASCRIPT API"
36 | Please note that all properties and functions which are marked as "deprecated" are
37 | only available for backwards compatibility and will be removed in a future version of
38 | web-greeter\&. Theme authors should not use any deprecated properties or
39 | functions in new works and should update any existing works which make use of
40 | deprecated properties and/or functions to ensure continued proper functionality\&.
41 | .PP
42 | See full documentation on https://jezerm\&.github\&.io/web-greeter/
43 | .PP
44 | The following signals are available to connect javascript functions when a LightDM
45 | or web-greeter signal occurs:
46 | .PP
47 | \fBlightdm.authentication_complete\fR
48 | .RS 4
49 | Gets emitted when the greeter has completed authentication\&.
50 | .RE
51 | .PP
52 | \fBlightdm.autologin_timer_expired\fR
53 | .RS 4
54 | Gets emitted when the automatic login timer has expired\&.
55 | .RE
56 | .PP
57 | \fBlightdm.show_message\fR
58 | .RS 4
59 | Gets emitted when the greeter should show a message to the user\&.
60 | This signal emits a \fBmessage: string\fR and a \fBtype: number\fR\&.
61 | .RE
62 | .PP
63 | \fBlightdm.show_prompt\fR
64 | .RS 4
65 | Gets emitted when the greeter should show a prompt to the user\&.
66 | This signal emits a \fBmessage: string\fR and a \fBtype: number\fR\&.
67 | .RE
68 | .PP
69 | The following functions are available for the greeter to call to execute
70 | actions within LightDM:
71 | .PP
72 | \fBlightdm\&.authenticate(username)\fR
73 | .RS 4
74 | Specifies the username of the user we'd like to start authenticating as\&. Note that
75 | if you call lightdm.authenticate with no argument, LightDM (via PAM) will issue
76 | a show_prompt() call to ask for the username\&.
77 | .RE
78 | .PP
79 | \fBlightdm\&.authenticate_as_guest()\fR
80 | .RS 4
81 | Authenticates as the guest user\&.
82 | .RE
83 | .PP
84 | \fBlightdm\&.cancel_authentication()\fR
85 | .RS 4
86 | Cancels the authentication of any user currently in the process of
87 | authenticating\&.
88 | .RE
89 | .PP
90 | \fBlightdm\&.cancel_autologin()\fR
91 | .RS 4
92 | Cancels the authentication of the autologin user\&.
93 | .RE
94 | .PP
95 | \fBlightdm\&.start_session(session)\fR
96 | .RS 4
97 | Once LightDM has successfully authenticated the user, start the user's session
98 | by calling this function\&. "session" is the authenticated user's session\&.
99 | If no session is passed, start the authenticated user with the system default
100 | session.
101 | .RE
102 | .PP
103 | \fBlightdm\&.respond(text)\fR
104 | .RS 4
105 | When LightDM has prompted for input, provide the response to LightDM\&.
106 | .RE
107 | .PP
108 | \fBlightdm\&.set_language(lang)\fR
109 | .RS 4
110 | Will set the language for the current LightDM session\&.
111 | .RE
112 | .PP
113 | \fBlightdm\&.shutdown()\fR
114 | .RS 4
115 | Shuts down the system, if the greeter has the authority to do so\&.
116 | Check if greeter can shutdown with \fBlightdm\&.can_shutdown\fR
117 | .RE
118 | .PP
119 | \fBlightdm\&.restart()\fR
120 | .RS 4
121 | Restarts the system, if the greeter has the authority to do so\&.
122 | Check if greeter can restart with \fBlightdm\&.can_restart\fR
123 | .RE
124 | .PP
125 | \fBlightdm\&.suspend()\fR
126 | .RS 4
127 | Suspends the system, if the greeter has the authority to do so\&.
128 | Check if greeter can suspend with \fBlightdm\&.can_suspend\fR
129 | .RE
130 | .PP
131 | \fBlightdm\&.hibernate()\fR
132 | .RS 4
133 | Hibernates the system, if the greeter has the authority to do so\&.
134 | Check if greeter can hibernate with \fBlightdm\&.can_hibernate\fR
135 | .RE
136 | .PP
137 | Variables available within the greeter are:
138 | .PP
139 | \fBlightdm\&.authentication_user\fR: string
140 | .RS 4
141 | The username of the authentication user being authenticated or null if no
142 | authentication is in progress\&.
143 | .RE
144 | .PP
145 | \fBlightdm\&.autologin_guest\fR: boolean
146 | .RS 4
147 | Indicates the guest user should be used for autologin\&.
148 | .RE
149 | .PP
150 | \fBlightdm\&.autologin_timeout\fR: number
151 | .RS 4
152 | The number of seconds to wait before automatically logging in\&.
153 | .RE
154 | .PP
155 | \fBlightdm\&.autologin_user\fR: string
156 | .RS 4
157 | The name of the user account that should be logged into
158 | automatically after timed login delay has passed\&.
159 | .RE
160 | .PP
161 | \fBlightdm\&.can_hibernate\fR: boolean
162 | .RS 4
163 | Whether or not the system can be made to hibernate by the greeter\&.
164 | .RE
165 | .PP
166 | \fBlightdm\&.can_restart\fR: boolean
167 | .RS 4
168 | Whether or not the system can be restarted by the greeter\&.
169 | .RE
170 | .PP
171 | \fBlightdm\&.can_shutdown\fR: boolean
172 | .RS 4
173 | Whether or not the system can be shutdown by the greeter\&.
174 | .RE
175 | .PP
176 | \fBlightdm\&.can_suspend\fR: boolean
177 | .RS 4
178 | Whether or not the system can be suspended by the greeter\&.
179 | .RE
180 | .PP
181 | \fBlightdm\&.default_session\fR: string
182 | .RS 4
183 | The name of the default session (as configured in lightdm.conf)\&.
184 | .RE
185 | .PP
186 | \fBlightdm\&.has_guest_account\fR: boolean
187 | .RS 4
188 | A guest account is available for login\&.
189 | .RE
190 | .PP
191 | \fBlightdm\&.hide_users_hint\fR: boolean
192 | .RS 4
193 | The whole list of users should not be displayed\&.
194 | .RE
195 | .PP
196 | \fBlightdm\&.hostname\fR: string
197 | .RS 4
198 | The hostname of the system\&.
199 | .RE
200 | .PP
201 | \fBlightdm\&.is_authenticated\fR: boolean
202 | .RS 4
203 | Indicates if the user has successfully authenticated\&.
204 | .RE
205 | .PP
206 | \fBlightdm\&.in_authentication\fR: boolean
207 | .RS 4
208 | Indicates if lightdm is currently in the authentication phase\&.
209 | .RE
210 | .PP
211 | \fBlightdm\&.language\fR: LightDM.Language | null
212 | .RS 4
213 | The currently selected language\&.
214 | .RE
215 | .PP
216 | \fBlightdm\&.languages\fR: LightDM.Languages[]
217 | .RS 4
218 | The languages that are available on the system\&.
219 | .RE
220 | .PP
221 | \fBlightdm\&.layout\fR: LightDM.Layout
222 | .RS 4
223 | The currently active layout for the selected user.
224 | .RE
225 | .PP
226 | \fBlightdm\&.layouts\fR: LightDM.Layout[]
227 | .RS 4
228 | The keyboard layouts that are available on the system\&.
229 | .RE
230 | .PP
231 | \fBlightdm\&.select_guest_hint\fR: boolean
232 | .RS 4
233 | The guest user should be selected by default for login\&.
234 | .RE
235 | .PP
236 | \fBlightdm\&.select_user_hint\fR: string
237 | .RS 4
238 | The username that should be selected by default for login\&.
239 | .RE
240 | .PP
241 | \fBlightdm\&.sessions\fR: LightDM.Session[]
242 | .RS 4
243 | The sessions that are available on the system\&.
244 | .RE
245 | .PP
246 | \fBlightdm\&.users\fR: LightDM.User[]
247 | .RS 4
248 | The users that are able to log in\&. Returns an Array of LightDMUser
249 | objects\&.
250 | .RE
251 | .PP
252 | The \fBtheme_utils\fR object has some utility functions associated with it which
253 | are intended to make a theme author's work easier\&.
254 | .PP
255 | \fBtheme_utils\&.dirlist(path)\fR
256 | .RS 4
257 | Returns an array of strings of filenames present at "path", or Null if the
258 | path does not exist\&.
259 | .RE
260 | .PP
261 | \fBtheme_utils\&.bind_this(context)\fR
262 | .RS 4
263 | Binds this to class, context, for all of the class's methods\&.
264 | .RE
265 | .PP
266 | \fBtheme_utils\&.get_current_localized_time()\fR
267 | .RS 4
268 | Get the current time in a localized format\&. Language is auto-detected by default,
269 | but can be set manually in the greeter config file\&.
270 | .RE
271 | \fBtheme_utils\&.get_current_localized_date()\fR
272 | .RS 4
273 | Get the current date in a localized format\&. Language is auto-detected by default,
274 | but can be set manually in the greeter config file\&.
275 | .RE
276 | .PP
277 | Please see the LightDM API documentation for the complete list of calls
278 | available\&. The web-greeter implements all of the LightDM API\&.
279 | .PP
280 | .SH "CONFIGURATION"
281 | .PP
282 | \fB/etc/lightdm/web-greeter\&.yml\fR
283 | .RS 4
284 | Configuration file\&.
285 | .RE
286 | .SH "FILES"
287 | .PP
288 | \fB/usr/share/web-greeter/themes\fR
289 | .RS 4
290 | Directory where themes should be stored\&.
291 | .RE
292 | .SH "EXAMPLES"
293 | .PP
294 | Please see the "dracula", "gruvbox" and "simple" themes that are shipped with web-greeter\&.
295 | .TP
296 | \fBCommand Line\fR
297 | .RS 4
298 | web-greeter --theme simple --debug
299 | .TP
300 | web-greeter --normal
301 | .SH "SEE ALSO"
302 | .PP
303 | http://people\&.ubuntu\&.com/~robert-ancell/lightdm/reference/
304 | .PP
305 | https://lazka\&.github\&.io/pgi-docs/#LightDM-1
306 | .PP
307 | https://jezerm\&.github\&.io/web-greeter/
308 | .PP
309 | https://github.com/JezerM/web-greeter
310 | .SH "AUTHOR"
311 | .PP
312 | The legacy lightdm-webkit-greeter was written by Robert Ancell \&.
313 | It was ported to webkit2 by the Antergos Developers \&. Also includes code improvements
314 | contributed by Scott Balneaves \&. Forked and mantained by JezerM \&.
315 |
--------------------------------------------------------------------------------
/dist/web-greeter.appdata.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | web-greeter
5 | CC0-1.0
6 | GPL-3.0+
7 | Web Greeter
8 | A modern, visually appealing greeter for LightDM
9 |
10 |
11 | Web Greeter for LightDM utilizes themes built with HTML/CSS/JavaScript for it's login screen.
12 |
13 |
14 | Web Greeter themes provide modern, visually appealing, and feature-rich
15 | login screens. Three themes are included by default. There is also a growing number of
16 | 3rd-Party themes available online.
17 |
18 |
19 |
20 |
21 |
22 | https://github.com/JezerM/web-greeter/raw/master/themes/gruvbox/assets/screenshots/theme-show-1.png
23 | Login screen (gruvbox theme)
24 |
25 |
26 | https://github.com/JezerM/web-greeter/raw/master/themes/gruvbox/assets/screenshots/theme-show-2.png
27 | Login screen shutting down (gruvbox theme)
28 |
29 |
30 | https://github.com/JezerM/web-greeter/raw/master/themes/gruvbox/assets/screenshots/theme-show-3.png
31 | Background selector (gruvbox theme)
32 |
33 |
34 | https://github.com/JezerM/web-greeter/raw/master/themes/dracula/assets/screenshots/theme-show-1.png
35 | Login screen (dracula theme)
36 |
37 |
38 | https://github.com/JezerM/web-greeter/raw/master/themes/dracula/assets/screenshots/theme-show-2.png
39 | Login screen background selector (dracula theme)
40 |
41 |
42 | https://github.com/JezerM/web-greeter/raw/master/themes/dracula/assets/screenshots/theme-show-3.png
43 | Login screen success message (dracula theme)
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 | Changes:
55 |
56 | - Fixed ruamel.yaml usage
57 |
58 |
59 |
60 |
61 |
62 | Changes:
63 |
64 | - Update themes submodule.
65 | - Update openSUSE dependencies.
66 | - Fix GreeterReady being dispatched earlier or never.
67 | - Possibly issue #19 has been solved, so it could be executed in Fedora.
68 | - Fix crash when a Dialog is created before the Application itself.
69 |
70 |
71 |
72 |
73 |
74 | Added:
75 |
76 | - Added --api-version command line argument, starting at 1.0.0
77 |
78 | Changes:
79 |
80 | - Removed themes in favor of web-greeter-themes submodule.
81 | - Reduced signal execution to 60ms, which should make web-greeter to feel faster.
82 |
83 |
84 |
85 |
86 |
87 | Added:
88 |
89 | - Added multi-monitor support from nody-greeter
90 | - Added "greeter_comm" object for cross-window communication
91 | - Added "GreeterBroadcastEvent" for listening to greeter_comm messages
92 |
93 | Changes:
94 |
95 | - Default themes migrated to Typescript
96 | - Added background selector to gruvbox theme
97 | - tsc (typescript compiler) is now a required dependency
98 | - "lightdm.batteryData" deprecated in favor of "lightdm.battery_data"
99 | - Command line arguments updated
100 | - Updated bash and zsh completions
101 | - Added Web Greeter scalable icon
102 | - Lots of bugfixes
103 |
104 |
105 |
106 |
107 |
108 | Changes:
109 |
110 | - Fix build process
111 | - Fix typos in README
112 | - Handle lightdm.start_session errors
113 | - Fix positioning on multiple screens
114 |
115 |
116 |
117 |
118 |
119 | Added:
120 |
121 | - Added top Menu-bar menus and items
122 | - Improve devtools qdock behavior
123 |
124 | Changes:
125 |
126 | - Bugfixes related to LightDM signals
127 | - Fix web-greeter initialization issues
128 | - Migrate build process from utils.sh script to full Makefile
129 | - Man-pages updated
130 |
131 | Removed:
132 |
133 | - Remove python-xlib dependency in favor of python C binding
134 |
135 |
136 |
137 |
138 |
139 | Changes:
140 |
141 | - Bugfixes related to LightDM signals
142 | - Allow --no-sandbox argument
143 | - Allow relative path with theme_utils.dirlist
144 | - Added brightness controller to use instead of external programs
145 |
146 |
147 |
148 |
149 |
150 | Changes:
151 |
152 | - Bugfixes related to older Qt versions
153 |
154 |
155 |
156 |
157 |
158 | Changes:
159 |
160 | - Default build system reverted to zippy method
161 | - Battery bugfixes
162 | - QWebChannel and Web Greeter bundle merged as one file
163 | - Lots of bugfixes
164 |
165 |
166 |
167 |
168 |
169 | Bugfixes
170 | Changes:
171 |
172 | - Bugfixes, just bugfixes
173 |
174 |
175 |
176 |
177 |
178 | Web Greeter 3.1.0 is here!
179 | The build system changed to cx_freeze, though the previous build/install method, can be used.
180 | Added:
181 |
182 | - Devtools implemented as a side view
183 | - Build system now uses cx_freeze
184 | - Added keyboard layout selector, and eye password reveal in both themes
185 |
186 | Changes:
187 |
188 | - Brightness and battery are now controlled by signals instead of timers
189 | - Old build system (zip build) is still usable with `build_old` and `install_old`
190 |
191 | Removed:
192 |
193 | - whither dependency removed
194 |
195 |
196 |
197 |
198 |
199 | Finally, Web Greeter 3.0.0 is ready!
200 | Added:
201 |
202 | - New themes: gruvbox and dracula
203 | - Added newer documentation
204 | - Support for brightness control
205 | - Support for battery status
206 | - Support for ES2020, as using Chrome 83
207 | - Improved mock.js system
208 | - Better debug logging
209 | - Custom cursor theme option as "icon_theme"
210 | - Some vendors added
211 | - Tab completion for "web-greeter" command
212 |
213 | Changed:
214 |
215 | - "lightdm-webkit2-greeter" name changed to "web-greeter"
216 | - "webkit2Gtk" replaced with "PyQtWebEngine"
217 | - Man-pages updated
218 | - Updated API usage for LightDM 1.26.0
219 | - "greeterutil" renamed to "theme_utils"
220 | - "config" renamed to "greeter_config"
221 | - "lightdm-webkit2-greeter.conf" renamed to "web-greeter.yml"
222 | - Themes are now installed inside "/usr/share/web-greeter/themes"
223 | - Vendors updated
224 | - Previous deprecated methods and properties were removed
225 |
226 | Removed:
227 |
228 | - Antergos theme removed
229 | - Some vendors removed
230 | - "time_format" config option removed
231 | - Transifex removed, sadly
232 |
233 |
234 |
235 |
236 |
237 | This is a hotfix release in the 2.2 series, with the following improvements:
238 |
239 | - Implement workaround to prevent the web process from crashing in webkit2gtk 2.14.3. (GH #107)
240 |
241 |
242 |
243 |
244 |
245 | This is a maintenance release in the 2.2 series, with the following improvements:
246 |
247 | - Increased the timeout for the "theme loaded" check to ensure themes are given enough time to load (when running on less powerful systems). (GH #98)
248 | - Fixed issue where users' custom .face image failed to load. (GH #98)
249 |
250 |
251 |
252 |
253 |
254 | This is a milestone release with the following improvements:
255 |
256 | - The JavaScript API for themes is now fully documented
257 | - New Theme Error Recovery System that will alert the user when errors are detected during JavaScript execution and give them the option to to load a fallback theme.
258 | - New config option: secure_mode (enabled by default). When enabled, only local http requests are allowed in themes. All non-local requests will be blocked.
259 | - It is now possible to override the language and format used by the greeter when displaying the current time. See the greeter config file for details.
260 | - A new utility method for getting the current localized time is available to themes.
261 | - Simple theme now has a fade out exit animation.
262 |
263 |
264 | - Switched build systems from Autotools to Meson.
265 | - Updated API usage for LightDM 1.19.2+.
266 | - Updated bundled JS & CSS vendor libs to their latest versions.
267 | - Updated translations with latest changes contributed by the Antergos Community on Transifex.
268 | - Buttons and user list-box received some minor style enhancements. (Default theme)
269 | - Theme is now compatible with the latest jQuery. (Default theme)
270 | - Removed deprecated HTML4 tags. (Simple theme)
271 | - Improved styles for the input field. (Simple theme)
272 |
273 |
274 | - The ugly default X cursor will no longer be shown after the greeter exits.
275 | - The error messages container will now appear correctly (size and position). (Default theme)
276 | - It is now once again possible to skip straight to password entry by pressing either the spacebar or the enter key. (Default theme)
277 |
278 |
279 |
280 |
281 | https://github.com/JezerM/web-greeter/issues
282 | https://jezerm.github.io/web-greeter/
283 | https://github.com/JezerM/web-greeter
284 |
285 | apps.light-locker
286 |
287 |
288 | web-greeter
289 |
290 | amyuki4@gmail.com
291 | web-greeter
292 | JezerM
293 |
294 |
295 |
--------------------------------------------------------------------------------
/dist/web-greeter.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=web-greeter
3 | Exec=/opt/web-greeter/web-greeter %U
4 | Terminal=false
5 | Type=Application
6 | Icon=com.github.jezerm.web-greeter
7 | StartupWMClass=web-greeter
8 | Comment=Web based greeter for lightdm
9 | Categories=System;
10 |
--------------------------------------------------------------------------------
/dist/web-greeter.yml:
--------------------------------------------------------------------------------
1 | #
2 | # branding:
3 | # background_images_dir: Path to directory that contains background images for use by themes.
4 | # logo_image: Path to logo image for use by greeter themes.
5 | # user_image: Default user image/avatar. This is used by themes when user has no .face image.
6 | #
7 | # NOTE: Paths must be accessible to the lightdm system user account (so they cannot be anywhere in /home)
8 | #
9 | branding:
10 | background_images_dir: /usr/share/backgrounds
11 | logo_image: /usr/share/web-greeter/themes/default/img/antergos-logo-user.png
12 | user_image: /usr/share/web-greeter/themes/default/img/antergos.png
13 |
14 | #
15 | # greeter:
16 | # debug_mode: Enable debug mode for the greeter as well as greeter themes.
17 | # detect_theme_errors: Provide an option to load a fallback theme when theme errors are detected.
18 | # screensaver_timeout: Blank the screen after this many seconds of inactivity.
19 | # secure_mode: Don't allow themes to make remote http requests.
20 | # theme: Greeter theme to use.
21 | # icon_theme: Icon/cursor theme to use, located in /usr/share/icons/, i.e. "Adwaita". Set to None to use default icon theme.
22 | # time_language: Language to use when displaying the date or time, i.e. "en-us", "es-419", "ko", "ja". Set to None to use system's language.
23 | #
24 | # NOTE: See IANA subtags registry for time_language options: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
25 | #
26 | greeter:
27 | debug_mode: False
28 | detect_theme_errors: True
29 | screensaver_timeout: 300
30 | secure_mode: True
31 | theme: gruvbox
32 | icon_theme:
33 | time_language:
34 |
35 | #
36 | # layouts A list of preferred layouts to use
37 | # - us "en_us" xkb layout
38 | # - latam "es_latam" xkb layout
39 | # - gb dvorak "en_gb_dvorak" xkb layout
40 | #
41 | # NOTE: See "man xkeyboard-config" for posible layout values. Also, see posible layouts here: https://web.archive.org/web/20161203032703/http://pastebin.com/v2vCPHjs
42 | # A layout value is composed in the main layout, like "us" or "latam", and its variant, like "dvorak", "colemak", "mac" or "mac_intl"
43 | #
44 | layouts:
45 | - us
46 | - latam
47 |
48 | #
49 | # features:
50 | # battery: Enable greeter and themes to get battery status.
51 | # backlight:
52 | # enabled: Enable greeter and themes to control display backlight.
53 | # value: The amount to increase/decrease brightness by greeter.
54 | # steps: How many steps are needed to do the change. 0 for instant change.
55 | #
56 | features:
57 | battery: False
58 | backlight:
59 | enabled: False
60 | value: 10
61 | steps: 0
62 |
--------------------------------------------------------------------------------
/dist/web-xgreeter.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Web Greeter
3 | Comment=Greeter for LightDM
4 | Exec=web-greeter
5 | Type=Application
6 | X-Ubuntu-Gettext-Domain=web-greeter
7 |
--------------------------------------------------------------------------------
/docs/Greeter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * LightDMGreeter.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | /**
30 | * Base class for the greeter's Theme JavaScript API. Greeter themes will interact
31 | * directly with an object derived from this class to facilitate the user log-in process.
32 | * The greeter will automatically create an instance when it starts.
33 | * The instance can be accessed using the global variable: `lightdm`.
34 | *
35 | * @memberOf LightDM
36 | */
37 | class Greeter {
38 |
39 | constructor() {
40 | if ( 'lightdm' in window ) {
41 | return window.lightdm;
42 | }
43 |
44 | window.lightdm = ThemeUtils.bind_this( this );
45 |
46 | return window.lightdm;
47 | }
48 |
49 | /**
50 | * The username of the user being authenticated or {@link null}
51 | * if no authentication is in progress
52 | * @type {String|Null}
53 | * @readonly
54 | */
55 | get authentication_user() {}
56 |
57 | /**
58 | * Whether or not the guest account should be automatically logged
59 | * into when the timer expires.
60 | * @type {Boolean}
61 | * @readonly
62 | */
63 | get autologin_guest() {}
64 |
65 | /**
66 | * The number of seconds to wait before automatically logging in.
67 | * @type {Number}
68 | * @readonly
69 | */
70 | get autologin_timeout() {}
71 |
72 | /**
73 | * The username with which to automattically log in when the timer expires.
74 | * @type {String}
75 | * @readonly
76 | */
77 | get autologin_user() {}
78 |
79 | /**
80 | * The battery data
81 | * @type {Battery}
82 | * @readonly
83 | */
84 | get batteryData() {}
85 |
86 | /**
87 | * The display brightness
88 | * @type {Number}
89 | */
90 | get brightness() {}
91 | set brightness( quantity ) {}
92 |
93 | /**
94 | * Whether or not the greeter can access to battery data.
95 | * @type {boolean}
96 | * @readonly
97 | */
98 | get can_access_battery() {}
99 |
100 | /**
101 | * Whether or not the greeter can control display brightness.
102 | * @type {boolean}
103 | * @readonly
104 | */
105 | get can_access_brightness() {}
106 |
107 | /**
108 | * Whether or not the greeter can make the system hibernate.
109 | * @type {Boolean}
110 | * @readonly
111 | */
112 | get can_hibernate() {}
113 |
114 | /**
115 | * Whether or not the greeter can make the system restart.
116 | * @type {Boolean}
117 | * @readonly
118 | */
119 | get can_restart() {}
120 |
121 | /**
122 | * Whether or not the greeter can make the system shutdown.
123 | * @type {Boolean}
124 | * @readonly
125 | */
126 | get can_shutdown() {}
127 |
128 | /**
129 | * Whether or not the greeter can make the system suspend/sleep.
130 | * @type {Boolean}
131 | * @readonly
132 | */
133 | get can_suspend() {}
134 |
135 | /**
136 | * The name of the default session.
137 | * @type {String}
138 | * @readonly
139 | */
140 | get default_session() {}
141 |
142 | /**
143 | * Whether or not guest sessions are supported.
144 | * @type {Boolean}
145 | * @readonly
146 | */
147 | get has_guest_account() {}
148 |
149 | /**
150 | * Whether or not user accounts should be hidden.
151 | * @type {boolean}
152 | * @readonly
153 | */
154 | get hide_users_hint() {}
155 |
156 | /**
157 | * The system's hostname.
158 | * @type {String}
159 | * @readonly
160 | */
161 | get hostname() {}
162 |
163 | /**
164 | * Whether or not the greeter is in the process of authenticating.
165 | * @type {Boolean}
166 | * @readonly
167 | */
168 | get in_authentication() {}
169 |
170 | /**
171 | * Whether or not the greeter has successfully authenticated.
172 | * @type {Boolean}
173 | * @readonly
174 | */
175 | get is_authenticated() {}
176 |
177 | /**
178 | * The current language or {@link null} if no language.
179 | * @type {Language|Null}
180 | * @readonly
181 | */
182 | get language() {}
183 |
184 | /**
185 | * A list of languages to present to the user.
186 | * @type {Language[]}
187 | * @readonly
188 | */
189 | get languages() {}
190 |
191 | /**
192 | * The currently active layout for the selected user.
193 | * @type {Layout}
194 | */
195 | get layout() {}
196 | set layout(layout) {}
197 |
198 | /**
199 | * A list of keyboard layouts to present to the user.
200 | * @type {Layout[]}
201 | * @readonly
202 | */
203 | get layouts() {}
204 |
205 | /**
206 | * Whether or not the greeter was started as a lock screen.
207 | * @type {Boolean}
208 | * @readonly
209 | */
210 | get lock_hint() {}
211 |
212 | /**
213 | * Whether or not the guest account should be selected by default.
214 | * @type {Boolean}
215 | * @readonly
216 | */
217 | get select_guest_hint() {}
218 |
219 | /**
220 | * The username to select by default.
221 | * @type {String}
222 | * @readonly
223 | */
224 | get select_user_hint() {}
225 |
226 | /**
227 | * List of available sessions.
228 | * @type {Session[]}
229 | * @readonly
230 | */
231 | get sessions() {}
232 |
233 | /**
234 | * Check if a manual login option should be shown. If {@link true}, the theme should
235 | * provide a way for a username to be entered manually. Otherwise, themes that show
236 | * a user list may limit logins to only those users.
237 | * @type {Boolean}
238 | * @readonly
239 | */
240 | get show_manual_login_hint() {}
241 |
242 | /**
243 | * Check if a remote login option should be shown. If {@link true}, the theme should provide
244 | * a way for a user to log into a remote desktop server.
245 | * @type {Boolean}
246 | * @readonly
247 | * @internal
248 | */
249 | get show_remote_login_hint() {}
250 |
251 | /**
252 | * List of available users.
253 | * @type {User[]}
254 | * @readonly
255 | */
256 | get users() {}
257 |
258 |
259 | /**
260 | * Starts the authentication procedure for a user.
261 | *
262 | * @param {String|Null} username A username or {@link null} to prompt for a username.
263 | */
264 | authenticate( username ) {}
265 |
266 | /**
267 | * Starts the authentication procedure for the guest user.
268 | */
269 | authenticate_as_guest() {}
270 |
271 | /**
272 | * Updates the battery data
273 | */
274 | batteryUpdate() {}
275 |
276 | /**
277 | * Set the brightness to quantity
278 | * @param {Number} quantity The quantity to set
279 | */
280 | brightnessSet( quantity ) {}
281 |
282 | /**
283 | * Increase the brightness by quantity
284 | * @param {Number} quantity The quantity to increase
285 | */
286 | brightnessIncrease( quantity ) {}
287 |
288 | /**
289 | * Decrease the brightness by quantity
290 | * @param {Number} quantity The quantity to decrease
291 | */
292 | brightnessDecrease( quantity ) {}
293 |
294 | /**
295 | * Cancel user authentication that is currently in progress.
296 | */
297 | cancel_authentication() {}
298 |
299 | /**
300 | * Cancel the automatic login.
301 | */
302 | cancel_autologin() {}
303 |
304 | /**
305 | * Triggers the system to hibernate.
306 | * @returns {Boolean} {@link true} if hibernation initiated, otherwise {@link false}
307 | */
308 | hibernate() {}
309 |
310 | /**
311 | * Provide a response to a prompt.
312 | * @param {*} response
313 | */
314 | respond( response ) {}
315 |
316 | /**
317 | * Triggers the system to restart.
318 | * @returns {Boolean} {@link true} if restart initiated, otherwise {@link false}
319 | */
320 | restart() {}
321 |
322 | /**
323 | * Set the language for the currently authenticated user.
324 | * @param {String} language The language in the form of a locale specification (e.g.
325 | * 'de_DE.UTF-8')
326 | * @returns {Boolean} {@link true} if successful, otherwise {@link false}
327 | */
328 | set_language( language ) {}
329 |
330 | /**
331 | * Triggers the system to shutdown.
332 | * @returns {Boolean} {@link true} if shutdown initiated, otherwise {@link false}
333 | */
334 | shutdown() {}
335 |
336 | /**
337 | * Start a session for the authenticated user.
338 | * @param {String|null} session The session to log into or {@link null} to use the default.
339 | * @returns {Boolean} {@link true} if successful, otherwise {@link false}
340 | */
341 | start_session( session ) {}
342 |
343 | /**
344 | * Triggers the system to suspend/sleep.
345 | * @returns {Boolean} {@link true} if suspend/sleep initiated, otherwise {@link false}
346 | */
347 | suspend() {}
348 |
349 | /**
350 | * Gets emitted when the greeter has completed authentication.
351 | * @type {Signal}
352 | */
353 | authentication_complete;
354 |
355 | /**
356 | * Gets emitted when the automatic login timer has expired.
357 | * @type {Signal}
358 | */
359 | autologin_timer_expired;
360 |
361 | /**
362 | * Gets emitted when brightness is updated
363 | * @type {Signal}
364 | */
365 | brightness_update;
366 |
367 | /**
368 | * Gets emitted when the user has logged in and the greeter is no longer needed.
369 | * @type {Signal}
370 | */
371 | idle;
372 |
373 | /**
374 | * Gets emitted when the user is returning to a greeter that
375 | * was previously marked idle.
376 | * @type {Signal}
377 | */
378 | reset;
379 |
380 | /**
381 | * Gets emitted when the greeter should show a message to the user.
382 | * @type {Signal}
383 | */
384 | show_message;
385 |
386 | /**
387 | * Gets emitted when the greeter should show a prompt to the user.
388 | * @type {Signal}
389 | */
390 | show_prompt;
391 |
392 | }
393 |
394 | /**
395 | * JS-Cookie instance - Themes must manually load the included vendor script in order to use this object.
396 | * @name Cookies
397 | * @type {object}
398 | * @version 2.1.3
399 | * @memberOf window
400 | * @see [JS Cookie Documentation](https://github.com/js-cookie/js-cookie/tree/latest#readme)
401 | */
402 |
403 |
404 |
405 |
--------------------------------------------------------------------------------
/docs/GreeterConfig.js:
--------------------------------------------------------------------------------
1 | /*
2 | * GreeterConfig.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | /**
30 | * Provides greeter themes with a way to access values from the greeter's config
31 | * file located at `/etc/lightdm/web-greeter.yml`. The greeter will
32 | * create an instance of this class when it starts. The instance can be accessed
33 | * with the global variable: `greeter_config`.
34 | *
35 | * @memberOf LightDM
36 | */
37 | class GreeterConfig {
38 | /**
39 | * Holds keys/values from the `branding` section of the config file.
40 | *
41 | * @type {Object}
42 | * @property {String} background_images_dir Path to directory that contains background images
43 | * for use in greeter themes.
44 | * @property {String} logo Path to distro logo image for use in greeter themes.
45 | * @property {String} user_image Default user image/avatar. This is used by greeter themes
46 | * for users that have not configured a `.face` image.
47 | * @readonly
48 | */
49 | get branding() {}
50 |
51 | /**
52 | * Holds keys/values from the `greeter` section of the config file.
53 | *
54 | * @type {Object}
55 | * @property {Boolean} debug_mode Greeter theme debug mode.
56 | * @property {Boolean} detect_theme_errors Provide an option to load a fallback theme when theme
57 | * errors are detected.
58 | * @property {Number} screensaver_timeout Blank the screen after this many seconds of inactivity.
59 | * @property {Boolean} secure_mode Don't allow themes to make remote http requests.
60 | * @property {String} theme The name of the theme to be used by the greeter.
61 | * @property {String|Null} icon_theme Icon/cursor theme to use, located in /usr/share/icons, i.e "Adwaita". Set to Null to use default icon theme.
62 | * @property {String|Null} time_language Language to use when displaying the date or time, i.e "en-us", "es-419", "ko", "ja". Set to Null to use system's language.
63 | * @readonly
64 | */
65 | get greeter() {}
66 |
67 |
68 | /**
69 | * Holds keys/values from the `features` section of the config file.
70 | *
71 | * @type {Object}
72 | * @property {Boolean} battery Enable greeter and themes to ger battery status.
73 | * @property {Object} backlight
74 | * @property {Boolean} backlight.enabled Enable greeter and themes to control display backlight.
75 | * @property {Number} backlight.value The amount to increase/decrease brightness by greeter.
76 | * @property {Number} backlight.steps How many steps are needed to do the change.
77 | */
78 | get features() {}
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/docs/LightDMObjects.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2015-2017 Antergos
3 | *
4 | * LightDMObjects.js
5 | *
6 | * This file is part of Web Greeter
7 | *
8 | * Web Greeter is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation, either version 3 of the License,
11 | * or any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with this program. If not, see .
26 | */
27 |
28 | /**
29 | * The global window object.
30 | *
31 | * @name window
32 | * @type {object}
33 | * @global
34 | */
35 |
36 | /**
37 | * The greeter's Theme JavaScript API.
38 | * Accesible through `lightdm` global variable.
39 | *
40 | * @namespace LightDM
41 | */
42 |
43 |
44 | /**
45 | * Interface for object that holds info about a session. Session objects are not
46 | * created by the theme's code, but rather by the [`LightDM.Greeter`](Greeter.md) class.
47 | *
48 | * @memberOf LightDM
49 | */
50 | class Session {
51 | constructor( { comment, key, name } ) {
52 | this._comment = comment;
53 | this._key = key;
54 | this._name = name;
55 | }
56 |
57 | /**
58 | * The name for the session.
59 | * @type {String}
60 | * @readonly
61 | */
62 | get name() {
63 | return this._name;
64 | }
65 |
66 | /**
67 | * The key for the session.
68 | * @type {String}
69 | * @readonly
70 | */
71 | get key() {
72 | return this._key;
73 | }
74 |
75 | /**
76 | * The comment for the session.
77 | * @type {String}
78 | * @readonly
79 | */
80 | get comment() {
81 | return this._comment;
82 | }
83 | }
84 |
85 |
86 | /**
87 | * Interface for object that holds info about a language on the system. Language objects are not
88 | * created by the theme's code, but rather by the [`LightDM.Greeter`](Greeter.md) class.
89 | *
90 | * @memberOf LightDM
91 | */
92 | class Language {
93 | constructor( { code, name, territory } ) {
94 | this._code = code;
95 | this._name = name;
96 | this._territory = territory;
97 | }
98 |
99 | /**
100 | * The code for the language.
101 | * @type {String}
102 | * @readonly
103 | */
104 | get code() {
105 | return this._code;
106 | }
107 |
108 | /**
109 | * The name for the layout.
110 | * @type {String}
111 | * @readonly
112 | */
113 | get name() {
114 | return this._name;
115 | }
116 |
117 | /**
118 | * The territory for the language.
119 | * @type {String}
120 | * @readonly
121 | */
122 | get territory() {
123 | return this._territory;
124 | }
125 | }
126 |
127 |
128 | /**
129 | * Interface for object that holds info about a keyboard layout on the system. Language
130 | * objects are not created by the theme's code, but rather by the [`LightDM.Greeter`](Greeter.md) class.
131 | *
132 | * @memberOf LightDM
133 | */
134 | class Layout {
135 | constructor( { description, name, short_description } ) {
136 | this._description = description;
137 | this._name = name;
138 | this._short_description = short_description;
139 | }
140 |
141 | /**
142 | * The description for the layout.
143 | * @type {String}
144 | * @readonly
145 | */
146 | get description() {
147 | return this._description;
148 | }
149 |
150 | /**
151 | * The name for the layout.
152 | * @type {String}
153 | * @readonly
154 | */
155 | get name() {
156 | return this._name;
157 | }
158 |
159 | /**
160 | * The territory for the layout.
161 | * @type {String}
162 | * @readonly
163 | */
164 | get short_description() {
165 | return this._short_description;
166 | }
167 | }
168 |
169 |
170 | /**
171 | * Interface for object that holds info about a user account on the system. User
172 | * objects are not created by the theme's code, but rather by the [`LightDM.Greeter`](Greeter.md) class.
173 | *
174 | * @memberOf LightDM
175 | */
176 | class User {
177 | constructor( user_info ) {
178 | Object.keys(user_info).forEach( key => {
179 | this[`_${key}`] = user_info[key];
180 | } );
181 | }
182 |
183 | /**
184 | * The display name for the user.
185 | * @type {String}
186 | * @readonly
187 | */
188 | get display_name() {
189 | return this._display_name;
190 | }
191 |
192 | /**
193 | * The language for the user.
194 | * @type {String}
195 | * @readonly
196 | */
197 | get language() {
198 | return this._language;
199 | }
200 |
201 | /**
202 | * The keyboard layout for the user.
203 | * @type {String}
204 | * @readonly
205 | */
206 | get layout() {
207 | return this._layout;
208 | }
209 |
210 | /**
211 | * The image for the user.
212 | * @type {String}
213 | * @readonly
214 | */
215 | get image() {
216 | return this._image;
217 | }
218 |
219 | /**
220 | * The home_directory for the user.
221 | * @type {String}
222 | * @readonly
223 | */
224 | get home_directory() {
225 | return this._home_directory;
226 | }
227 |
228 | /**
229 | * The username for the user.
230 | * @type {String}
231 | * @readonly
232 | */
233 | get username() {
234 | return this._username;
235 | }
236 |
237 | /**
238 | * Whether or not the user is currently logged in.
239 | * @type {Boolean}
240 | * @readonly
241 | */
242 | get logged_in() {
243 | return this._logged_in;
244 | }
245 |
246 | /**
247 | * The last session that the user logged into.
248 | * @type {String|Null}
249 | * @readonly
250 | */
251 | get session() {
252 | return this._session;
253 | }
254 | }
255 |
256 | /**
257 | * Interface for object that holds info about the battery on the system. This object is not created by the theme's code, but rather by the [`LightDM.Greeter`]{@link Greeter.md} class.
258 | *
259 | * @memberOf LightDM
260 | */
261 | class Battery {
262 | constructor( {level, name, state} ) {
263 | this._level = level;
264 | this._name = name;
265 | this._state = state;
266 | }
267 |
268 | /**
269 | * The battery level.
270 | * @type {String|Null}
271 | * @readonly
272 | */
273 | get level() {
274 | return this._level;
275 | }
276 |
277 | /**
278 | * The battery's name.
279 | * @type {String|Null}
280 | * @readonly
281 | */
282 | get name() {
283 | return this._name;
284 | }
285 |
286 | /**
287 | * The state for the battery
288 | * @type {String|Null}
289 | * @readonly
290 | */
291 | get state() {
292 | return this._state;
293 | }
294 | }
295 |
296 |
297 | /**
298 | * Interface for signals connected to LightDM itself. This is not created by the theme's code, but rather by Web Greeter.
299 | * When Web Greeter triggers the signal, all calbacks are executed.
300 | *
301 | * @memberOf LightDM
302 | */
303 | class Signal {
304 |
305 | /**
306 | * Connects a callback to the signal.
307 | * @param {Function} callback The callback to attach.
308 | */
309 | connect( callback ) {}
310 |
311 | /**
312 | * Disconnects a callback to the signal.
313 | * @param {Function} callback The callback to disattach.
314 | */
315 | disconnect( callback ) {}
316 | }
317 |
--------------------------------------------------------------------------------
/docs/ThemeUtils.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ThemeUtils.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | let time_language = null,
30 | _ThemeUtils = null;
31 |
32 |
33 | /**
34 | * Provides various utility methods for use in greeter themes. The greeter will automatically
35 | * create an instance of this class when it starts. The instance can be accessed
36 | * with the global variable: [`theme_utils`](#dl-window-theme_utils).
37 | *
38 | * @memberOf LightDM
39 | */
40 | class ThemeUtils {
41 |
42 | constructor( instance ) {
43 | if ( null !== _ThemeUtils ) {
44 | return _ThemeUtils;
45 | }
46 |
47 | _ThemeUtils = instance;
48 | }
49 |
50 | /**
51 | * Binds `this` to class, `context`, for all of the class's methods.
52 | *
53 | * @arg {object} context An ES6 class instance with at least one method.
54 | *
55 | * @return {object} `context` with `this` bound to it for all of its methods.
56 | */
57 | bind_this( context ) {
58 | let excluded_methods = ['constructor'];
59 |
60 | function not_excluded( _method, _context ) {
61 | let is_excluded = excluded_methods.findIndex( excluded_method => _method === excluded_method ) > -1,
62 | is_method = 'function' === typeof _context[_method];
63 |
64 | return is_method && !is_excluded;
65 | }
66 |
67 | for ( let obj = context; obj; obj = Object.getPrototypeOf( obj ) ) {
68 | // Stop once we have traveled all the way up the inheritance chain
69 | if ( 'Object' === obj.constructor.name ) {
70 | break;
71 | }
72 |
73 | for ( let method of Object.getOwnPropertyNames( obj ) ) {
74 | if ( not_excluded( method, context ) ) {
75 | context[method] = context[method].bind( context );
76 | }
77 | }
78 | }
79 |
80 | return context;
81 | }
82 |
83 |
84 | /**
85 | * Returns the contents of directory found at `path` provided that the (normalized) `path`
86 | * meets at least one of the following conditions:
87 | * * Is located within the greeter themes' root directory.
88 | * * Has been explicitly allowed in the greeter's config file.
89 | * * Is located within the greeter's shared data directory (`/var/lib/lightdm-data`).
90 | * * Is located in `/tmp`.
91 | *
92 | * @param {string} path The abs path to desired directory.
93 | * @param {boolean} only_images Include only images in the results. Default `true`.
94 | * @param {function(string[])} callback Callback function to be called with the result.
95 | */
96 | dirlist( path, only_images = true, callback ) {
97 | if ( '' === path || 'string' !== typeof path ) {
98 | console.error(`theme_utils.dirlist(): path must be a non-empty string!`);
99 | return callback([]);
100 |
101 | } else if ( null !== path.match(/^[^/].+/) ) {
102 | console.error(`theme_utils.dirlist(): path must be absolute!`);
103 | return callback([]);
104 | }
105 |
106 | if ( null !== path.match(/\/\.+(?=\/)/) ) {
107 | // No special directory names allowed (eg ../../)
108 | path = path.replace(/\/\.+(?=\/)/g, '' );
109 | }
110 |
111 | try {
112 | return _ThemeUtils.dirlist( path, only_images, callback );
113 | } catch( err ) {
114 | console.error(`theme_utils.dirlist(): ${err}`);
115 | return callback([]);
116 | }
117 | }
118 |
119 | /**
120 | * Get the current date in a localized format. Local language is autodetected by default, but can be set manually in the greeter config file.
121 | * * `language` defaults to the system's language, but can be set manually in the config file.
122 | *
123 | * @returns {Object} The current date.
124 | */
125 | get_current_localized_date() {
126 | let config = greeter_config.greeter
127 |
128 | var locale = []
129 |
130 | if (time_language === null) {
131 | time_language = config.time_language || ""
132 | }
133 |
134 | if (time_language != "") {
135 | locale.push(time_language)
136 | }
137 |
138 | let optionsDate = { day: "2-digit", month: "2-digit", year: "2-digit" }
139 |
140 | let fmtDate = Intl.DateTimeFormat(locale, optionsDate)
141 |
142 | let now = new Date()
143 | var date = fmtDate.format(now)
144 |
145 | return date
146 | }
147 |
148 | /**
149 | * Get the current time in a localized format. Local language is autodetected by default, but can be set manually in the greeter config file.
150 | * * `language` defaults to the system's language, but can be set manually in the config file.
151 | *
152 | * @returns {Object} The current time.
153 | */
154 | get_current_localized_time() {
155 | let config = greeter_config.greeter
156 |
157 | var locale = []
158 |
159 | if (time_language === null) {
160 | time_language = config.time_language || ""
161 | }
162 |
163 | if (time_language != "") {
164 | locale.push(time_language)
165 | }
166 |
167 | let optionsTime = { hour: "2-digit", minute: "2-digit" }
168 |
169 | let fmtTime = Intl.DateTimeFormat(locale, optionsTime)
170 |
171 | let now = new Date()
172 | var time = fmtTime.format(now)
173 |
174 | return time
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/docs/bootstrap.js:
--------------------------------------------------------------------------------
1 | /*
2 | * bootstrap.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | (() => {
30 | let _channel;
31 |
32 | /**
33 | * Greeter Ready Event. Themes should not initialize until this event has fired.
34 | * @event window#GreeterReady
35 | * @name GreeterReady
36 | * @type Event
37 | * @memberOf window
38 | */
39 | window._ready_event = new Event( 'GreeterReady' );
40 |
41 | function channel_ready_cb( channel ) {
42 | _channel = channel;
43 |
44 | /**
45 | * Greeter Instance
46 | * @name lightdm
47 | * @type {LightDM.Greeter}
48 | * @memberOf window
49 | */
50 | window.lightdm = _channel.objects.LightDMGreeter;
51 |
52 | /**
53 | * Greeter Config - Access values from the greeter's config file.
54 | * @name greeter_config
55 | * @type {LightDM.GreeterConfig}
56 | * @memberOf window
57 | */
58 | window.greeter_config = _channel.objects.Config;
59 |
60 | /**
61 | * Theme Utils - various utility methods for use in greeter themes.
62 | * @name theme_utils
63 | * @type {LightDM.ThemeUtils}
64 | * @memberOf window
65 | */
66 | window.theme_utils = new ThemeUtils( _channel.objects.ThemeUtils );
67 |
68 | setTimeout( function () {
69 | window.dispatchEvent( _ready_event );
70 | }, 2 );
71 | }
72 |
73 | document.addEventListener( 'DOMContentLoaded', ( event ) => {
74 | new QWebChannel( qt.webChannelTransport, channel_ready_cb );
75 | });
76 |
77 | })();
78 |
79 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | PyGObject
2 | PyQt5
3 | PyQtWebEngine
4 | ruamel.yaml
5 | pyinotify
6 |
--------------------------------------------------------------------------------
/src/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JezerM/web-greeter/0bfa7f0036b2336c4d9aa9ad35e0777ab0b41857/src/__init__.py
--------------------------------------------------------------------------------
/src/__main__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # __main__.py
4 | #
5 | # Copyright © 2021 JezerM
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 |
28 | # Standard lib
29 | import sys
30 | import argparse
31 | import os
32 | from typing import List
33 |
34 | # 3rd-Party Libs
35 | import config
36 |
37 | def list_themes() -> List[str]:
38 | """List available themes"""
39 | themes_dir = config.web_greeter_config["app"]["theme_dir"]
40 | themes_dir = themes_dir if os.path.exists(themes_dir) else "/usr/share/web-greeter/themes"
41 | filenames = os.listdir(themes_dir)
42 |
43 | dirlist = []
44 | for file in filenames:
45 | if os.path.isdir(os.path.join(themes_dir, file)):
46 | dirlist.append(file)
47 |
48 | return dirlist
49 |
50 | def print_themes():
51 | """Print available themes"""
52 | themes_dir = config.web_greeter_config["app"]["theme_dir"]
53 | themes_dir = themes_dir if os.path.exists(themes_dir) else "/usr/share/web-greeter/themes"
54 | themes = list_themes()
55 | print(f"Themes are located in {themes_dir}\n")
56 | for theme in themes:
57 | print("-", theme)
58 |
59 |
60 | def set_theme(theme: str):
61 | """Sets the theme"""
62 | config.web_greeter_config["config"]["greeter"]["theme"] = theme
63 |
64 | def set_debug(value: bool):
65 | """Sets debug mode"""
66 | conf = config.web_greeter_config["config"]
67 | app = config.web_greeter_config["app"]
68 | conf["greeter"]["debug_mode"] = value
69 | app["frame"] = value
70 | app["fullscreen"] = not value
71 |
72 | def parse(argv):
73 | """Parse command arguments"""
74 | version = config.web_greeter_config["app"]["version"]["full"]
75 | api_version = config.web_greeter_config["app"]["api_version"]["full"]
76 |
77 | parser = argparse.ArgumentParser(prog="web-greeter", add_help = False)
78 | parser.add_argument("-h", "--help", action = "help",
79 | help = "Show this help message and exit")
80 | parser.add_argument("-v", "--version", action = "version",
81 | version = version, help = "Show version number")
82 | parser.add_argument("--api-version", action = "version",
83 | version = api_version, help = "Show JavaScript API version number")
84 |
85 | parser.add_argument("--mode", help = "Set browser mode",
86 | choices = ["debug", "normal"])
87 | parser.add_argument("-d", "--debug", action = "store_true",
88 | help = "Run the greeter in debug mode",
89 | dest = "debug", default = None)
90 | parser.add_argument("-n", "--normal", action = "store_false",
91 | help = "Run in non-debug mode", dest = "debug")
92 | parser.add_argument("--list", action = "store_true",
93 | help = "List available themes")
94 | parser.add_argument("--theme", help = "Set the theme to use", metavar = "[name]")
95 | parser.add_argument("--no-sandbox", action = "store_true", help = argparse.SUPPRESS)
96 |
97 | args: argparse.Namespace
98 |
99 | try:
100 | args = parser.parse_args(argv)
101 | except argparse.ArgumentError:
102 | sys.exit()
103 |
104 | # print(args)
105 |
106 | if args.list:
107 | print_themes()
108 | sys.exit()
109 | if args.theme:
110 | set_theme(args.theme)
111 | if args.mode == "debug":
112 | args.debug = True
113 | elif args.mode == "normal":
114 | args.debug = False
115 | if args.debug is not None:
116 | set_debug(args.debug)
117 |
118 | if __name__ == '__main__':
119 | parse(sys.argv[1:])
120 |
121 | import globales
122 | from browser.browser import Browser
123 | from bridge import Greeter, Config, ThemeUtils
124 | from PyQt5.QtWidgets import QApplication
125 | from PyQt5.QtCore import Qt, QCoreApplication
126 |
127 | QCoreApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
128 | QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
129 | app = QApplication(sys.argv)
130 |
131 | globales.LDMGreeter = Greeter()
132 | globales.LDMGreeterConfig = Config()
133 | globales.LDMThemeUtils = ThemeUtils(globales.LDMGreeter)
134 |
135 | config.load_theme_config()
136 | config.ensure_theme()
137 |
138 | globales.greeter = Browser(app)
139 | browser = globales.greeter
140 |
141 | # browser.load()
142 | browser.show()
143 | browser.run()
144 |
--------------------------------------------------------------------------------
/src/bindings/__init__.py:
--------------------------------------------------------------------------------
1 | from .screensaver import screensaver
2 |
--------------------------------------------------------------------------------
/src/bindings/screensaver.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | Display* OpenDisplay() {
7 | char *disp = NULL;
8 | Display *display = XOpenDisplay(disp);
9 | return display;
10 | }
11 |
12 | typedef struct {
13 | int timeout;
14 | int interval;
15 | int prefer_blank;
16 | int allow_exp;
17 | } ScreenSaver;
18 |
19 | ScreenSaver *get_screensaver() {
20 | int timeout, interval, prefer_blank, allow_exp;
21 |
22 | Display *display = OpenDisplay();
23 | if (display == NULL) return NULL;
24 |
25 | XGetScreenSaver(display, &timeout, &interval, &prefer_blank, &allow_exp);
26 | XCloseDisplay(display);
27 |
28 | ScreenSaver *data = malloc(sizeof *data);
29 | data->timeout = timeout;
30 | data->interval = interval;
31 | data->prefer_blank = prefer_blank;
32 | data->allow_exp = allow_exp;
33 |
34 | return data;
35 | }
36 |
37 | void set_screensaver(int timeout, int interval, int prefer_blank, int allow_exp) {
38 | int timeout_i, interval_i, prefer_blank_i, allow_exp_i;
39 |
40 | Display *display = OpenDisplay();
41 | if (display == NULL) return;
42 | XGetScreenSaver(display, &timeout_i, &interval_i, &prefer_blank_i, &allow_exp_i);
43 |
44 | if (timeout != -1) timeout_i = timeout;
45 | if (interval != -1) interval_i = interval;
46 | if (prefer_blank != -1) prefer_blank_i = prefer_blank;
47 | if (allow_exp != -1) allow_exp_i = allow_exp;
48 |
49 | XSetScreenSaver(display, timeout_i, interval_i, prefer_blank_i, allow_exp_i);
50 |
51 | XCloseDisplay(display);
52 | }
53 |
54 | void force_screensaver(bool value) {
55 | Display *display = OpenDisplay();
56 | if (display == NULL) return;
57 |
58 | if (value == true)
59 | XForceScreenSaver(display, ScreenSaverActive);
60 | else
61 | XForceScreenSaver(display, ScreenSaverReset);
62 |
63 | XCloseDisplay(display);
64 | }
65 |
--------------------------------------------------------------------------------
/src/bindings/screensaver.py:
--------------------------------------------------------------------------------
1 | import ctypes
2 | import os
3 | from typing import Union
4 | from logger import logger
5 |
6 |
7 | class ScreenSaverData(ctypes.Structure):
8 | """Screensaver data"""
9 | _fields_ = [
10 | ("timeout", ctypes.c_int),
11 | ("interval", ctypes.c_int),
12 | ("prefer_blank", ctypes.c_int),
13 | ("allow_exp", ctypes.c_int),
14 | ]
15 |
16 | def __str__(self):
17 | timeout = getattr(self, "timeout")
18 | interval = getattr(self, "interval")
19 | prefer_blank = getattr(self, "prefer_blank")
20 | allow_exp = getattr(self, "allow_exp")
21 | return f"{timeout} {interval} {prefer_blank} {allow_exp}"
22 |
23 | ScreenSaverDataPointer = ctypes.POINTER(ScreenSaverData)
24 |
25 | class ScreenSaver:
26 | """Screensaver wrapper"""
27 |
28 | saved_data: Union[ScreenSaverData, None]
29 | saved: bool = False
30 |
31 | def __init__(self):
32 | directory = os.path.dirname(os.path.realpath(__file__))
33 | libname = os.path.join(directory, "_screensaver.so")
34 | self.clib = ctypes.CDLL(libname)
35 | self.clib.get_screensaver.restype = ScreenSaverDataPointer
36 |
37 | def get_screensaver(self):
38 | """Gets screensaver data"""
39 | data: ScreenSaverDataPointer = self.clib.get_screensaver()
40 | if data is None:
41 | return None
42 | contents: ScreenSaverData = data.contents
43 | return contents
44 |
45 | def set_screensaver(self, timeout = -1, interval = -1, prefer_blank = -1, allow_exp = -1):
46 | """Sets screensaver properties"""
47 | if self.saved:
48 | return
49 | self.saved_data = self.get_screensaver()
50 | self.saved = True
51 | self.clib.set_screensaver(
52 | ctypes.c_int(timeout),
53 | ctypes.c_int(interval),
54 | ctypes.c_int(prefer_blank),
55 | ctypes.c_int(allow_exp)
56 | )
57 | logger.debug("Screensaver timeout set")
58 |
59 | def reset_screensaver(self):
60 | """Reset screensaver"""
61 | if not self.saved or not self.saved_data:
62 | return
63 | self.clib.set_screensaver(
64 | ctypes.c_int(getattr(self.saved_data, "timeout")),
65 | ctypes.c_int(getattr(self.saved_data, "interval")),
66 | ctypes.c_int(getattr(self.saved_data, "prefer_blank")),
67 | ctypes.c_int(getattr(self.saved_data, "allow_exp"))
68 | )
69 | self.saved = False
70 | logger.debug("Screensaver reset")
71 |
72 | def force_screensaver(self, value: bool):
73 | """Force screensaver"""
74 | self.clib.force_screensaver(ctypes.c_bool(value))
75 |
76 | screensaver = ScreenSaver()
77 |
--------------------------------------------------------------------------------
/src/bridge/Config.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Config.py
4 | #
5 | # Copyright © 2017 Antergos
6 | # Copyright © 2021 JezerM
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | # pylint: disable=wrong-import-position
30 |
31 | # Standard Lib
32 | from typing import List
33 |
34 | # 3rd-Party Libs
35 | import gi
36 | gi.require_version('LightDM', '1')
37 | from gi.repository import LightDM
38 |
39 | from PyQt5.QtCore import QVariant
40 |
41 | # This application
42 | from browser.bridge import Bridge, BridgeObject
43 | from config import web_greeter_config
44 |
45 | from . import layout_to_dict
46 |
47 | def get_layouts(config_layouts: List[str]):
48 | """Get layouts from web-greeter's config"""
49 | layouts = LightDM.get_layouts()
50 | final_layouts: list[LightDM.Layout] = []
51 | for ldm_lay in layouts:
52 | for conf_lay in config_layouts:
53 | if not isinstance(conf_lay, str):
54 | return []
55 | conf_lay = conf_lay.replace(" ", "\t")
56 | if ldm_lay.get_name() == conf_lay:
57 | final_layouts.append(layout_to_dict(ldm_lay))
58 | return final_layouts
59 |
60 |
61 | class Config(BridgeObject):
62 | # pylint: disable=no-self-use,missing-function-docstring,too-many-public-methods,invalid-name
63 | """Config bridge class, known as `greeter_config` in javascript"""
64 |
65 | noop_signal = Bridge.signal()
66 |
67 | def __init__(self, *args, **kwargs):
68 | super().__init__(name='Config', *args, **kwargs)
69 |
70 | _config = web_greeter_config["config"]
71 | self._branding = _config["branding"]
72 | self._greeter = _config["greeter"]
73 | self._features = _config["features"]
74 | self._layouts = get_layouts(_config["layouts"])
75 |
76 | @Bridge.prop(QVariant, notify=noop_signal)
77 | def branding(self):
78 | return self._branding
79 |
80 | @Bridge.prop(QVariant, notify=noop_signal)
81 | def greeter(self):
82 | return self._greeter
83 |
84 | @Bridge.prop(QVariant, notify=noop_signal)
85 | def features(self):
86 | return self._features
87 |
88 | @Bridge.prop(QVariant, notify=noop_signal)
89 | def layouts(self):
90 | return self._layouts
91 |
--------------------------------------------------------------------------------
/src/bridge/GreeterComm.py:
--------------------------------------------------------------------------------
1 | #
2 | # Greeter.py
3 | #
4 | # Copyright © 2021 JezerM
5 | #
6 | # This file is part of Web Greeter.
7 | #
8 | # Web Greeter is free software; you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation; either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # Web Greeter is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # The following additional terms are in effect as per Section 7 of the license:
19 | #
20 | # The preservation of all legal notices and author attributions in
21 | # the material or in the Appropriate Legal Notices displayed
22 | # by works containing it is required.
23 | #
24 | # You should have received a copy of the GNU General Public License
25 | # along with Web Greeter; If not, see .
26 |
27 | # pylint: disable=wrong-import-position
28 |
29 |
30 | from typing import List
31 |
32 | from PyQt5.QtCore import QVariant, QTimer
33 |
34 | # This Application
35 | from browser.window import WindowAbstract
36 | from browser.bridge import Bridge, BridgeObject
37 | from bridge import window_metadata_to_dict
38 |
39 | import globales
40 |
41 | communications: List = []
42 |
43 | def communication_emit(window, data):
44 | """Emit broadcast_signal for each GreeterComm element in communications"""
45 | for comm in communications:
46 | # print(window)
47 | comm.broadcast_signal.emit(window, data)
48 |
49 | class GreeterComm(BridgeObject):
50 | # pylint: disable=missing-function-docstring,too-many-public-methods,invalid-name
51 | """Greeter Communication bridge class, known as `greeter_comm` in javascript"""
52 |
53 | broadcast_signal = Bridge.signal(QVariant, QVariant, arguments=("window", "data"))
54 | metadata_signal = Bridge.signal(QVariant, arguments=("metadata"))
55 |
56 | property_changed = Bridge.signal()
57 | window: WindowAbstract
58 |
59 | def __init__(self, window, *args, **kwargs):
60 | super().__init__(name='Comm', *args, **kwargs)
61 | self.window = window
62 |
63 | communications.append(self)
64 |
65 | @Bridge.prop(QVariant, notify=property_changed)
66 | def window_metadata(self):
67 | for win in globales.greeter.windows:
68 | if self.window.meta.id == win.meta.id:
69 | meta = window_metadata_to_dict(win.meta)
70 | # print(meta)
71 | return meta
72 |
73 | return {}
74 |
75 | @Bridge.method(QVariant)
76 | def broadcast(self, data):
77 | self.property_changed.emit()
78 | QTimer().singleShot(60, lambda: communication_emit(self.window_metadata, data))
79 |
80 | @Bridge.method()
81 | def requestMetadata(self):
82 | for win in globales.greeter.windows:
83 | if self.window.meta.id == win.meta.id:
84 | meta = window_metadata_to_dict(win.meta)
85 | self.metadata_signal.emit(meta)
86 |
87 | def destroy(self):
88 | global communications
89 | comms = []
90 | for obj in communications:
91 | if obj != self:
92 | comms.append(obj)
93 |
94 | communications = comms
95 |
--------------------------------------------------------------------------------
/src/bridge/ThemeUtils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # ThemeUtils.py
4 | #
5 | # Copyright © 2017 Antergos
6 | # Copyright © 2021 JezerM
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | # pylint: disable=wrong-import-position
30 |
31 | # Standard Lib
32 | import os
33 | import re
34 | import tempfile
35 |
36 | # 3rd-Party Libs
37 | from PyQt5.QtCore import QVariant
38 |
39 | # This application
40 | from browser.bridge import Bridge, BridgeObject
41 |
42 | from config import web_greeter_config
43 | from logger import logger
44 |
45 | class ThemeUtils(BridgeObject):
46 | # pylint: disable=no-self-use,missing-function-docstring,too-many-public-methods,invalid-name
47 | """ThemeUtils bridge class, known as `theem_utils` in javascript"""
48 |
49 | def __init__(self, greeter_object, *args, **kwargs):
50 | super().__init__(name='ThemeUtils', *args, **kwargs)
51 |
52 | self._config = web_greeter_config
53 | self._greeter = greeter_object
54 |
55 | self._allowed_dirs = (
56 | os.path.dirname(
57 | os.path.realpath(self._config["config"]["greeter"]["theme"])
58 | ),
59 | self._config["app"]["theme_dir"],
60 | self._config["config"]["branding"]["background_images_dir"],
61 | self._greeter.shared_data_directory,
62 | tempfile.gettempdir(),
63 | )
64 |
65 | @Bridge.method(str, bool, result=QVariant)
66 | def dirlist(self, dir_path, only_images=True):
67 | if not dir_path or not isinstance(dir_path, str) or '/' == dir_path:
68 | return []
69 |
70 | if dir_path.startswith("./"):
71 | dir_path = os.path.join(
72 | os.path.dirname(self._config["config"]["greeter"]["theme"]),
73 | dir_path
74 | )
75 |
76 | dir_path = os.path.realpath(os.path.normpath(dir_path))
77 |
78 | if not os.path.isabs(dir_path) or not os.path.isdir(dir_path):
79 | return []
80 |
81 | allowed = False
82 |
83 | for allowed_dir in self._allowed_dirs:
84 | if dir_path.startswith(allowed_dir):
85 | allowed = True
86 | break
87 |
88 | if not allowed:
89 | logger.error("Path \"%s\" is not allowed", dir_path)
90 | return []
91 |
92 | result = []
93 | if only_images:
94 | for f in os.scandir(dir_path):
95 | if f.is_file() and re.match(r".+\.(jpe?g|png|gif|bmp|webp)", f.name):
96 | result.append(f.path)
97 |
98 | else:
99 | result = [os.path.join(dir_path, f) for f in os.listdir(dir_path)]
100 |
101 | result.sort()
102 | return result
103 |
--------------------------------------------------------------------------------
/src/bridge/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | #
4 | # __init__.py
5 | #
6 | # Copyright © 2017 Antergos
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | import math
30 | import sys
31 | from typing import Literal
32 | from logger import logger
33 |
34 | def language_to_dict(lang):
35 | """Returns a dict from LightDMLanguage object"""
36 | if not lang:
37 | return {}
38 | try:
39 | return {
40 | "code": lang.get_code(),
41 | "name": lang.get_name(),
42 | "territory": lang.get_territory()
43 | }
44 | except Exception as e:
45 | logger.warn(e)
46 | return {}
47 |
48 |
49 | def layout_to_dict(layout):
50 | """Returns a dict from LightDMLayout object"""
51 | if not layout:
52 | return {}
53 | try:
54 | return {
55 | "description": layout.get_description(),
56 | "name": layout.get_name(),
57 | "short_description": layout.get_short_description()
58 | }
59 | except Exception as e:
60 | logger.warn(e)
61 | return {}
62 |
63 |
64 | def session_to_dict(session):
65 | """Returns a dict from LightDMSession object"""
66 | if not session:
67 | return {}
68 | try:
69 | return {
70 | "comment": session.get_comment(),
71 | "key": session.get_key(),
72 | "name": session.get_name(),
73 | "type": session.get_session_type(),
74 | }
75 | except Exception as e:
76 | logger.warn(e)
77 | return {}
78 |
79 |
80 | def user_to_dict(user):
81 | """Returns a dict from LightDMUser object"""
82 | if not user:
83 | return {}
84 | try:
85 | return {
86 | "background": user.get_background(),
87 | "display_name": user.get_display_name(),
88 | "home_directory": user.get_home_directory(),
89 | "image": user.get_image(),
90 | "language": user.get_language(),
91 | "layout": user.get_layout(),
92 | "layouts": user.get_layouts(),
93 | "logged_in": user.get_logged_in(),
94 | "session": user.get_session(),
95 | "username": user.get_name(),
96 | }
97 | except Exception as e:
98 | logger.warn(e)
99 | return {}
100 |
101 |
102 | def battery_to_dict(battery):
103 | """Returns a dict from Battery object"""
104 | if not battery:
105 | return {}
106 | if len(battery.batteries) == 0:
107 | return {}
108 | try:
109 | return {
110 | "name": battery.get_name(),
111 | "level": battery.get_level(),
112 | "status": battery.get_status(),
113 | "ac_status": battery.get_ac_status(),
114 | "capacity": battery.get_capacity(),
115 | "time": battery.get_time(),
116 | "watt": battery.get_watt()
117 | }
118 | except Exception as e:
119 | logger.warn(e)
120 | return {}
121 |
122 | def inf_to_infinity(num: float):
123 | """Converts a math.inf to "infinity" or "-infinity" """
124 | if not math.isinf(num):
125 | return num
126 | if num > 0:
127 | return "infinity"
128 | return "-infinity"
129 |
130 | def window_metadata_to_dict(metadata):
131 | """Returns a dict from WindowMetadata object"""
132 | if not metadata:
133 | return {}
134 | return {
135 | "id": metadata.id,
136 | "is_primary": metadata.is_primary,
137 | "overallBoundary": {
138 | "minX": metadata.overallBoundary.minX,
139 | "maxX": inf_to_infinity(metadata.overallBoundary.maxX),
140 | "minY": metadata.overallBoundary.minY,
141 | "maxY": inf_to_infinity(metadata.overallBoundary.maxY),
142 | },
143 | "position": {
144 | "x": metadata.position.x,
145 | "y": metadata.position.y,
146 | },
147 | "size": {
148 | "width": metadata.size.width,
149 | "height": metadata.size.height,
150 | },
151 | }
152 |
153 | # pylint: disable=wrong-import-position
154 | from .Greeter import Greeter
155 | from .Config import Config
156 | from .ThemeUtils import ThemeUtils
157 | from .GreeterComm import GreeterComm
158 |
--------------------------------------------------------------------------------
/src/browser/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JezerM/web-greeter/0bfa7f0036b2336c4d9aa9ad35e0777ab0b41857/src/browser/__init__.py
--------------------------------------------------------------------------------
/src/browser/bridge.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # bridge.py
4 | #
5 | # Copyright © 2016-2017 Antergos
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 |
28 | from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty
29 |
30 | class Bridge:
31 | """Bridge class"""
32 | @staticmethod
33 | def method(*args, **kwargs):
34 | """Declare a method"""
35 | return pyqtSlot(*args, **kwargs)
36 |
37 | @staticmethod
38 | def prop(*args, **kwargs):
39 | """Declare a property"""
40 | return pyqtProperty(*args, **kwargs)
41 |
42 | @staticmethod
43 | def signal(*args, **kwargs):
44 | """Declare a signal"""
45 | return pyqtSignal(*args, **kwargs)
46 |
47 | class BridgeObject(QObject):
48 | """BridgeObject class"""
49 | def __init__(self, name: str):
50 | super().__init__(parent=None)
51 | self._name = name
52 |
--------------------------------------------------------------------------------
/src/browser/browser.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # browser.py
4 | #
5 | # Copyright © 2017 Antergos
6 | # Copyright © 2021 JezerM
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | # Standard lib
30 |
31 | import math
32 | import random
33 | import re
34 | import sys
35 | import os
36 | from typing import (
37 | List,
38 | Tuple,
39 | TypeVar,
40 | )
41 |
42 | # 3rd-Party Libs
43 | from PyQt5.QtCore import (
44 | QUrl,
45 | Qt,
46 | QRect,
47 | )
48 | from PyQt5.QtWebEngineCore import QWebEngineUrlScheme
49 | from PyQt5.QtWidgets import (
50 | QApplication
51 | )
52 | from PyQt5.QtWebEngineWidgets import (
53 | QWebEngineProfile
54 | )
55 | from PyQt5.QtGui import QScreen
56 |
57 | from browser.url_scheme import QtUrlSchemeHandler
58 | from browser.interceptor import QtUrlRequestInterceptor
59 | from bridge.GreeterComm import GreeterComm
60 | from browser.browser_interfaces import OverallBoundary, WindowMetadata, WindowPosition, WindowSize
61 | from browser.window import BrowserWindow, WindowAbstract
62 | import globales
63 |
64 | from logger import logger
65 | from config import load_primary_theme_path, load_secondary_theme_path, load_theme_dir, web_greeter_config
66 | from bindings.screensaver import screensaver
67 |
68 | # pylint: disable-next=unused-import
69 | # Do not ever remove this import
70 | import resources
71 |
72 | # Typing Helpers
73 | BridgeObjects = Tuple["BridgeObject"]
74 | Url = TypeVar("Url", str, QUrl)
75 |
76 | os.environ["QT_DEVICE_PIXEL_RATIO"] = "0"
77 | os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
78 | os.environ["QT_SCREEN_SCALE_FACTORS"] = "1"
79 | os.environ["QT_SCALE_FACTOR"] = "1"
80 |
81 | def get_default_cursor():
82 | """Gets the default cursor theme"""
83 | default_theme = "/usr/share/icons/default/index.theme"
84 | cursor_theme = ""
85 | matched = None
86 | try:
87 | with open(default_theme, "r", encoding = "utf-8") as file:
88 | matched = re.search(r"Inherits=.*", file.read())
89 | except IOError:
90 | return ""
91 | if not matched:
92 | logger.error("Default cursor couldn't be get")
93 | return ""
94 | cursor_theme = matched.group().replace("Inherits=", "")
95 | return cursor_theme
96 |
97 | class Application:
98 | """Main application"""
99 | app: QApplication
100 | windows: List[WindowAbstract]
101 |
102 | def __init__(self, application):
103 | self.app = application
104 |
105 | self.set_protocol()
106 |
107 | self.windows = self.create_windows()
108 |
109 | timeout = web_greeter_config["config"]["greeter"]["screensaver_timeout"]
110 | screensaver.set_screensaver(timeout or 300)
111 |
112 | cursor_theme = web_greeter_config["config"]["greeter"]["icon_theme"]
113 | if cursor_theme is not None:
114 | os.environ["XCURSOR_THEME"] = cursor_theme
115 | else:
116 | os.environ["XCURSOR_THEME"] = get_default_cursor()
117 |
118 | self.app.aboutToQuit.connect(self._before_exit)
119 |
120 | def create_windows(self):
121 | """Initialize application windows"""
122 | screens: List[QScreen] = self.app.screens()
123 | primary_screen: QScreen = self.app.primaryScreen()
124 |
125 | overall_boundary: OverallBoundary = OverallBoundary(
126 | minX = math.inf,
127 | maxX = -math.inf,
128 | minY = math.inf,
129 | maxY = -math.inf
130 | )
131 |
132 | for screen in screens:
133 | overall_boundary.minX = min(overall_boundary.minX,
134 | screen.geometry().x())
135 | overall_boundary.minY = min(overall_boundary.minY,
136 | screen.geometry().y())
137 | overall_boundary.maxX = max(
138 | overall_boundary.maxX,
139 | screen.geometry().x() + screen.geometry().width()
140 | )
141 | overall_boundary.maxY = max(
142 | overall_boundary.maxY,
143 | screen.geometry().y() + screen.geometry().height()
144 | )
145 |
146 | windows: List[WindowAbstract] = []
147 | # screens.append(primary_screen)
148 | for screen in screens:
149 | is_primary: bool = screen == primary_screen
150 |
151 | window = BrowserWindow(
152 | QRect(
153 | screen.geometry().x(),
154 | screen.geometry().y(),
155 | screen.geometry().width(),
156 | screen.geometry().height()
157 | ),
158 | web_greeter_config["config"]["greeter"]["debug_mode"]
159 | )
160 |
161 | abstract = WindowAbstract(
162 | is_primary = is_primary,
163 | display = screen,
164 | window = window,
165 | meta = WindowMetadata(
166 | id = random.randrange(1, 20000),
167 | is_primary = is_primary,
168 | size = WindowSize(
169 | width = screen.geometry().width(),
170 | height = screen.geometry().height(),
171 | ),
172 | position = WindowPosition(
173 | x = screen.geometry().x(),
174 | y = screen.geometry().y(),
175 | ),
176 | overallBoundary = overall_boundary
177 | )
178 | )
179 | window.bridge_objects.append(
180 | GreeterComm(abstract)
181 | )
182 | windows.append(abstract)
183 | window.closeEv.connect(self._remove_window)
184 |
185 | logger.debug("Browser Window created")
186 |
187 | return windows
188 |
189 | def _remove_window(self, window):
190 | wins = []
191 | for win in self.windows:
192 | if win.window != window:
193 | wins.append(win)
194 | else:
195 | comm: GreeterComm = win.window.bridge_objects[-1]
196 | comm.destroy()
197 | self.windows = wins
198 |
199 | def set_protocol(self):
200 | """Set protocol"""
201 | url_scheme = "web-greeter"
202 | self.url_scheme = QWebEngineUrlScheme(url_scheme.encode())
203 | self.url_scheme.setDefaultPort(QWebEngineUrlScheme.PortUnspecified)
204 | self.url_scheme.setFlags(QWebEngineUrlScheme.SecureScheme or
205 | QWebEngineUrlScheme.LocalScheme or
206 | QWebEngineUrlScheme.LocalAccessAllowed)
207 | QWebEngineUrlScheme.registerScheme(self.url_scheme)
208 |
209 | self.profile = QWebEngineProfile.defaultProfile()
210 | self.interceptor = QtUrlRequestInterceptor(url_scheme)
211 | self.url_scheme_handler = QtUrlSchemeHandler()
212 |
213 | self.profile.installUrlSchemeHandler(url_scheme.encode(), self.url_scheme_handler)
214 |
215 | if web_greeter_config["config"]["greeter"]["secure_mode"]:
216 | if hasattr(QWebEngineProfile, "setUrlRequestInterceptor"):
217 | self.profile.setUrlRequestInterceptor(self.interceptor)
218 | else: # Older Qt5 versions
219 | self.profile.setRequestInterceptor(self.interceptor)
220 |
221 | @classmethod
222 | def _before_exit(cls):
223 | """Runs before exit"""
224 | screensaver.reset_screensaver()
225 |
226 | def show(self):
227 | """Show window"""
228 | primary: WindowAbstract | None = None
229 | for win in self.windows:
230 | if win.is_primary:
231 | primary = win
232 | continue
233 | win.window.show()
234 | logger.debug("Web Greeter started win: %s", str(win.meta.id))
235 |
236 | if primary and primary.is_primary:
237 | primary.window.show()
238 | primary.window.activateWindow()
239 | primary.window.raise_()
240 | logger.debug("Web Greeter started win: %s", str(primary.meta.id))
241 |
242 | def run(self) -> int:
243 | """Runs the application"""
244 | logger.debug("Web Greeter started")
245 | return self.app.exec_()
246 |
247 | class Browser(Application):
248 | # pylint: disable=too-many-instance-attributes
249 | """The main browser"""
250 |
251 | def __init__(self, application):
252 | super().__init__(application)
253 | self.init()
254 | self.load_theme()
255 |
256 | def init(self):
257 | """Initialize browser"""
258 | logger.debug("Initializing Browser Window")
259 |
260 | if web_greeter_config["config"]["greeter"]["debug_mode"]:
261 | os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = '12345'
262 |
263 | def load_theme(self):
264 | """Load theme"""
265 | load_theme_dir()
266 | primary_html = load_primary_theme_path()
267 | secondary_html = load_secondary_theme_path()
268 |
269 | primary_url = QUrl(f"web-greeter://app/{primary_html}")
270 | secondary_url = QUrl(f"web-greeter://app/{secondary_html}")
271 |
272 | for win in self.windows:
273 | if win.is_primary:
274 | win.window.win_page.setUrl(primary_url)
275 | else:
276 | win.window.win_page.setUrl(secondary_url)
277 |
278 | logger.debug("Theme loaded")
279 |
280 | def primary_window(self):
281 | """Returns the primary window"""
282 | for win in self.windows:
283 | if win.is_primary:
284 | return win.window
285 | raise Exception("No primary window initialized")
286 |
--------------------------------------------------------------------------------
/src/browser/browser_interfaces.py:
--------------------------------------------------------------------------------
1 |
2 | class WindowSize:
3 | width: float
4 | height: float
5 |
6 | def __init__(self, width, height):
7 | self.width = width
8 | self.height = height
9 |
10 | class WindowPosition:
11 | x: float
12 | y: float
13 |
14 | def __init__(self, x, y):
15 | self.x = x
16 | self.y = y
17 |
18 | class OverallBoundary:
19 | minX: float
20 | maxX: float
21 | minY: float
22 | maxY: float
23 |
24 | def __init__(self, minX, maxX, minY, maxY):
25 | self.minX = minX
26 | self.maxX = maxX
27 | self.minY = minY
28 | self.maxY = maxY
29 |
30 | class WindowMetadata:
31 | id: int
32 | is_primary: bool
33 | size: WindowSize
34 | position: WindowPosition
35 | overallBoundary: OverallBoundary
36 |
37 | def __init__(self, id, is_primary, size, position, overallBoundary):
38 | self.id = id
39 | self.is_primary = is_primary
40 | self.size = size
41 | self.position = position
42 | self.overallBoundary = overallBoundary
43 |
--------------------------------------------------------------------------------
/src/browser/error_prompt.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # error_prompt.py
4 | #
5 | # Copyright © 2021 JezerM
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 |
28 | # Standard lib
29 |
30 | # 3rd-Party Libs
31 | from typing import List
32 | from logging import (
33 | getLogger,
34 | DEBUG,
35 | Formatter,
36 | StreamHandler,
37 | )
38 | from PyQt5.QtWebEngineWidgets import QWebEnginePage
39 | from PyQt5.QtWidgets import (
40 | QAbstractButton,
41 | QDialogButtonBox,
42 | QDialog,
43 | QVBoxLayout,
44 | QLabel,
45 | QPushButton
46 | )
47 | from PyQt5.QtGui import QWindow
48 | from config import web_greeter_config
49 |
50 | import globales
51 |
52 | LOG_FORMAT = ''.join([
53 | '%(asctime)s [ %(levelname)s ] %(filename)s %(',
54 | 'lineno)d: %(message)s'
55 | ])
56 | formatter = Formatter(fmt=LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S")
57 | logger = getLogger("javascript")
58 | logger.propagate = False
59 | stream_handler = StreamHandler()
60 | stream_handler.setLevel(DEBUG)
61 | stream_handler.setFormatter(formatter)
62 | logger.setLevel(DEBUG)
63 | logger.addHandler(stream_handler)
64 |
65 | class WebPage(QWebEnginePage):
66 | """web-greeter's webpage class"""
67 |
68 | def javaScriptConsoleMessage(
69 | self, level: QWebEnginePage.JavaScriptConsoleMessageLevel,
70 | message: str, line_number: int, source_id: str
71 | ):
72 | # pylint: disable = no-self-use,missing-function-docstring,invalid-name
73 | if source_id == "":
74 | source_id = "console"
75 |
76 | log_level = 0
77 | if level == WebPage.ErrorMessageLevel:
78 | log_level = 40
79 | elif level == WebPage.WarningMessageLevel:
80 | log_level = 30
81 | elif level == WebPage.InfoMessageLevel:
82 | return
83 | else:
84 | return
85 |
86 | record = logger.makeRecord(
87 | name="javascript",
88 | level=log_level,
89 | fn="",
90 | lno=line_number,
91 | msg=message,
92 | args=(),
93 | exc_info=None
94 | )
95 | record.filename = source_id
96 | logger.handle(record)
97 |
98 | if log_level == 40:
99 | errorMessage = f"{source_id} {line_number}: {message}"
100 | error_prompt(errorMessage)
101 |
102 | def increaseZoom(self, value = 0.1):
103 | """Increase zoom"""
104 | # pylint: disable=invalid-name
105 | self.setZoomFactor(
106 | self.zoomFactor() + (value if value else 0.1)
107 | )
108 |
109 | def decreaseZoom(self, value = 0.1):
110 | """Increase zoom"""
111 | # pylint: disable=invalid-name
112 | self.setZoomFactor(
113 | self.zoomFactor() - (value if value else 0.1)
114 | )
115 |
116 | class Dialog(QDialog):
117 | """Popup dialog class"""
118 |
119 | def __init__(
120 | self, parent = None, title: str = "Dialog",
121 | message: str = "Message", detail: str = "",
122 | buttons: List[str] = None
123 | ):
124 | super().__init__(parent)
125 | self.setWindowTitle(title)
126 |
127 | self.button_box = QDialogButtonBox()
128 | if buttons is not None:
129 | for i, btn in enumerate(buttons, 0):
130 | button = QPushButton(btn)
131 | button.role = i
132 | self.button_box.addButton(button, QDialogButtonBox.NoRole)
133 |
134 | self.button_box.clicked.connect(self.handle_click)
135 |
136 | self.layout = QVBoxLayout()
137 | self.layout.addWidget(QLabel(message))
138 | self.layout.addWidget(QLabel(detail))
139 | self.layout.addWidget(self.button_box)
140 |
141 | self.setLayout(self.layout)
142 |
143 | def handle_click(self, button: QAbstractButton):
144 | """Handle click of button"""
145 | self.done(button.role)
146 |
147 | def general_error_prompt(window: QWindow, message: str, detail: str, title: str):
148 | """General error prompt"""
149 | dialog = Dialog(parent = window,
150 | title = title,
151 | message = message,
152 | detail = detail,
153 | buttons = ["Reload theme", "Use default theme", "Cancel"])
154 | dialog.exec()
155 | result = dialog.result()
156 |
157 | if result == 2: # Cancel
158 | pass
159 | elif result == 1: # Default theme
160 | web_greeter_config["config"]["greeter"]["theme"] = "gruvbox"
161 | globales.greeter.load_theme()
162 | elif result == 0: # Reload
163 | globales.greeter.load_theme()
164 |
165 |
166 | def error_prompt(err: str):
167 | """Prompts a popup dialog on error"""
168 | if not web_greeter_config["config"]["greeter"]["detect_theme_errors"]:
169 | return
170 |
171 | general_error_prompt(globales.greeter.primary_window(),
172 | "An error ocurred. Do you want to change to default theme?",
173 | f"{err}",
174 | "An error ocurred")
175 |
--------------------------------------------------------------------------------
/src/browser/interceptor.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # interceptor.py
4 | #
5 | # Copyright © 2016-2017 Antergos
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 |
28 | # 3rd-Party Libs
29 | from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
30 |
31 | class QtUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
32 | """Url request interceptor for web-greeter's protocol"""
33 |
34 | def __init__(self, url_scheme: str):
35 | super().__init__()
36 | self._url_scheme = url_scheme
37 |
38 | def intercept_request(self, info: QWebEngineUrlRequestInfo) -> None:
39 | """Intercept request"""
40 | url = info.requestUrl().toString()
41 | not_webg_uri = self._url_scheme != info.requestUrl().scheme()
42 | not_data_uri = 'data' != info.requestUrl().scheme()
43 | not_local_file = not info.requestUrl().isLocalFile()
44 |
45 | # print(url)
46 |
47 | not_devtools = (
48 | not url.startswith('http://127.0.0.1') and
49 | not url.startswith('ws://127.0.0.1')
50 | and not url.startswith('devtools')
51 | )
52 |
53 | block_request = (
54 | not_devtools and not_data_uri and
55 | not_webg_uri and not_local_file
56 | )
57 |
58 | info.block(block_request) # Block everything that is not allowed
59 |
60 | def interceptRequest(self, info: QWebEngineUrlRequestInfo) -> None:
61 | # pylint: disable=invalid-name,missing-function-docstring
62 | self.intercept_request(info)
63 |
--------------------------------------------------------------------------------
/src/browser/url_scheme.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # url_scheme.py
4 | #
5 | # Copyright © 2016-2018 Antergos
6 | # Copyright © 2021 JezerM
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | """ Custom Url Scheme Handler """
30 |
31 | # Standard Lib
32 | import os
33 | import mimetypes
34 |
35 | # 3rd-Party Libs
36 | from PyQt5.QtCore import QBuffer, QIODevice
37 | from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler, QWebEngineUrlRequestJob
38 |
39 |
40 | class QtUrlSchemeHandler(QWebEngineUrlSchemeHandler):
41 | """URL Scheme Handler for web-greeter's protocol"""
42 |
43 | def requestStarted(self, job: QWebEngineUrlRequestJob) -> None:
44 | # pylint: disable=invalid-name,missing-function-docstring
45 | path = job.requestUrl().path()
46 | path = os.path.realpath(path)
47 |
48 | # print("PATH", job.requestUrl().path())
49 |
50 | if not path:
51 | # print("JOB FAIL", path)
52 | job.fail(QWebEngineUrlRequestJob.UrlInvalid)
53 | return
54 |
55 | if not os.path.exists(path):
56 | # print("NOT FOUND", path)
57 | job.fail(QWebEngineUrlRequestJob.UrlNotFound)
58 | return
59 |
60 | try:
61 | with open(path, 'rb') as file:
62 | content_type = mimetypes.guess_type(path)
63 | if content_type[0] is None:
64 | content_type = ("text/plain", None)
65 | buffer = QBuffer(parent=self)
66 |
67 | buffer.open(QIODevice.WriteOnly)
68 | buffer.write(file.read())
69 | buffer.seek(0)
70 | buffer.close()
71 |
72 | if content_type[0] is None:
73 | job.reply("text/plain", "")
74 | else:
75 | job.reply(content_type[0].encode(), buffer)
76 |
77 | except Exception as err:
78 | raise err
79 |
--------------------------------------------------------------------------------
/src/config.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # config.py
4 | #
5 | # Copyright © 2021 JezerM
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 | # Standard lib
28 |
29 | import os
30 | from ruamel import yaml
31 |
32 | from logger import logger
33 |
34 | PATH_TO_CONFIG = "/etc/lightdm/web-greeter.yml"
35 |
36 | yaml_loader = yaml.YAML(typ='safe', pure=True)
37 |
38 | web_greeter_config = {
39 | "config": {
40 | "branding": {
41 | "background_images_dir": "/usr/share/backgrounds",
42 | "logo_image": "",
43 | "user_image": "",
44 | },
45 | "greeter": {
46 | "debug_mode": False,
47 | "detect_theme_errors": True,
48 | "screensaver_timeout": 300,
49 | "secure_mode": True,
50 | "theme": "gruvbox",
51 | "icon_theme": None,
52 | "time_language": None,
53 | },
54 | "layouts": ["us", "latam"],
55 | "features": {
56 | "battery": False,
57 | "backlight": {
58 | "enabled": False,
59 | "value": 10,
60 | "steps": 0,
61 | }
62 | }
63 | },
64 | "app": {
65 | "fullscreen": True,
66 | "frame": False,
67 | "debug_mode": False,
68 | "theme_dir": "/usr/share/web-greeter/themes/",
69 | "version": {
70 | "full": "3.5.3",
71 | "major": 3,
72 | "minor": 5,
73 | "micro": 3,
74 | },
75 | "api_version": {
76 | "full": "1.0.0",
77 | "major:": 1,
78 | "minor": 0,
79 | "micro": 0,
80 | },
81 | },
82 | "theme": {
83 | "primary_html": "index.html",
84 | "secondary_html": "",
85 | }
86 | }
87 |
88 | path_to_config = os.getenv("WEB_GREETER_CONFIG") or "/etc/lightdm/web-greeter.yml"
89 |
90 | theme_dir = None
91 |
92 | def load_theme_dir() -> str:
93 | """Loads the theme directory"""
94 | theme: str = web_greeter_config["config"]["greeter"]["theme"]
95 | directory: str = web_greeter_config["app"]["theme_dir"]
96 | def_theme = "gruvbox"
97 |
98 | theme_dir = os.path.join(directory, theme)
99 |
100 | if theme.startswith("/"):
101 | theme_dir = theme
102 | elif theme.__contains__(".") or theme.__contains__("/"):
103 | theme_dir = os.path.join(os.getcwd(), theme)
104 |
105 | if theme_dir.endswith(".html"):
106 | theme_dir = os.path.dirname(theme_dir)
107 |
108 | if not os.path.exists(theme_dir):
109 | logger.warn("\"%s\" theme does not exists. Using \"%s\" theme",
110 | theme, def_theme)
111 | theme_dir = os.path.join(directory, def_theme)
112 |
113 | return theme_dir
114 |
115 | def load_primary_theme_path() -> str:
116 | """
117 | Loads the primary theme path
118 | The provided theme with `--theme` flag is preferred over index.yml
119 | """
120 | global theme_dir
121 | if not theme_dir:
122 | theme_dir = load_theme_dir()
123 | abs_theme: str = web_greeter_config["config"]["greeter"]["theme"]
124 | abs_theme_name = abs_theme.split("/").pop()
125 | directory: str = web_greeter_config["app"]["theme_dir"]
126 | def_theme = "gruvbox"
127 |
128 | if abs_theme_name.endswith(".html"):
129 | web_greeter_config["theme"]["primary_html"] = abs_theme_name
130 |
131 | primary = web_greeter_config["theme"]["primary_html"]
132 | path_to_theme = os.path.join(theme_dir, primary)
133 |
134 | if not path_to_theme.endswith(".html"):
135 | path_to_theme = os.path.join(path_to_theme, "index.html")
136 |
137 | if not os.path.exists(path_to_theme):
138 | logger.warn("\"%s\" theme does not exists. Using \"%s\" theme",
139 | path_to_theme, def_theme)
140 | path_to_theme = os.path.join(directory, def_theme, "index.html")
141 |
142 | web_greeter_config["config"]["greeter"]["theme"] = path_to_theme
143 | return path_to_theme
144 |
145 | def load_secondary_theme_path() -> str:
146 | """
147 | Loads the secondary theme path
148 | This can only be set with index.yml, either it defaults to primary html
149 | """
150 | global theme_dir
151 | if not theme_dir:
152 | theme_dir = load_theme_dir()
153 | primary = web_greeter_config["theme"]["primary_html"]
154 | secondary = web_greeter_config["theme"]["secondary_html"]
155 | path_to_theme = os.path.join(theme_dir, secondary or primary)
156 |
157 | if not path_to_theme.endswith(".html"):
158 | path_to_theme = os.path.join(path_to_theme, "index.html")
159 |
160 | if not os.path.exists(path_to_theme):
161 | logger.warn("\"%s\" does not exists. Using \"%s\" for secondary monitors",
162 | secondary, primary)
163 | path_to_theme = load_primary_theme_path()
164 |
165 | return path_to_theme
166 |
167 | def load_theme_config():
168 | """Loads the theme config inside "index.yml" """
169 | global theme_dir
170 | if not theme_dir:
171 | theme_dir = load_theme_dir()
172 | path_to_theme_config = os.path.join(theme_dir, "index.yml")
173 |
174 | try:
175 | if not os.path.exists(path_to_theme_config):
176 | raise Exception("index.yml file not found")
177 | with open(path_to_theme_config, "r", encoding="utf-8") as file:
178 | theme_config = yaml_loader.load(file)
179 | web_greeter_config["theme"] = theme_config
180 |
181 | except Exception as err:
182 | logger.warn("Theme config was not loaded:\n\t%s", err)
183 | logger.debug("Using default theme config")
184 |
185 | def ensure_theme():
186 | """
187 | Ensures that the theme does exists
188 | If it doesn't, default theme (gruvbox) is used
189 | """
190 | global theme_dir
191 | if not theme_dir:
192 | theme_dir = load_theme_dir()
193 | primary = web_greeter_config["theme"]["primary_html"]
194 | directory = web_greeter_config["app"]["theme_dir"]
195 | def_theme = "gruvbox"
196 |
197 | primary_exists = os.path.exists(os.path.join(theme_dir, primary))
198 |
199 | if not primary_exists:
200 | theme_dir = os.path.join(directory, def_theme)
201 | load_theme_config()
202 |
203 | def load_config():
204 | """Load web-greeter's config"""
205 | try:
206 | if not os.path.exists(PATH_TO_CONFIG):
207 | raise Exception("Config file not found")
208 | with open(PATH_TO_CONFIG, "r", encoding="utf-8") as file:
209 | web_greeter_config["config"] = yaml_loader.load(file)
210 | except Exception as err:
211 | logger.error("Config was not loaded:\n\t%s", err)
212 |
213 | load_config()
214 |
--------------------------------------------------------------------------------
/src/globales.py:
--------------------------------------------------------------------------------
1 | """Global variables"""
2 |
3 | from bridge import Config, Greeter, ThemeUtils
4 | from browser.browser import Browser
5 |
6 | greeter: Browser # pylint: disable=invalid-name
7 |
8 | LDMGreeter: Greeter
9 | LDMGreeterConfig: Config
10 | LDMThemeUtils: ThemeUtils
11 |
--------------------------------------------------------------------------------
/src/logger.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # logger.py
4 | #
5 | # Copyright © 2017 Antergos
6 | # Copyright © 2021 JezerM
7 | #
8 | # This file is part of Web Greeter.
9 | #
10 | # Web Greeter is free software; you can redistribute it and/or modify
11 | # it under the terms of the GNU General Public License as published by
12 | # the Free Software Foundation; either version 3 of the License, or
13 | # (at your option) any later version.
14 | #
15 | # Web Greeter is distributed in the hope that it will be useful,
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | # GNU General Public License for more details.
19 | #
20 | # The following additional terms are in effect as per Section 7 of the license:
21 | #
22 | # The preservation of all legal notices and author attributions in
23 | # the material or in the Appropriate Legal Notices displayed
24 | # by works containing it is required.
25 | #
26 | # You should have received a copy of the GNU General Public License
27 | # along with Web Greeter; If not, see .
28 |
29 | from logging import (
30 | getLogger,
31 | DEBUG,
32 | Formatter,
33 | StreamHandler
34 | )
35 |
36 | LOG_FORMAT = ''.join([
37 | '%(asctime)s [ %(levelname)s ] %(module)s - %(filename)s:%(',
38 | 'lineno)d : %(funcName)s | %(message)s'
39 | ])
40 | formatter = Formatter(fmt=LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S")
41 | stream_handler = StreamHandler()
42 |
43 | logger = getLogger("debug")
44 |
45 | stream_handler.setLevel(DEBUG)
46 | stream_handler.setFormatter(formatter)
47 | logger.propagate = False
48 | logger.setLevel(DEBUG)
49 | logger.addHandler(stream_handler)
50 |
--------------------------------------------------------------------------------
/src/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JezerM/web-greeter/0bfa7f0036b2336c4d9aa9ad35e0777ab0b41857/src/requirements.txt
--------------------------------------------------------------------------------
/src/resources/css/style.css:
--------------------------------------------------------------------------------
1 | QMainWindow {
2 | background: #000000;
3 | }
4 |
5 | /*QWidget {*/
6 | /*background: #282828;*/
7 | /*color: #ebdbb2;*/
8 | /*}*/
9 |
10 | /*window,*/
11 | /*GtkWindow {*/
12 | /*background: #000000;*/
13 | /*}*/
14 |
15 | /*dialog .dialog-vbox > label,*/
16 | /*GtkDialog .dialog-vbox > GtkLabel {*/
17 | /*margin: 20px 15px;*/
18 | /*}*/
19 |
--------------------------------------------------------------------------------
/src/resources/js/GreeterComm.js:
--------------------------------------------------------------------------------
1 | /*
2 | * GreeterComm.js
3 | *
4 | * Copyright © 2022 JezerM
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 | function inf_to_infinity(num) {
29 | if (num === "infinity") return Infinity;
30 | else if (num === "-infinity") return -Infinity;
31 | else return num;
32 | }
33 |
34 | class GreeterComm {
35 | static _comm = null; // Bridge object
36 | static _instance = null; // GreeterComm object
37 |
38 | _windowMetadata = null;
39 |
40 | constructor(comm) {
41 | if (GreeterComm._instance !== null) {
42 | return GreeterComm._instance;
43 | }
44 |
45 | GreeterComm._comm = comm;
46 | GreeterComm._comm.broadcast_signal.connect(this._on_broadcast);
47 |
48 | // Obtain metadata and fire the whenReady promise
49 | GreeterComm._comm.metadata_signal.connect((metadata) => {
50 | let meta = metadata;
51 | meta.overallBoundary.minX = inf_to_infinity(meta.overallBoundary.minX);
52 | meta.overallBoundary.minY = inf_to_infinity(meta.overallBoundary.minY);
53 | meta.overallBoundary.maxX = inf_to_infinity(meta.overallBoundary.maxX);
54 | meta.overallBoundary.maxY = inf_to_infinity(meta.overallBoundary.maxY);
55 | this._windowMetadata = meta;
56 | if (this._ready) this._ready();
57 | });
58 |
59 | this._readyPromise = new Promise((resolve) => {
60 | this._ready = resolve;
61 | });
62 |
63 | // Send initial request for metadata
64 | GreeterComm._comm.requestMetadata();
65 |
66 | GreeterComm._instance = this;
67 | }
68 |
69 | get window_metadata() {
70 | if (this._windowMetadata) {
71 | return this._windowMetadata;
72 | }
73 | throw new Error(`window_metadata not available, did you wait for the GreeterReady event?`);
74 | }
75 |
76 | whenReady() {
77 | return this._readyPromise;
78 | }
79 |
80 | broadcast(data) {
81 | GreeterComm._comm.broadcast(data);
82 | }
83 |
84 | _on_broadcast(window_meta, data) {
85 | const event = new Event("GreeterBroadcastEvent");
86 | event.window = window_meta;
87 | event.data = data;
88 | window.dispatchEvent(event);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/resources/js/ThemeUtils.js:
--------------------------------------------------------------------------------
1 | /*
2 | * ThemeUtils.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 | let time_language = null,
29 | _ThemeUtils = null;
30 |
31 | /**
32 | * Provides various utility methods for use in greeter themes. The greeter will automatically
33 | * create an instance of this class when it starts. The instance can be accessed
34 | * with the global variable: [`theme_utils`](#dl-window-theme_utils).
35 | *
36 | * @memberOf LightDM
37 | */
38 | class ThemeUtils {
39 | constructor(instance) {
40 | if (null !== _ThemeUtils) {
41 | return _ThemeUtils;
42 | }
43 |
44 | _ThemeUtils = instance;
45 | }
46 |
47 | /**
48 | * Binds `this` to class, `context`, for all of the class's methods.
49 | *
50 | * @arg {object} context An ES6 class instance with at least one method.
51 | *
52 | * @return {object} `context` with `this` bound to it for all of its methods.
53 | */
54 | bind_this(context) {
55 | let excluded_methods = ["constructor"];
56 |
57 | function not_excluded(_method, _context) {
58 | let is_excluded =
59 | excluded_methods.findIndex((excluded_method) => _method === excluded_method) >
60 | -1,
61 | is_method = "function" === typeof _context[_method];
62 |
63 | return is_method && !is_excluded;
64 | }
65 |
66 | for (let obj = context; obj; obj = Object.getPrototypeOf(obj)) {
67 | // Stop once we have traveled all the way up the inheritance chain
68 | if ("Object" === obj.constructor.name) {
69 | break;
70 | }
71 |
72 | for (let method of Object.getOwnPropertyNames(obj)) {
73 | if (not_excluded(method, context)) {
74 | context[method] = context[method].bind(context);
75 | }
76 | }
77 | }
78 |
79 | return context;
80 | }
81 |
82 | /**
83 | * Returns the contents of directory found at `path` provided that the (normalized) `path`
84 | * meets at least one of the following conditions:
85 | * * Is located within the greeter themes' root directory.
86 | * * Has been explicitly allowed in the greeter's config file.
87 | * * Is located within the greeter's shared data directory (`/var/lib/lightdm-data`).
88 | * * Is located in `/tmp`.
89 | *
90 | * @param {string} path The abs path to desired directory.
91 | * @param {boolean} only_images Include only images in the results. Default `true`.
92 | * @param {function(string[])} callback Callback function to be called with the result.
93 | */
94 | dirlist(path, only_images = true, callback) {
95 | if ("" === path || "string" !== typeof path) {
96 | console.error(`theme_utils.dirlist(): path must be a non-empty string!`);
97 | return callback([]);
98 | } else if (null !== path.match(/^[^/].+/)) {
99 | return _ThemeUtils.dirlist(path, only_images, callback);
100 | }
101 |
102 | if (null !== path.match(/\/\.+(?=\/)/)) {
103 | // No special directory names allowed (eg ../../)
104 | path = path.replace(/\/\.+(?=\/)/g, "");
105 | }
106 |
107 | try {
108 | return _ThemeUtils.dirlist(path, only_images, callback);
109 | } catch (err) {
110 | console.error(`theme_utils.dirlist(): ${err}`);
111 | return callback([]);
112 | }
113 | }
114 |
115 | /**
116 | * Get the current date in a localized format. Local language is autodetected by default, but can be set manually in the greeter config file.
117 | * * `language` defaults to the system's language, but can be set manually in the config file.
118 | *
119 | * @returns {Object} The current date.
120 | */
121 | get_current_localized_date() {
122 | let config = greeter_config.greeter;
123 |
124 | var locale = [];
125 |
126 | if (time_language === null) {
127 | time_language = config.time_language || "";
128 | }
129 |
130 | if (time_language != "") {
131 | locale.push(time_language);
132 | }
133 |
134 | let optionsDate = { day: "2-digit", month: "2-digit", year: "2-digit" };
135 |
136 | let fmtDate = Intl.DateTimeFormat(locale, optionsDate);
137 |
138 | let now = new Date();
139 | var date = fmtDate.format(now);
140 |
141 | return date;
142 | }
143 |
144 | /**
145 | * Get the current time in a localized format. Local language is autodetected by default, but can be set manually in the greeter config file.
146 | * * `language` defaults to the system's language, but can be set manually in the config file.
147 | *
148 | * @returns {Object} The current time.
149 | */
150 | get_current_localized_time() {
151 | let config = greeter_config.greeter;
152 |
153 | var locale = [];
154 |
155 | if (time_language === null) {
156 | time_language = config.time_language || "";
157 | }
158 |
159 | if (time_language != "") {
160 | locale.push(time_language);
161 | }
162 |
163 | let optionsTime = { hour: "2-digit", minute: "2-digit" };
164 |
165 | let fmtTime = Intl.DateTimeFormat(locale, optionsTime);
166 |
167 | let now = new Date();
168 | var time = fmtTime.format(now);
169 |
170 | return time;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/resources/js/bootstrap.js:
--------------------------------------------------------------------------------
1 | /*
2 | * bootstrap.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 | (() => {
29 | let _channel;
30 |
31 | /**
32 | * Greeter Ready Event. Themes should not initialize until this event has fired.
33 | * @event window#GreeterReady
34 | * @name GreeterReady
35 | * @type Event
36 | * @memberOf window
37 | */
38 | window._ready_event = new Event("GreeterReady");
39 |
40 | let dispatched = false;
41 |
42 | function dispatch_channel_ready_cb() {
43 | if (window.lightdm === undefined) {
44 | console.debug(
45 | "LIGHTDM:",
46 | "Document loaded before channel is done. Won't dispatch GreeterReady"
47 | );
48 | return;
49 | }
50 | if (dispatched) {
51 | console.debug("LIGHTDM:", "GreeterReady already dispatched");
52 | return;
53 | }
54 | dispatched = true;
55 | setTimeout(function () {
56 | console.debug("LIGHTDM:", "Event dispatch");
57 | window.dispatchEvent(_ready_event);
58 | }, 2);
59 | }
60 |
61 | function channel_ready_cb(channel) {
62 | _channel = channel;
63 |
64 | /**
65 | * Greeter Instance
66 | * @name lightdm
67 | * @type {LightDM.Greeter}
68 | * @memberOf window
69 | */
70 | window.lightdm = _channel.objects.LightDMGreeter;
71 |
72 | /**
73 | * Greeter Config - Access values from the greeter's config file.
74 | * @name greeter_config
75 | * @type {LightDM.GreeterConfig}
76 | * @memberOf window
77 | */
78 | window.greeter_config = _channel.objects.Config;
79 |
80 | /**
81 | * Theme Utils - various utility methods for use in greeter themes.
82 | * @name theme_utils
83 | * @type {LightDM.ThemeUtils}
84 | * @memberOf window
85 | */
86 | window.theme_utils = new ThemeUtils(_channel.objects.ThemeUtils);
87 |
88 | window.greeter_comm = new GreeterComm(_channel.objects.Comm);
89 |
90 | console.debug("LIGHTDM:", "Channel is done");
91 | console.debug("LIGHTDM:", "Document state", document.readyState);
92 |
93 | if (document.readyState === "loading") {
94 | document.addEventListener("DOMContentLoaded", dispatch_channel_ready_cb);
95 | } else if (document.readyState === "interactive") {
96 | window.addEventListener("load", dispatch_channel_ready_cb);
97 | } else {
98 | dispatch_channel_ready_cb();
99 | }
100 | }
101 | new QWebChannel(qt.webChannelTransport, channel_ready_cb);
102 |
103 | window.addEventListener("DOMContentLoaded", dispatch_channel_ready_cb);
104 | })();
105 |
--------------------------------------------------------------------------------
/src/resources/js/docs/Greeter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * LightDMGreeter.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | /**
30 | * Base class for the greeter's Theme JavaScript API. Greeter themes will interact
31 | * directly with an object derived from this class to facilitate the user log-in process.
32 | * The greeter will automatically create an instance when it starts.
33 | * The instance can be accessed using the global variable: [`lightdm`](#dl-window-lightdm).
34 | *
35 | * @memberOf LightDM
36 | */
37 | class Greeter {
38 |
39 | constructor() {
40 | if ( 'lightdm' in window ) {
41 | return window.lightdm;
42 | }
43 |
44 | window.lightdm = GreeterUtils.bind_this( this );
45 |
46 | return window.lightdm;
47 | }
48 |
49 | /**
50 | * The username of the user being authenticated or {@link null}
51 | * if there is no authentication in progress.
52 | * @type {string|null}
53 | * @readonly
54 | */
55 | get authentication_user() {}
56 |
57 | /**
58 | * Whether or not the guest account should be automatically logged
59 | * into when the timer expires.
60 | * @type {boolean}
61 | * @readonly
62 | */
63 | get autologin_guest() {}
64 |
65 | /**
66 | * The number of seconds to wait before automatically logging in.
67 | * @type {number}
68 | * @readonly
69 | */
70 | get autologin_timeout() {}
71 |
72 | /**
73 | * The username with which to automatically log in when the timer expires.
74 | * @type {string}
75 | * @readonly
76 | */
77 | get autologin_user() {}
78 |
79 | /**
80 | * Whether or not the greeter can make the system hibernate.
81 | * @type {boolean}
82 | * @readonly
83 | */
84 | get can_hibernate() {}
85 |
86 | /**
87 | * Whether or not the greeter can make the system restart.
88 | * @type {boolean}
89 | * @readonly
90 | */
91 | get can_restart() {}
92 |
93 | /**
94 | * Whether or not the greeter can make the system shutdown.
95 | * @type {boolean}
96 | * @readonly
97 | */
98 | get can_shutdown() {}
99 |
100 | /**
101 | * Whether or not the greeter can make the system suspend/sleep.
102 | * @type {boolean}
103 | * @readonly
104 | */
105 | get can_suspend() {}
106 |
107 | /**
108 | * The name of the default session.
109 | * @type {string}
110 | * @readonly
111 | */
112 | get default_session() {}
113 |
114 | /**
115 | * Whether or not guest sessions are supported.
116 | * @type {boolean}
117 | * @readonly
118 | */
119 | get has_guest_account() {}
120 |
121 | /**
122 | * Whether or not user accounts should be hidden.
123 | * @type {boolean}
124 | * @readonly
125 | */
126 | get hide_users_hint() {}
127 |
128 | /**
129 | * The system's hostname.
130 | * @type {string}
131 | * @readonly
132 | */
133 | get hostname() {}
134 |
135 | /**
136 | * Whether or not the greeter is in the process of authenticating.
137 | * @type {boolean}
138 | * @readonly
139 | */
140 | get in_authentication() {}
141 |
142 | /**
143 | * Whether or not the greeter has successfully authenticated.
144 | * @type {boolean}
145 | * @readonly
146 | */
147 | get is_authenticated() {}
148 |
149 | /**
150 | * The current language or {@link null} if no language.
151 | * @type {LightDM.Language|null}
152 | * @readonly
153 | */
154 | get language() {}
155 |
156 | /**
157 | * A list of languages to present to the user.
158 | * @type {LightDM.Language[]}
159 | * @readonly
160 | */
161 | get languages() {}
162 |
163 | /**
164 | * The currently active layout for the selected user.
165 | * @type {LightDM.Layout}
166 | */
167 | get layout() {}
168 |
169 | /**
170 | * Set the active layout for the selected user.
171 | * @param {LightDM.Layout} value
172 | */
173 | set layout( value ) {}
174 |
175 | /**
176 | * A list of keyboard layouts to present to the user.
177 | * @type {LightDM.Layout[]}
178 | * @readonly
179 | */
180 | get layouts() {}
181 |
182 | /**
183 | * Whether or not the greeter was started as a lock screen.
184 | * @type {boolean}
185 | * @readonly
186 | */
187 | get lock_hint() {}
188 |
189 | /**
190 | * The available remote sessions.
191 | * @type {LightDM.Session[]}
192 | * @readonly
193 | */
194 | get remote_sessions() {}
195 |
196 | /**
197 | * Whether or not the guest account should be selected by default.
198 | * @type {boolean}
199 | * @readonly
200 | */
201 | get select_guest_hint() {}
202 |
203 | /**
204 | * The username to select by default.
205 | * @type {string}
206 | * @readonly
207 | */
208 | get select_user_hint() {}
209 |
210 | /**
211 | * List of available sessions.
212 | * @type {LightDM.Session[]}
213 | * @readonly
214 | */
215 | get sessions() {}
216 |
217 | /**
218 | * Check if a manual login option should be shown. If {@link true}, the theme should
219 | * provide a way for a username to be entered manually. Otherwise, themes that show
220 | * a user list may limit logins to only those users.
221 | * @type {string}
222 | * @readonly
223 | */
224 | get show_manual_login_hint() {}
225 |
226 | /**
227 | * Check if a remote login option should be shown. If {@link true}, the theme should provide
228 | * a way for a user to log into a remote desktop server.
229 | * @type {string}
230 | * @readonly
231 | * @internal
232 | */
233 | get show_remote_login_hint() {}
234 |
235 | /**
236 | * List of available users.
237 | * @type {LightDM.User[]}
238 | * @readonly
239 | */
240 | get users() {}
241 |
242 |
243 | /**
244 | * Starts the authentication procedure for a user.
245 | *
246 | * @arg {String|null} username A username or {@link null} to prompt for a username.
247 | */
248 | authenticate( username = null ) {}
249 |
250 | /**
251 | * Starts the authentication procedure for the guest user.
252 | */
253 | authenticate_as_guest() {}
254 |
255 | /**
256 | * Cancel the user authentication that is currently in progress.
257 | */
258 | cancel_authentication() {}
259 |
260 | /**
261 | * Cancel the automatic login.
262 | */
263 | cancel_autologin() {}
264 |
265 | /**
266 | * Triggers the system to hibernate.
267 | * @returns {boolean} {@link true} if hibernation initiated, otherwise {@link false}
268 | */
269 | hibernate() {}
270 |
271 | /**
272 | * Provide a response to a prompt.
273 | * @arg {string} response
274 | */
275 | respond( response ) {}
276 |
277 | /**
278 | * Triggers the system to restart.
279 | * @returns {boolean} {@link true} if restart initiated, otherwise {@link false}
280 | */
281 | restart() {}
282 |
283 | /**
284 | * Set the language for the currently authenticated user.
285 | * @arg {string} language The language in the form of a locale specification (e.g. 'de_DE.UTF-8')
286 | * @returns {boolean} {@link true} if successful, otherwise {@link false}
287 | */
288 | set_language( language ) {}
289 |
290 | /**
291 | * Triggers the system to shutdown.
292 | * @returns {boolean} {@link true} if shutdown initiated, otherwise {@link false}
293 | */
294 | shutdown() {}
295 |
296 | /**
297 | * Start a session for the authenticated user.
298 | * @arg {String|null} session The session to log into or {@link null} to use the default.
299 | * @returns {boolean} {@link true} if successful, otherwise {@link false}
300 | */
301 | start_session( session ) {}
302 |
303 | /**
304 | * Triggers the system to suspend/sleep.
305 | * @returns {boolean} {@link true} if suspend/sleep initiated, otherwise {@link false}
306 | */
307 | suspend() {}
308 |
309 | /**
310 | * Gets the brightness
311 | * @type {Number}
312 | * @readonly
313 | */
314 | get brightness() {}
315 |
316 | /**
317 | * Set the brightness to quantity
318 | * @arg {Number} quantity The quantity to set
319 | */
320 | brightnessSet( quantity ) {}
321 |
322 | /**
323 | * Increase the brightness by quantity
324 | * @arg {Number} quantity The quantity to increase
325 | */
326 | brightnessIncrease( quantity ) {}
327 |
328 | /**
329 | * Decrease the brightness by quantity
330 | * @arg {Number} quantity The quantity to decrease
331 | */
332 | brightnessDecrease( quantity ) {}
333 |
334 | /**
335 | * Gets the battery data
336 | * @type {Object}
337 | * @readonly
338 | */
339 | get batteryData() {}
340 |
341 | /**
342 | * Whether or not the greeter can access to battery data
343 | * @type {boolean}
344 | * @readonly
345 | */
346 | get can_access_battery() {}
347 |
348 | /**
349 | * Updates the battery data
350 | */
351 | batteryUpdate() {}
352 |
353 | /**
354 | * Whether or not the greeter can control display brightness
355 | * @type {boolean}
356 | * @readonly
357 | */
358 | get can_access_brightness() {}
359 |
360 | }
361 |
362 |
363 | /**
364 | * Moment.js instance - Loaded and instantiated automatically by the greeter.
365 | * @name moment
366 | * @type {object}
367 | * @version 2.17.0
368 | * @memberOf window
369 | * @see [Moment.js Documentation](http://momentjs.com/docs)
370 | */
371 |
372 | /**
373 | * jQuery instance - Themes must manually load the included vendor script in order to use this object.
374 | * @name jQuery
375 | * @type {object}
376 | * @version 3.1.1
377 | * @memberOf window
378 | * @see [jQuery Documentation](http://api.jquery.com)
379 | */
380 |
381 | /**
382 | * jQuery instance
383 | * @name $
384 | * @memberOf window
385 | * @see {@link window.jQuery}
386 | */
387 |
388 | /**
389 | * JS-Cookie instance - Themes must manually load the included vendor script in order to use this object.
390 | * @name Cookies
391 | * @type {object}
392 | * @version 2.1.3
393 | * @memberOf window
394 | * @see [JS Cookie Documentation](https://github.com/js-cookie/js-cookie/tree/latest#readme)
395 | */
396 |
397 |
398 |
399 |
--------------------------------------------------------------------------------
/src/resources/js/docs/GreeterConfig.js:
--------------------------------------------------------------------------------
1 | /*
2 | * GreeterConfig.js
3 | *
4 | * Copyright © 2017 Antergos Developers
5 | *
6 | * This file is part of Web Greeter.
7 | *
8 | * Web Greeter is free software; you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation; either version 3 of the License, or
11 | * (at your option) any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with web-greeter; If not, see .
26 | */
27 |
28 |
29 | /**
30 | * Provides greeter themes with a way to access values from the greeter's config
31 | * file located at `/etc/lightdm/web-greeter.yml`. The greeter will
32 | * create an instance of this class when it starts. The instance can be accessed
33 | * with the global variable: [`greeter_config`](#dl-window-greeter_config).
34 | *
35 | * @memberOf LightDM
36 | */
37 | class GreeterConfig {
38 | /**
39 | * Holds keys/values from the `branding` section of the config file.
40 | *
41 | * @type {object} branding
42 | * @prop {string} background_images_dir Path to directory that contains background images
43 | * for use in greeter themes.
44 | * @prop {string} logo Path to distro logo image for use in greeter themes.
45 | * @prop {string} user_image Default user image/avatar. This is used by greeter themes
46 | * for users that have not configured a `.face` image.
47 | * @readonly
48 | */
49 | get branding() {}
50 |
51 | /**
52 | * Holds keys/values from the `greeter` section of the config file.
53 | *
54 | * @type {object} greeter
55 | * @prop {boolean} debug_mode Greeter theme debug mode.
56 | * @prop {boolean} detect_theme_errors Provide an option to load a fallback theme when theme
57 | * errors are detected.
58 | * @prop {number} screensaver_timeout Blank the screen after this many seconds of inactivity.
59 | * @prop {boolean} secure_mode Don't allow themes to make remote http requests.
60 | * @prop {string} time_format A moment.js format string to be used by the greeter to
61 | * generate localized time for display.
62 | * @prop {string} time_language Language to use when displaying the time or `auto`
63 | * to use the system's language.
64 | * @prop {string} theme The name of the theme to be used by the greeter.
65 | * @readonly
66 | */
67 | get greeter() {}
68 | }
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/resources/js/docs/LightDMObjects.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © 2015-2017 Antergos
3 | *
4 | * LightDMObjects.js
5 | *
6 | * This file is part of Web Greeter
7 | *
8 | * Web Greeter is free software: you can redistribute it and/or modify
9 | * it under the terms of the GNU General Public License as published by
10 | * the Free Software Foundation, either version 3 of the License,
11 | * or any later version.
12 | *
13 | * Web Greeter is distributed in the hope that it will be useful,
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | * GNU General Public License for more details.
17 | *
18 | * The following additional terms are in effect as per Section 7 of the license:
19 | *
20 | * The preservation of all legal notices and author attributions in
21 | * the material or in the Appropriate Legal Notices displayed
22 | * by works containing it is required.
23 | *
24 | * You should have received a copy of the GNU General Public License
25 | * along with this program. If not, see .
26 | */
27 |
28 | /**
29 | * The global window object.
30 | *
31 | * @name window
32 | * @type {object}
33 | * @global
34 | */
35 |
36 | /**
37 | * The greeter's Theme JavaScript API.
38 | *
39 | * @namespace LightDM
40 | */
41 |
42 |
43 | /**
44 | * Interface for object that holds info about a session. Session objects are not
45 | * created by the theme's code, but rather by the [`LightDM.Greeter`](#dl-LightDM-Greeter) class.
46 | *
47 | * @memberOf LightDM
48 | */
49 | class Session {
50 | constructor( { comment, key, name } ) {
51 | this._comment = comment;
52 | this._key = key;
53 | this._name = name;
54 | }
55 |
56 | /**
57 | * The name for the session.
58 | * @type {string}
59 | * @readonly
60 | */
61 | get name() {
62 | return this._name;
63 | }
64 |
65 | /**
66 | * The key for the session.
67 | * @type {string}
68 | * @readonly
69 | */
70 | get key() {
71 | return this._key;
72 | }
73 |
74 | /**
75 | * The comment for the session.
76 | * @type {string}
77 | * @readonly
78 | */
79 | get comment() {
80 | return this._comment;
81 | }
82 | }
83 |
84 |
85 | /**
86 | * Interface for object that holds info about a language on the system. Language objects are not
87 | * created by the theme's code, but rather by the [`LightDM.Greeter`](#dl-LightDM-Greeter) class.
88 | *
89 | * @memberOf LightDM
90 | */
91 | class Language {
92 | constructor( { code, name, territory } ) {
93 | this._code = code;
94 | this._name = name;
95 | this._territory = territory;
96 | }
97 |
98 | /**
99 | * The code for the language.
100 | * @type {string}
101 | * @readonly
102 | */
103 | get code() {
104 | return this._code;
105 | }
106 |
107 | /**
108 | * The name for the layout.
109 | * @type {string}
110 | * @readonly
111 | */
112 | get name() {
113 | return this._name;
114 | }
115 |
116 | /**
117 | * The territory for the language.
118 | * @type {string}
119 | * @readonly
120 | */
121 | get territory() {
122 | return this._territory;
123 | }
124 | }
125 |
126 |
127 | /**
128 | * Interface for object that holds info about a keyboard layout on the system. Language
129 | * objects are not created by the theme's code, but rather by the [`LightDM.Greeter`](#dl-LightDM-Greeter) class.
130 | *
131 | * @memberOf LightDM
132 | */
133 | class Layout {
134 | constructor( { description, name, short_description } ) {
135 | this._description = description;
136 | this._name = name;
137 | this._short_description = short_description;
138 | }
139 |
140 | /**
141 | * The description for the layout.
142 | * @type {string}
143 | * @readonly
144 | */
145 | get description() {
146 | return this._description;
147 | }
148 |
149 | /**
150 | * The name for the layout.
151 | * @type {string}
152 | * @readonly
153 | */
154 | get name() {
155 | return this._name;
156 | }
157 |
158 | /**
159 | * The territory for the layout.
160 | * @type {string}
161 | * @readonly
162 | */
163 | get short_description() {
164 | return this._short_description;
165 | }
166 | }
167 |
168 |
169 | /**
170 | * Interface for object that holds info about a user account on the system. User
171 | * objects are not created by the theme's code, but rather by the [`LightDM.Greeter`](#dl-LightDM-Greeter) class.
172 | *
173 | * @memberOf LightDM
174 | */
175 | class User {
176 | constructor( user_info ) {
177 | Object.keys(user_info).forEach( key => {
178 | this[`_${key}`] = user_info[key];
179 | } );
180 | }
181 |
182 | /**
183 | * The display name for the user.
184 | * @type {string}
185 | * @readonly
186 | */
187 | get display_name() {
188 | return this._display_name;
189 | }
190 |
191 | /**
192 | * The language for the user.
193 | * @type {string}
194 | * @readonly
195 | */
196 | get language() {
197 | return this._language;
198 | }
199 |
200 | /**
201 | * The keyboard layout for the user.
202 | * @type {string}
203 | * @readonly
204 | */
205 | get layout() {
206 | return this._layout;
207 | }
208 |
209 | /**
210 | * The image for the user.
211 | * @type {string}
212 | * @readonly
213 | */
214 | get image() {
215 | return this._image;
216 | }
217 |
218 | /**
219 | * The home_directory for the user.
220 | * @type {string}
221 | * @readonly
222 | */
223 | get home_directory() {
224 | return this._home_directory;
225 | }
226 |
227 | /**
228 | * The username for the user.
229 | * @type {string}
230 | * @readonly
231 | */
232 | get username() {
233 | return this._username;
234 | }
235 |
236 | /**
237 | * Whether or not the user is currently logged in.
238 | * @type {boolean}
239 | * @readonly
240 | */
241 | get logged_in() {
242 | return this._logged_in;
243 | }
244 |
245 | /**
246 | * The last session that the user logged into.
247 | * @type {string|null}
248 | * @readonly
249 | */
250 | get session() {
251 | return this._session;
252 | }
253 | }
254 |
255 |
--------------------------------------------------------------------------------
/src/resources/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | css/style.css
5 | js/bundle.js
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JezerM/web-greeter/0bfa7f0036b2336c4d9aa9ad35e0777ab0b41857/src/utils/__init__.py
--------------------------------------------------------------------------------
/src/utils/acpi.py:
--------------------------------------------------------------------------------
1 |
2 | import subprocess
3 | from threading import Thread
4 | from typing import List, Callable, Any
5 | from shutil import which
6 | from logger import logger
7 |
8 | Callback = Callable[[str], Any]
9 |
10 | class ACPIController:
11 | """ACPI controller"""
12 |
13 | tries = 0
14 | callbacks: List[Callback] = []
15 |
16 | def __init__(self):
17 | if self.check_acpi():
18 | self.listen()
19 | else:
20 | logger.error("ACPI: acpi_listen does not exists")
21 |
22 | @staticmethod
23 | def check_acpi() -> bool:
24 | """Checks if acpi_listen does exists"""
25 | if which("acpi_listen"):
26 | return True
27 | return False
28 |
29 | def connect(self, callback: Callback):
30 | """Connect callback to ACPI controller"""
31 | self.callbacks.append(callback)
32 |
33 | def disconnect(self, callback: Callback):
34 | """Disconnect callback from ACPI controller"""
35 | self.callbacks.remove(callback)
36 |
37 | def _listen(self):
38 | try:
39 | with subprocess.Popen("acpi_listen",
40 | stdout = subprocess.PIPE,
41 | text = True) as process:
42 | if not process.stdout:
43 | raise IOError("No stdout")
44 | while True:
45 | line = process.stdout.readline().strip()
46 | if not line:
47 | continue
48 | for _, callback in enumerate(self.callbacks):
49 | callback(line)
50 | except IOError as err:
51 | logger.error("ACPI: %s", err)
52 | if self.tries < 5:
53 | self.tries += 1
54 | logger.debug("Restarting acpi_listen")
55 | self._listen()
56 |
57 | def listen(self):
58 | """Listens to acpi_listen"""
59 | self.thread = Thread(target = self._listen)
60 | self.thread.daemon = True
61 | self.thread.start()
62 |
63 | ACPI = ACPIController()
64 |
--------------------------------------------------------------------------------
/src/utils/battery.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 | import math
4 | import time
5 | from typing import Union
6 | import globales
7 | from utils.acpi import ACPI
8 |
9 | class Battery:
10 | # pylint: disable=too-many-instance-attributes
11 | """Battery controller"""
12 |
13 | batteries = []
14 | ac_path = "AC0"
15 | pspath = "/sys/class/power_supply/"
16 | perc = -1
17 | status = "N/A"
18 | capacity = 0
19 | time = ""
20 | watt = 0
21 | running_update = False
22 |
23 | def __init__(self):
24 | if len(self.batteries) == 0:
25 | scandir_line(self.pspath, self._update_batteries)
26 | ACPI.connect(self.acpi_listen)
27 | self.full_update()
28 |
29 | def acpi_listen(self, data: str):
30 | """Listens"""
31 | if re.match(r"battery|ac_adapter", data):
32 | self.full_update()
33 |
34 | def _update_batteries(self, line):
35 | bstr = re.match(r"BAT\w+", line)
36 | if bstr:
37 | self.batteries.append(dict(
38 | name = bstr.group(),
39 | status = "N/A",
40 | perc = 0,
41 | capacity = 0,
42 | ))
43 | else:
44 | match = re.match(r"A\w+", line)
45 | self.ac_path = match.group() if match else self.ac_path
46 |
47 | # Based on "bat" widget from "lain" awesome-wm library
48 | # * (c) 2013, Luca CPZ
49 | # * (c) 2010-2012, Peter Hofmann
50 | # @see https://github.com/lcpz/lain/blob/master/widget/bat.lua
51 | def full_update(self):
52 | # pylint: disable=too-many-locals,too-many-statements,too-many-branches
53 | """Do a full update"""
54 | if self.running_update:
55 | return
56 | self.running_update = True
57 |
58 | sum_rate_current = 0
59 | sum_rate_voltage = 0
60 | sum_rate_power = 0
61 | sum_rate_energy = 0
62 | sum_energy_now = 0
63 | sum_energy_full = 0
64 | sum_charge_full = 0
65 | sum_charge_design = 0
66 |
67 | for i, battery in enumerate(self.batteries):
68 | bstr = self.pspath + battery["name"]
69 | present = read_first_line(bstr + "/present")
70 |
71 | if tonumber(present) == 1:
72 | rate_current = tonumber(read_first_line(bstr + "/current_now")) or 0
73 | rate_voltage = tonumber(read_first_line(bstr + "/voltage_now")) or 0
74 | rate_power = tonumber(read_first_line((bstr + "/power_now"))) or 0
75 | charge_full = tonumber(read_first_line(bstr + "/charge_full")) or 0
76 | charge_design = tonumber(read_first_line(bstr + "/charge_full_design")) or 0
77 |
78 | energy_now = tonumber(read_first_line(bstr + "/energy_now")
79 | or read_first_line(bstr + "/charge_now")) or 0
80 | energy_full = tonumber(read_first_line(bstr + "/energy_full") or charge_full) or 0
81 | energy_percentage = tonumber(read_first_line(bstr + "/capacity")
82 | or math.floor(energy_now / energy_full * 100)) or 0
83 |
84 | self.batteries[i]["status"] = read_first_line(bstr + "/status") or "N/A"
85 | self.batteries[i]["perc"] = energy_percentage or self.batteries[i].perc
86 |
87 | if not charge_design or charge_design == 0:
88 | self.batteries[i]["capacity"] = 0
89 | else:
90 | self.batteries[i]["capacity"] = math.floor(
91 | charge_full / charge_design * 100)
92 |
93 | sum_rate_current = sum_rate_current + rate_current
94 | sum_rate_voltage = sum_rate_voltage + rate_voltage
95 | sum_rate_power = sum_rate_power + rate_power
96 | sum_rate_energy = sum_rate_energy + (
97 | rate_power or (rate_voltage * rate_current / 1e6)
98 | )
99 | sum_energy_now = sum_energy_now + energy_now
100 | sum_energy_full = sum_energy_full + energy_full
101 | sum_charge_full = sum_charge_full + charge_full
102 | sum_charge_design = sum_charge_design + charge_design
103 |
104 | self.capacity = math.floor(min(100, sum_charge_full / (sum_charge_design or 1) * 100))
105 | self.status = self.batteries[0]["status"] if len(self.batteries) > 0 else "N/A"
106 |
107 | for i, battery in enumerate(self.batteries):
108 | if battery["status"] == "Discharging" or battery["status"] == "Charging":
109 | self.status = battery["status"]
110 |
111 | self.ac_status = tonumber(read_first_line(self.pspath + self.ac_path + "/online")) or 0
112 |
113 | if self.status != "N/A":
114 | if self.status != "Full" and sum_rate_power == 0 and self.ac_status == 1:
115 | self.perc = math.floor(min(100,
116 | sum_energy_now / sum_energy_full * 100 + 0.5))
117 | self.time = "00:00"
118 | self.watt = 0
119 | elif self.status != "Full":
120 | rate_time = 0
121 | if (sum_rate_power > 0 or sum_rate_current > 0):
122 | div = sum_rate_power > 0 or sum_rate_current
123 |
124 | if self.status == "Charging":
125 | rate_time = (sum_energy_full - sum_energy_now) / div
126 | else:
127 | rate_time = sum_energy_now / div
128 |
129 | if rate_time and rate_time < 0.01:
130 | rate_time_magnitude = tonumber(abs(math.floor(math.log10(rate_time)))) or 0
131 | rate_time = int(rate_time * 10) ^ (rate_time_magnitude - 2)
132 |
133 | hours = math.floor(rate_time)
134 | minutes = math.floor((rate_time - hours) * 60)
135 | self.perc = math.floor(
136 | min(100, (sum_energy_now / sum_energy_full) * 100) + 0.5
137 | )
138 | self.time = f"{hours:02d}:{minutes:02d}"
139 | self.watt = f"{sum_rate_energy/1e6:.2f}"
140 | elif self.status == "Full":
141 | self.perc = 100
142 | self.time = "00:00"
143 | self.watt = 0
144 |
145 | self.perc = self.perc if self.perc is not None else 0
146 |
147 | if hasattr(globales, "greeter") and hasattr(globales, "LDMGreeter"):
148 | globales.LDMGreeter.battery_update.emit()
149 |
150 | time.sleep(0.1)
151 |
152 | self.running_update = False
153 |
154 | def get_name(self):
155 | """Get name"""
156 | return self.batteries[0]["name"]
157 |
158 | def get_level(self):
159 | """Get level"""
160 | return self.perc
161 |
162 | def get_status(self):
163 | """Get status"""
164 | return self.status
165 |
166 | def get_ac_status(self):
167 | """Get AC status"""
168 | return self.ac_status
169 |
170 | def get_capacity(self):
171 | """Get capacity"""
172 | return self.capacity
173 |
174 | def get_time(self):
175 | """Get time"""
176 | return self.time
177 |
178 | def get_watt(self):
179 | """Get watt"""
180 | return self.watt
181 |
182 | def scandir_line(path, callback):
183 | """List directory"""
184 | lines = os.listdir(path)
185 | for _, line in enumerate(lines):
186 | callback(line)
187 |
188 | def read_first_line(path) -> Union[str, None]:
189 | """Just read the first line of file"""
190 | try:
191 | first = None
192 | with open(path, "r", encoding = "utf-8") as file:
193 | first = file.readline()
194 | first = first.replace("\n", "")
195 | return first
196 | except IOError:
197 | return None
198 |
199 | def tonumber(string) -> Union[int, None]:
200 | """Converts string to int or None"""
201 | try:
202 | return int(string)
203 | except (ValueError, TypeError):
204 | return None
205 |
--------------------------------------------------------------------------------
/src/utils/brightness.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # brightness.py
4 | #
5 | # Copyright © 2021 JezerM
6 | #
7 | # This file is part of Web Greeter.
8 | #
9 | # Web Greeter 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 | # Web Greeter 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 | # The following additional terms are in effect as per Section 7 of the license:
20 | #
21 | # The preservation of all legal notices and author attributions in
22 | # the material or in the Appropriate Legal Notices displayed
23 | # by works containing it is required.
24 | #
25 | # You should have received a copy of the GNU General Public License
26 | # along with Web Greeter; If not, see .
27 |
28 | import os
29 | import stat
30 | import time
31 | from typing import List
32 | from threading import Thread
33 | import pyinotify
34 | from logger import logger
35 | from config import web_greeter_config
36 | import globales
37 |
38 | sys_path = ["/sys/class/backlight/"]
39 |
40 | def get_controllers() -> List[str]:
41 | """Get brightness controllers path"""
42 | ctrls: List[str] = []
43 | for dev in sys_path:
44 | if os.path.exists(dev) and stat.S_ISDIR(os.stat(dev).st_mode):
45 | drs = os.listdir(dev)
46 | for name in drs:
47 | ctrls.append(os.path.join(dev, name))
48 | return ctrls
49 |
50 | class EventHandler(pyinotify.ProcessEvent):
51 | """PyInotify handler"""
52 | @classmethod
53 | def process_IN_MODIFY(cls, _):
54 | # pylint: disable=invalid-name,missing-function-docstring
55 | if hasattr(globales, "greeter") and hasattr(globales, "LDMGreeter"):
56 | globales.LDMGreeter.brightness_update.emit()
57 |
58 |
59 | # Behavior based on "acpilight"
60 | # Copyright(c) 2016-2019 by wave++ "Yuri D'Elia"
61 | # See https://gitlab.com/wavexx/acpilight
62 | class BrightnessController:
63 | # pylint: disable=too-many-instance-attributes
64 | """Brightness controller for web-greeter"""
65 |
66 | _controllers: List[str] = []
67 | _available: bool = False
68 | _brightness_path: str
69 | _max_brightness_path: str
70 | steps: int
71 | delay: int
72 | _brightness: int
73 | _max_brightness: int = -1
74 |
75 | def __init__(self):
76 | self._controllers = get_controllers()
77 | if (len(self._controllers) == 0 or
78 | self._controllers[0] is None or
79 | not web_greeter_config["config"]["features"]["backlight"]["enabled"]):
80 | self._available = False
81 | return
82 | b_path = self._controllers[0]
83 | self._available = True
84 | self._brightness_path = os.path.join(b_path, "brightness")
85 | self._max_brightness_path = os.path.join(b_path, "max_brightness")
86 |
87 | with open(self._max_brightness_path, "r", encoding = "utf-8") as file:
88 | self._max_brightness = int(file.read())
89 |
90 | steps = web_greeter_config["config"]["features"]["backlight"]["steps"]
91 | self.steps = 1 if steps <= 1 else steps
92 | self.delay = 200
93 | self.watch_brightness()
94 |
95 | def _watch(self):
96 | watch_manager = pyinotify.WatchManager()
97 | handler = EventHandler()
98 | # pylint: disable-next=no-member
99 | watch_manager.add_watch(self._brightness_path, pyinotify.IN_MODIFY)
100 |
101 | notifier = pyinotify.Notifier(watch_manager, handler)
102 |
103 | notifier.loop()
104 |
105 | def watch_brightness(self):
106 | """Starts a thread to watch brightness"""
107 | if not self._available:
108 | return
109 | thread = Thread(target = self._watch)
110 | thread.daemon = True
111 | thread.start()
112 |
113 | @property
114 | def max_brightness(self) -> int:
115 | """Max brightness"""
116 | return self._max_brightness
117 |
118 | @property
119 | def real_brightness(self) -> int:
120 | """Real brightness"""
121 | if not self._available:
122 | return -1
123 | try:
124 | with open(self._brightness_path, "r", encoding = "utf-8") as file:
125 | return int(file.read())
126 | except OSError:
127 | logger.error("Couldn't read from \"%s\"", self._brightness_path)
128 | return -1
129 |
130 | @real_brightness.setter
131 | def real_brightness(self, value: int):
132 | if not self._available:
133 | return
134 | if value > self.max_brightness:
135 | value = self.max_brightness
136 | elif value <= 0:
137 | value = 0
138 |
139 | if not os.path.exists(self._brightness_path):
140 | return
141 |
142 | try:
143 | with open(self._brightness_path, "w", encoding = "utf-8") as file:
144 | file.write(str(round(value)))
145 | except OSError:
146 | logger.error("Couldn't write to \"%s\"", self._brightness_path)
147 |
148 | @property
149 | def brightness(self) -> int:
150 | """Brightness"""
151 | if not self._available:
152 | return -1
153 | return round(self.real_brightness * 100 / self.max_brightness)
154 |
155 | @brightness.setter
156 | def brightness(self, value: int):
157 | self.real_brightness = round(value * self.max_brightness / 100)
158 |
159 | def _set_brightness(self, value: int):
160 | if not self._available:
161 | return
162 | steps = self.steps or 1
163 | sleep = self.delay / steps
164 | current = self.brightness
165 |
166 | if steps <= 1:
167 | self.brightness = value
168 | return
169 |
170 | for i in range(steps + 1):
171 | time.sleep(sleep / 1000)
172 | brigh = current + ((value - current) * i) / steps
173 | self.brightness = round(brigh)
174 |
175 | def set_brightness(self, value: int):
176 | """Set brightness"""
177 | thread = Thread(target = self._set_brightness, args = (value,))
178 | thread.start()
179 |
180 | def inc_brightness(self, value: int):
181 | """Increase brightness"""
182 | self.set_brightness(self.brightness + value)
183 |
184 | def dec_brightness(self, value: int):
185 | """Decrease brightness"""
186 | self.set_brightness(self.brightness - value)
187 |
--------------------------------------------------------------------------------
/web-greeter.doap:
--------------------------------------------------------------------------------
1 |
6 |
7 | web-greeter
8 | Web Greeter for LightDM
9 | Python
10 | JavaScript
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Jezer Mejía
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------