├── lib
├── apps
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ ├── cli.vala
│ │ └── fuzzy.vala
├── auth
│ ├── version
│ ├── pam
│ │ └── astal-auth
│ ├── default.nix
│ ├── meson_options.txt
│ ├── include
│ │ ├── meson.build
│ │ └── astal-auth.h.in
│ ├── meson.build
│ └── src
│ │ └── meson.build
├── battery
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ ├── cli.vala
│ │ └── ifaces.vala
├── cava
│ ├── version
│ ├── .gitignore
│ ├── subprojects
│ │ └── cava.wrap
│ ├── meson_options.txt
│ ├── default.nix
│ └── meson.build
├── greet
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ └── cli.vala
├── hyprland
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ ├── cli.vala
│ │ ├── workspace.vala
│ │ └── structs.vala
├── mpris
│ ├── gir.py
│ ├── version
│ ├── meson.options
│ ├── default.nix
│ └── src
│ │ └── config.vala.in
├── network
│ ├── gir.py
│ ├── version
│ ├── src
│ │ ├── vpn.vala
│ │ └── config.vala.in
│ └── default.nix
├── notifd
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ ├── enums.vala
│ │ ├── gschema.xml
│ │ └── action.vala
├── river
│ ├── version
│ ├── subprojects
│ │ └── wayland-glib
│ ├── meson_options.txt
│ ├── include
│ │ ├── meson.build
│ │ └── river-private.h
│ ├── default.nix
│ ├── meson.build
│ ├── protocols
│ │ └── meson.build
│ └── src
│ │ ├── astal-river.c
│ │ └── meson.build
├── tray
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── src
│ │ ├── config.vala.in
│ │ └── cli.vala
│ └── default.nix
├── astal
│ ├── gtk3
│ │ ├── gir.py
│ │ ├── version
│ │ ├── src
│ │ │ ├── config.vala.in
│ │ │ ├── widget
│ │ │ │ ├── levelbar.vala
│ │ │ │ ├── label.vala
│ │ │ │ ├── stack.vala
│ │ │ │ ├── scrollable.vala
│ │ │ │ ├── box.vala
│ │ │ │ ├── overlay.vala
│ │ │ │ └── centerbox.vala
│ │ │ ├── vapi
│ │ │ │ └── AstalInhibitManager.vapi
│ │ │ └── idle-inhibit.h
│ │ ├── default.nix
│ │ └── meson.build
│ ├── gtk4
│ │ ├── gir.py
│ │ ├── version
│ │ ├── src
│ │ │ ├── config.vala.in
│ │ │ └── widget
│ │ │ │ ├── bin.vala
│ │ │ │ ├── box.vala
│ │ │ │ └── slider.vala
│ │ ├── default.nix
│ │ └── meson.build
│ └── io
│ │ ├── gir.py
│ │ ├── version
│ │ ├── default.nix
│ │ └── config.vala.in
├── bluetooth
│ ├── gir.py
│ ├── version
│ ├── default.nix
│ └── src
│ │ ├── config.vala.in
│ │ ├── utils.vala
│ │ ├── battery.vala
│ │ └── interfaces.vala
├── powerprofiles
│ ├── gir.py
│ ├── version
│ ├── meson_options.txt
│ ├── default.nix
│ └── src
│ │ └── config.vala.in
├── wireplumber
│ ├── version
│ ├── meson_options.txt
│ ├── include
│ │ ├── astal
│ │ │ └── wireplumber
│ │ │ │ ├── meson.build
│ │ │ │ ├── channel.h
│ │ │ │ ├── profile.h
│ │ │ │ ├── route.h
│ │ │ │ ├── stream.h
│ │ │ │ ├── endpoint.h
│ │ │ │ ├── video.h
│ │ │ │ ├── audio.h
│ │ │ │ ├── wp.h
│ │ │ │ ├── device.h
│ │ │ │ └── node.h
│ │ ├── private
│ │ │ ├── channel-private.h
│ │ │ ├── wp-private.h
│ │ │ └── node-private.h
│ │ ├── meson.build
│ │ └── astal-wp.h.in
│ ├── default.nix
│ ├── meson.build
│ └── src
│ │ └── meson.build
└── wayland-glib
│ ├── version
│ ├── meson.build
│ └── wl-source.vala
├── examples
├── gtk3
│ └── simple-bar
│ │ ├── py
│ │ ├── __init__.py
│ │ ├── widget
│ │ │ └── __init__.py
│ │ ├── README.md
│ │ └── app.py
│ │ └── vala
│ │ ├── README.md
│ │ ├── flake.nix
│ │ ├── meson.build
│ │ └── app.in.vala
├── gtk4
│ └── simple-bar
│ │ ├── py
│ │ ├── src
│ │ │ ├── app
│ │ │ │ ├── __init__.py
│ │ │ │ └── App.py
│ │ │ ├── bar
│ │ │ │ ├── __init__.py
│ │ │ │ ├── Bar.scss
│ │ │ │ └── Bar.blp
│ │ │ ├── style.scss
│ │ │ ├── gresource.xml
│ │ │ ├── main.in.py
│ │ │ └── meson.build
│ │ ├── meson.build
│ │ ├── README.md
│ │ └── flake.nix
│ │ ├── js
│ │ ├── src
│ │ │ ├── style.scss
│ │ │ ├── main.in.sh
│ │ │ ├── gresource.xml
│ │ │ ├── main.in.js
│ │ │ ├── bar
│ │ │ │ ├── Bar.scss
│ │ │ │ └── Bar.blp
│ │ │ ├── App.ts
│ │ │ └── meson.build
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ ├── meson.build
│ │ ├── flake.nix
│ │ └── README.md
│ │ └── vala
│ │ ├── src
│ │ ├── style.scss
│ │ ├── gresource.xml
│ │ ├── bar
│ │ │ ├── Bar.scss
│ │ │ └── Bar.blp
│ │ ├── meson.build
│ │ └── App.vala
│ │ ├── meson.build
│ │ ├── README.md
│ │ ├── flake.nix
│ │ └── flake.lock
├── .gitignore
└── README.md
├── lang
├── gjs
│ ├── index.ts
│ ├── .gitignore
│ ├── src
│ │ ├── gtk3
│ │ │ ├── app.ts
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── gtk4
│ │ │ ├── index.ts
│ │ │ ├── app.ts
│ │ │ └── jsx-runtime.ts
│ │ ├── time.ts
│ │ ├── package.json
│ │ ├── file.ts
│ │ └── process.ts
│ ├── default.nix
│ ├── tsconfig.json
│ ├── eslint.config.mjs
│ ├── meson.build
│ └── package.json
├── lua
│ ├── stylua.toml
│ ├── astal
│ │ ├── gtk3
│ │ │ └── init.lua
│ │ ├── time.lua
│ │ ├── init.lua
│ │ ├── file.lua
│ │ ├── process.lua
│ │ └── binding.lua
│ └── astal-dev-1.rockspec
└── README.md
├── CHANGELOG.md
├── docs
├── .vitepress
│ ├── config.ts
│ └── theme
│ │ └── index.ts
├── public
│ └── showcase
│ │ ├── aylur.webp
│ │ ├── ezerinz.webp
│ │ ├── tokyob0t.webp
│ │ ├── delta-shell.webp
│ │ ├── hyprpanel_showcase.webp
│ │ ├── kotontrion-kompass.webp
│ │ └── retrozinndev-colorshell.webp
├── showcases
│ ├── index.md
│ ├── Showcases.vue
│ └── showcases.ts
├── .gitignore
├── default.nix
├── package.json
├── README.md
└── guide
│ ├── installation.md
│ └── libraries
│ ├── network.md
│ ├── river.md
│ ├── battery.md
│ ├── hyprland.md
│ ├── powerprofiles.md
│ ├── cava.md
│ ├── tray.md
│ └── bluetooth.md
├── .gitignore
├── .gitattributes
├── README.md
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── question.md
│ └── feature_request.md
├── FUNDING.yml
└── workflows
│ ├── vitepress.yml
│ └── gi-docs.yml
├── flake.lock
├── nix
├── gi-docgen.patch
└── devshell.nix
├── CONTRIBUTING.md
└── flake.nix
/lib/apps/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/apps/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/auth/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/battery/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/cava/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/greet/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/greet/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/hyprland/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/mpris/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/mpris/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/network/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/notifd/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/notifd/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/river/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/tray/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/tray/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/gir.py:
--------------------------------------------------------------------------------
1 | ../../gir.py
--------------------------------------------------------------------------------
/lib/astal/gtk3/version:
--------------------------------------------------------------------------------
1 | 3.0.0
2 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/gir.py:
--------------------------------------------------------------------------------
1 | ../../gir.py
--------------------------------------------------------------------------------
/lib/astal/gtk4/version:
--------------------------------------------------------------------------------
1 | 4.0.0
2 |
--------------------------------------------------------------------------------
/lib/astal/io/gir.py:
--------------------------------------------------------------------------------
1 | ../../gir.py
--------------------------------------------------------------------------------
/lib/astal/io/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/battery/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/bluetooth/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/bluetooth/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/hyprland/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/network/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/powerprofiles/gir.py:
--------------------------------------------------------------------------------
1 | ../gir.py
--------------------------------------------------------------------------------
/lib/wireplumber/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/network/src/vpn.vala:
--------------------------------------------------------------------------------
1 | // TODO:
2 |
--------------------------------------------------------------------------------
/lib/powerprofiles/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/lib/wayland-glib/version:
--------------------------------------------------------------------------------
1 | 0.1.0
2 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/py/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lang/gjs/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./src"
2 |
--------------------------------------------------------------------------------
/lib/cava/.gitignore:
--------------------------------------------------------------------------------
1 | /subprojects/**/
2 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/py/widget/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/app/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/bar/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/lib/river/subprojects/wayland-glib:
--------------------------------------------------------------------------------
1 | ../../wayland-glib
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.1.0
4 |
5 | WIP
6 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/style.scss:
--------------------------------------------------------------------------------
1 | @use "./bar/Bar.scss";
2 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/style.scss:
--------------------------------------------------------------------------------
1 | @use "./bar/Bar.scss";
2 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/style.scss:
--------------------------------------------------------------------------------
1 | @use "./bar/Bar.scss";
2 |
--------------------------------------------------------------------------------
/lang/gjs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | result/
3 | dist/
4 | @girs/
5 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | export { default as default } from "../vitepress.config"
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | build/
3 | result
4 | result-dev
5 | .cache/
6 | test.sh
7 | tmp/
8 |
--------------------------------------------------------------------------------
/lang/lua/stylua.toml:
--------------------------------------------------------------------------------
1 | indent_type = "Spaces"
2 | indent_width = 4
3 | column_width = 100
4 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | __pycache__/
3 | @types/
4 | build/
5 | dist/
6 | result
7 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/meson.build:
--------------------------------------------------------------------------------
1 | project('simple-bar', 'vala')
2 |
3 | subdir('src')
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | examples/** linguist-vendored
2 | lang/** linguist-vendored
3 | docs/** linguist-vendored
4 |
--------------------------------------------------------------------------------
/docs/public/showcase/aylur.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/aylur.webp
--------------------------------------------------------------------------------
/docs/public/showcase/ezerinz.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/ezerinz.webp
--------------------------------------------------------------------------------
/docs/public/showcase/tokyob0t.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/tokyob0t.webp
--------------------------------------------------------------------------------
/docs/public/showcase/delta-shell.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/delta-shell.webp
--------------------------------------------------------------------------------
/lib/mpris/meson.options:
--------------------------------------------------------------------------------
1 | option('lib', type: 'boolean', value: true)
2 | option('cli', type: 'boolean', value: true)
3 |
--------------------------------------------------------------------------------
/docs/public/showcase/hyprpanel_showcase.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/hyprpanel_showcase.webp
--------------------------------------------------------------------------------
/docs/public/showcase/kotontrion-kompass.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/kotontrion-kompass.webp
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier": {
3 | "semi": false,
4 | "tabWidth": 4
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/main.in.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | LD_PRELOAD="@LAYER_SHELL_PREFIX@/lib/libgtk4-layer-shell.so" "@INDEX@" $@
4 |
--------------------------------------------------------------------------------
/docs/public/showcase/retrozinndev-colorshell.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aylur/astal/HEAD/docs/public/showcase/retrozinndev-colorshell.webp
--------------------------------------------------------------------------------
/docs/showcases/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 | ---
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | result/
3 | .vitepress/cache/
4 |
5 | node_modules/
6 |
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 | pnpm-debug.log*
11 |
--------------------------------------------------------------------------------
/lib/apps/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/battery/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/greet/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/hyprland/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/notifd/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/tray/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/powerprofiles/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'lib',
3 | type: 'boolean',
4 | value: true,
5 | )
6 |
7 | option(
8 | 'cli',
9 | type: 'boolean',
10 | value: true,
11 | )
12 |
--------------------------------------------------------------------------------
/lib/auth/pam/astal-auth:
--------------------------------------------------------------------------------
1 | # PAM configuration file for the astal-auth library.
2 | # By default, it only includes the 'login'
3 | # configuration file (see /etc/pam.d/login)
4 |
5 | auth include login
6 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "ES2020",
5 | "module": "ES2022",
6 | "moduleResolution": "Bundler"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/lang/gjs/src/gtk3/app.ts:
--------------------------------------------------------------------------------
1 | import Gtk from "gi://Gtk?version=3.0"
2 | import Astal from "gi://Astal?version=3.0"
3 | import { mkApp } from "../_app"
4 |
5 | Gtk.init(null)
6 |
7 | export default mkApp(Astal.Application)
8 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | import DefaultTheme from "vitepress/theme"
2 | import "../../vitepress.theme.css"
3 | import "devicon/devicon.min.css"
4 | import "font-logos/assets/font-logos.css"
5 |
6 | export default DefaultTheme
7 |
--------------------------------------------------------------------------------
/lang/README.md:
--------------------------------------------------------------------------------
1 | # Higher Level Libraries
2 |
3 | Do not use these, they will be removed before the first release.
4 |
5 | GJS code moved to
6 | Lua moved to
7 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/gresource.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | bar/Bar.ui
5 | style.css
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/gresource.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | style.css
5 | bar/Bar.ui
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/bluetooth/default.nix:
--------------------------------------------------------------------------------
1 | {mkAstalPkg, ...}:
2 | mkAstalPkg {
3 | pname = "astal-bluetooth";
4 | src = ./.;
5 |
6 | libname = "bluetooth";
7 | authors = "Aylur";
8 | gir-suffix = "Bluetooth";
9 | description = "DBus proxy for bluez";
10 | }
11 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/gresource.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | index.js
5 | style.css
6 | bar/Bar.ui
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/astal/io/default.nix:
--------------------------------------------------------------------------------
1 | {mkAstalPkg, ...}:
2 | mkAstalPkg {
3 | pname = "astal";
4 | src = ./.;
5 |
6 | libname = "io";
7 | gir-suffix = "IO";
8 | authors = "Aylur";
9 | description = "Astal Core library";
10 | repo-path = "astal/io";
11 | website-path = "io";
12 | }
13 |
--------------------------------------------------------------------------------
/lib/cava/subprojects/cava.wrap:
--------------------------------------------------------------------------------
1 | [wrap-file]
2 | directory = cava-0.10.6
3 | source_url = https://github.com/LukashonakV/cava/archive/0.10.6.tar.gz
4 | source_filename = cava-0.10.6.tar.gz
5 | source_hash = e715c4c6a625b8dc063e57e8e81c80e4d1015ec1b98db69a283b2c6770f839f4
6 | [provide]
7 | cava = cava_dep
8 |
--------------------------------------------------------------------------------
/lib/apps/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-apps";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "apps";
12 | gir-suffix = "Apps";
13 | authors = "Aylur";
14 | description = "Application query library";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/auth/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-auth";
8 | src = ./.;
9 | packages = [pkgs.pam];
10 |
11 | libname = "auth";
12 | gir-suffix = "Auth";
13 | authors = "kotontrion";
14 | description = "Authentication using pam";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/greet/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-greet";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "greet";
12 | authors = "Aylur";
13 | gir-suffix = "Greet";
14 | description = "IPC client for greetd";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/auth/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'introspection',
3 | type: 'boolean',
4 | value: true,
5 | description: 'Build gobject-introspection data',
6 | )
7 | option(
8 | 'vapi',
9 | type: 'boolean',
10 | value: true,
11 | description: 'Generate vapi data (needs vapigen & introspection option)',
12 | )
13 |
--------------------------------------------------------------------------------
/lib/mpris/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-mpris";
8 | src = ./.;
9 | packages = [pkgs.gvfs pkgs.json-glib];
10 |
11 | libname = "mpris";
12 | authors = "Aylur";
13 | gir-suffix = "Mpris";
14 | description = "Control mpris players";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/cava/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'introspection',
3 | type: 'boolean',
4 | value: true,
5 | description: 'Build gobject-introspection data',
6 | )
7 |
8 | option(
9 | 'vapi',
10 | type: 'boolean',
11 | value: true,
12 | description: 'Generate vapi data (needs vapigen & introspection option)',
13 | )
14 |
--------------------------------------------------------------------------------
/lib/hyprland/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-hyprland";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "hyprland";
12 | authors = "Aylur";
13 | gir-suffix = "Hyprland";
14 | description = "IPC client for Hyprland";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/river/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'introspection',
3 | type: 'boolean',
4 | value: true,
5 | description: 'Build gobject-introspection data',
6 | )
7 |
8 | option(
9 | 'vapi',
10 | type: 'boolean',
11 | value: true,
12 | description: 'Generate vapi data (needs vapigen & introspection option)',
13 | )
14 |
--------------------------------------------------------------------------------
/lang/gjs/src/index.ts:
--------------------------------------------------------------------------------
1 | import "./overrides.js"
2 | export { default as AstalIO } from "gi://AstalIO?version=0.1"
3 | export * from "./process.js"
4 | export * from "./time.js"
5 | export * from "./file.js"
6 | export * from "./gobject.js"
7 | export { Binding, bind } from "./binding.js"
8 | export { Variable, derive } from "./variable.js"
9 |
--------------------------------------------------------------------------------
/lib/battery/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-battery";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "battery";
12 | authors = "Aylur";
13 | gir-suffix = "Battery";
14 | description = "DBus proxy for upowerd devices";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/wireplumber/meson_options.txt:
--------------------------------------------------------------------------------
1 | option(
2 | 'introspection',
3 | type: 'boolean',
4 | value: true,
5 | description: 'Build gobject-introspection data',
6 | )
7 |
8 | option(
9 | 'vapi',
10 | type: 'boolean',
11 | value: true,
12 | description: 'Generate vapi data (needs vapigen & introspection option)',
13 | )
14 |
--------------------------------------------------------------------------------
/lib/astal/io/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode (gir_namespace = "AstalIO", gir_version = "@API_VERSION@")]
2 | namespace AstalIO {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/notifd/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-notifd";
8 | src = ./.;
9 | packages = with pkgs; [json-glib gdk-pixbuf];
10 |
11 | libname = "notifd";
12 | authors = "Aylur";
13 | gir-suffix = "Notifd";
14 | description = "Notification daemon library";
15 | }
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Astal
2 |
3 | Astal is a collection of libraries written in Vala and C, designed to serve as
4 | the foundation for both lightweight widgets and fully-featured desktop shells.
5 | It handles the backend logic, so you can focus entirely on building the
6 | frontend.
7 |
8 | To get started read the [wiki](https://aylur.github.io/astal/)
9 |
--------------------------------------------------------------------------------
/lib/apps/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalApps", gir_version = "@API_VERSION@")]
2 | namespace AstalApps {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode (gir_namespace = "Astal", gir_version = "@API_VERSION@")]
2 | namespace Astal {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode (gir_namespace = "Astal", gir_version = "@API_VERSION@")]
2 | namespace Astal {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/tray/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalTray", gir_version = "@API_VERSION@")]
2 | namespace AstalTray {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/greet/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalGreet", gir_version = "@API_VERSION@")]
2 | namespace AstalGreet {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/mpris/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalMpris", gir_version = "@API_VERSION@")]
2 | namespace AstalMpris {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/notifd/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalNotifd", gir_version = "@API_VERSION@")]
2 | namespace AstalNotifd {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lang/lua/astal/gtk3/init.lua:
--------------------------------------------------------------------------------
1 | local lgi = require("lgi")
2 |
3 | return {
4 | App = require("astal.gtk3.app"),
5 | astalify = require("astal.gtk3.astalify"),
6 | Widget = require("astal.gtk3.widget"),
7 |
8 | Gtk = lgi.require("Gtk", "3.0"),
9 | Gdk = lgi.require("Gdk", "3.0"),
10 | Astal = lgi.require("Astal", "3.0"),
11 | }
12 |
--------------------------------------------------------------------------------
/lib/battery/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalBattery", gir_version = "@API_VERSION@")]
2 | namespace AstalBattery {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/hyprland/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalHyprland", gir_version = "@API_VERSION@")]
2 | namespace AstalHyprland {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/network/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalNetwork", gir_version = "@API_VERSION@")]
2 | namespace AstalNetwork {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/powerprofiles/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-powerprofiles";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "powerprofiles";
12 | authors = "Aylur";
13 | gir-suffix = "PowerProfiles";
14 | description = "DBus proxy for upowerd profiles";
15 | }
16 |
--------------------------------------------------------------------------------
/lib/bluetooth/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalBluetooth", gir_version = "@API_VERSION@")]
2 | namespace AstalBluetooth {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lib/network/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-network";
8 | src = ./.;
9 | packages = [pkgs.networkmanager];
10 |
11 | libname = "network";
12 | authors = "Aylur";
13 | gir-suffix = "Network";
14 | description = "NetworkManager wrapper library";
15 | dependencies = ["NM-1.0"];
16 | }
17 |
--------------------------------------------------------------------------------
/lib/powerprofiles/src/config.vala.in:
--------------------------------------------------------------------------------
1 | [CCode(gir_namespace = "AstalPowerProfiles", gir_version = "@API_VERSION@")]
2 | namespace AstalPowerProfiles {
3 | public const int MAJOR_VERSION = @MAJOR_VERSION@;
4 | public const int MINOR_VERSION = @MINOR_VERSION@;
5 | public const int MICRO_VERSION = @MICRO_VERSION@;
6 | public const string VERSION = "@VERSION@";
7 | }
8 |
--------------------------------------------------------------------------------
/lang/gjs/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | self,
4 | ...
5 | }:
6 | pkgs.stdenvNoCC.mkDerivation {
7 | src = ./.;
8 | name = "astal-gjs";
9 | nativeBuildInputs = [
10 | pkgs.meson
11 | pkgs.ninja
12 | pkgs.pkg-config
13 | self.packages.${pkgs.stdenv.hostPlatform.system}.io
14 | self.packages.${pkgs.stdenv.hostPlatform.system}.astal3
15 | ];
16 | }
17 |
--------------------------------------------------------------------------------
/lib/notifd/src/enums.vala:
--------------------------------------------------------------------------------
1 | public enum AstalNotifd.Urgency {
2 | LOW = 0,
3 | NORMAL = 1,
4 | CRITICAL = 2,
5 | }
6 |
7 | public enum AstalNotifd.ClosedReason {
8 | EXPIRED = 1,
9 | DISMISSED_BY_USER = 2,
10 | CLOSED = 3,
11 | UNDEFINED = 4,
12 | }
13 |
14 | public enum AstalNotifd.State {
15 | DRAFT,
16 | SENT,
17 | RECEIVED
18 | }
19 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/meson.build:
--------------------------------------------------------------------------------
1 | astal_wireplumber_subheaders = files(
2 | 'audio.h',
3 | 'device.h',
4 | 'node.h',
5 | 'endpoint.h',
6 | 'stream.h',
7 | 'profile.h',
8 | 'route.h',
9 | 'video.h',
10 | 'wp.h',
11 | 'enums.h',
12 | 'channel.h'
13 | )
14 |
15 | install_headers(astal_wireplumber_subheaders, subdir: 'astal/wireplumber')
16 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/meson.build:
--------------------------------------------------------------------------------
1 | project('simple-bar')
2 |
3 | dependency('astal-4-4.0')
4 | dependency('astal-battery-0.1')
5 | dependency('astal-wireplumber-0.1')
6 | dependency('astal-network-0.1')
7 | dependency('astal-mpris-0.1')
8 | dependency('astal-power-profiles-0.1')
9 | dependency('astal-tray-0.1')
10 | dependency('astal-bluetooth-0.1')
11 |
12 | subdir('src')
13 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/meson.build:
--------------------------------------------------------------------------------
1 | project('simple-bar')
2 |
3 | dependency('astal-4-4.0')
4 | dependency('astal-battery-0.1')
5 | dependency('astal-wireplumber-0.1')
6 | dependency('astal-network-0.1')
7 | dependency('astal-mpris-0.1')
8 | dependency('astal-power-profiles-0.1')
9 | dependency('astal-tray-0.1')
10 | dependency('astal-bluetooth-0.1')
11 |
12 | subdir('src')
13 |
--------------------------------------------------------------------------------
/lib/wireplumber/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-wireplumber";
8 | src = ./.;
9 | packages = [pkgs.wireplumber];
10 |
11 | libname = "wireplumber";
12 | authors = "kotontrion";
13 | gir-suffix = "Wp";
14 | description = "Wrapper library over the wireplumber API";
15 | dependencies = ["WP-0.5"];
16 | }
17 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | These are examples written in Vala, Python (PyGObject) and TypeScript (GJS)
4 | intended to show you how to write a Gtk application from scratch using
5 | gtk-layer-shell and Astal.
6 |
7 | > [!IMPORTANT]
8 | > If you are interested in using AGS, I don't recommend these examples, but
9 | > instead have a look at [AGS examples](https://github.com/Aylur/ags/tree/main/examples).
10 |
--------------------------------------------------------------------------------
/lang/gjs/src/gtk4/index.ts:
--------------------------------------------------------------------------------
1 | import Astal from "gi://Astal?version=4.0"
2 | import Gtk from "gi://Gtk?version=4.0"
3 | import Gdk from "gi://Gdk?version=4.0"
4 | import astalify, { type ConstructProps } from "./astalify.js"
5 |
6 | export { Astal, Gtk, Gdk }
7 | export { default as App } from "./app.js"
8 | export { astalify, ConstructProps }
9 | export * as Widget from "./widget.js"
10 | export { hook } from "../_astal"
11 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/private/channel-private.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef ASTAL_WP_CHANNEL_PRIV_H
3 | #define ASTAL_WP_CHANNEL_PRIV_H
4 |
5 | #include
6 |
7 | #include "channel.h"
8 | #include "node.h"
9 |
10 | G_BEGIN_DECLS
11 |
12 | void astal_wp_channel_update_volume(AstalWpChannel *self, gdouble volume);
13 | AstalWpChannel *astal_wp_channel_new(AstalWpNode *ep, const gchar *name);
14 |
15 | #endif // #ASTAL_WP_CHANNEL_PRIV_H
16 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/main.in.js:
--------------------------------------------------------------------------------
1 | #!@GJS@ -m
2 |
3 | import { exit, programArgs } from "system"
4 | import Gio from "gi://Gio"
5 | import GLib from "gi://GLib"
6 |
7 | // makes sure `LD_PRELOAD` does not leak into subprocesses
8 | GLib.setenv("LD_PRELOAD", "", true)
9 | Gio.Resource.load("@PKGDATADIR@/data.gresource")._register()
10 |
11 | const module = await import("resource:///index.js")
12 | exit(await module.default.main(programArgs))
13 |
--------------------------------------------------------------------------------
/lang/gjs/src/gtk3/index.ts:
--------------------------------------------------------------------------------
1 | import Astal from "gi://Astal?version=3.0"
2 | import Gtk from "gi://Gtk?version=3.0"
3 | import Gdk from "gi://Gdk?version=3.0"
4 | import astalify, { type ConstructProps, type BindableProps } from "./astalify.js"
5 |
6 | export { Astal, Gtk, Gdk }
7 | export { default as App } from "./app.js"
8 | export { astalify, ConstructProps, BindableProps }
9 | export * as Widget from "./widget.js"
10 | export { hook } from "../_astal"
11 |
--------------------------------------------------------------------------------
/lib/auth/include/meson.build:
--------------------------------------------------------------------------------
1 |
2 | config = configure_file(
3 | input: 'astal-auth.h.in',
4 | output: 'astal-auth.h',
5 | configuration: {
6 | 'VERSION': meson.project_version(),
7 | 'MAJOR_VERSION': version_split[0],
8 | 'MINOR_VERSION': version_split[1],
9 | 'MICRO_VERSION': version_split[2],
10 | },
11 | )
12 |
13 |
14 | astal_auth_inc = include_directories('.')
15 | astal_auth_headers = config
16 |
17 | install_headers(astal_auth_headers)
18 |
--------------------------------------------------------------------------------
/lib/river/include/meson.build:
--------------------------------------------------------------------------------
1 |
2 | config = configure_file(
3 | input: 'astal-river.h.in',
4 | output: 'astal-river.h',
5 | configuration: {
6 | 'VERSION': meson.project_version(),
7 | 'MAJOR_VERSION': version_split[0],
8 | 'MINOR_VERSION': version_split[1],
9 | 'MICRO_VERSION': version_split[2],
10 | },
11 | )
12 |
13 |
14 | astal_river_inc = include_directories('.')
15 | astal_river_headers = config
16 |
17 | install_headers(astal_river_headers)
18 |
--------------------------------------------------------------------------------
/docs/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | self,
3 | pkgs,
4 | }: let
5 | inherit (builtins) removeAttrs concatStringsSep map attrValues;
6 | packages = attrValues (removeAttrs self.packages.${pkgs.stdenv.hostPlatform.system} ["default" "docs"]);
7 |
8 | cp = pkg: ''
9 | doc="${pkg.doc}/share/doc"
10 | name=$(ls $doc)
11 |
12 | mkdir -p "$out/$name"
13 | cp -r "$doc/$name" $out
14 | '';
15 | in
16 | pkgs.runCommand "docs" {} (concatStringsSep "" (map cp packages))
17 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/private/wp-private.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_WP_PRIV_H
2 | #define ASTAL_WP_WP_PRIV_H
3 |
4 | #include
5 |
6 | #include "wp.h"
7 |
8 | G_BEGIN_DECLS
9 |
10 | void astal_wp_wp_set_matadata(AstalWpWp* self, guint subject, const gchar* key, const gchar* type,
11 | const gchar* value);
12 |
13 | void astal_wp_wp_update_metadata(AstalWpWp* self, guint subject);
14 | G_END_DECLS
15 |
16 | #endif // !ASTAL_WP_WP_PRIV_H
17 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | self,
5 | }:
6 | mkAstalPkg {
7 | pname = "astal3";
8 | src = ./.;
9 | packages = [
10 | self.packages.${pkgs.stdenv.hostPlatform.system}.io
11 | pkgs.gtk3
12 | pkgs.gtk-layer-shell
13 | ];
14 |
15 | libname = "astal3";
16 | gir-suffix = "";
17 | authors = "Aylur";
18 | description = "Astal GTK3 widget library";
19 | dependencies = ["AstalIO-0.1" "Gtk-3.0"];
20 | repo-path = "astal/gtk3";
21 | }
22 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | self,
5 | }:
6 | mkAstalPkg {
7 | pname = "astal4";
8 | src = ./.;
9 | packages = [
10 | self.packages.${pkgs.stdenv.hostPlatform.system}.io
11 | pkgs.gtk4
12 | pkgs.gtk4-layer-shell
13 | ];
14 |
15 | libname = "astal4";
16 | gir-suffix = "";
17 | authors = "Aylur";
18 | description = "Astal GTK4 widget library";
19 | dependencies = ["AstalIO-0.1" "Gtk-4.0"];
20 | repo-path = "astal/gtk4";
21 | }
22 |
--------------------------------------------------------------------------------
/lang/gjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "experimentalDecorators": true,
4 | "module": "ES2022",
5 | "target": "ES2023",
6 | "outDir": "dist",
7 | "strict": true,
8 | "moduleResolution": "Bundler",
9 | "skipLibCheck": true,
10 | "baseUrl": ".",
11 | },
12 | "include": [
13 | "@girs",
14 | "src/*.ts",
15 | // "src/gtk3/*",
16 | // "src/gtk4/*",
17 | "index.ts",
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal',
3 | 'vala',
4 | 'c',
5 | version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
6 | meson_version: '>= 0.62.0',
7 | default_options: [
8 | 'warning_level=2',
9 | 'werror=false',
10 | 'c_std=gnu11',
11 | ],
12 | )
13 |
14 | libdir = get_option('prefix') / get_option('libdir')
15 | pkgdatadir = get_option('prefix') / get_option('datadir') / 'astal'
16 | girpy = files('gir.py')
17 |
18 | subdir('src')
19 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal-4',
3 | 'vala',
4 | 'c',
5 | version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
6 | meson_version: '>= 0.62.0',
7 | default_options: [
8 | 'warning_level=2',
9 | 'werror=false',
10 | 'c_std=gnu11',
11 | ],
12 | )
13 |
14 | libdir = get_option('prefix') / get_option('libdir')
15 | pkgdatadir = get_option('prefix') / get_option('datadir') / 'astal'
16 | girpy = files('gir.py')
17 |
18 | subdir('src')
19 |
--------------------------------------------------------------------------------
/.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 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | A description or script to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Additional context**
20 | Add any other context about the problem here.
21 |
--------------------------------------------------------------------------------
/lib/river/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }:
6 | mkAstalPkg {
7 | pname = "astal-river";
8 | src = ./.;
9 | packages = [pkgs.json-glib];
10 |
11 | libname = "river";
12 | authors = "kotontrion";
13 | gir-suffix = "River";
14 | description = "IPC client for River";
15 |
16 | postUnpack = ''
17 | rm -rf $sourceRoot/subprojects
18 | mkdir -p $sourceRoot/subprojects
19 | cp -r --remove-destination ${../wayland-glib} $sourceRoot/subprojects/wayland-glib
20 | '';
21 | }
22 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/bar/Bar.scss:
--------------------------------------------------------------------------------
1 | // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gtk/theme/Default/_colors-public.scss
2 | $fg-color: #{"@theme_fg_color"};
3 | $bg-color: #{"@theme_bg_color"};
4 |
5 | window.Bar {
6 | > box {
7 | background: $bg-color;
8 | color: $fg-color;
9 | font-weight: bold;
10 | }
11 |
12 | button {
13 | min-height: 0;
14 | min-width: 0;
15 | border-radius: 8px;
16 | margin: 4px;
17 | padding: 4px 8px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/bar/Bar.scss:
--------------------------------------------------------------------------------
1 | // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gtk/theme/Default/_colors-public.scss
2 | $fg-color: #{"@theme_fg_color"};
3 | $bg-color: #{"@theme_bg_color"};
4 |
5 | window.Bar {
6 | > box {
7 | background: $bg-color;
8 | color: $fg-color;
9 | font-weight: bold;
10 | }
11 |
12 | button {
13 | min-height: 0;
14 | min-width: 0;
15 | border-radius: 8px;
16 | margin: 4px;
17 | padding: 4px 8px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/bar/Bar.scss:
--------------------------------------------------------------------------------
1 | // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gtk/theme/Default/_colors-public.scss
2 | $fg-color: #{"@theme_fg_color"};
3 | $bg-color: #{"@theme_bg_color"};
4 |
5 | window.Bar {
6 | > box {
7 | background: $bg-color;
8 | color: $fg-color;
9 | font-weight: bold;
10 | }
11 |
12 | button {
13 | min-height: 0;
14 | min-width: 0;
15 | border-radius: 8px;
16 | margin: 4px;
17 | padding: 4px 8px;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lang/gjs/src/time.ts:
--------------------------------------------------------------------------------
1 | import Astal from "gi://AstalIO"
2 |
3 | export type Time = Astal.Time
4 | export const Time = Astal.Time
5 |
6 | export function interval(interval: number, callback?: () => void) {
7 | return Astal.Time.interval(interval, () => void callback?.())
8 | }
9 |
10 | export function timeout(timeout: number, callback?: () => void) {
11 | return Astal.Time.timeout(timeout, () => void callback?.())
12 | }
13 |
14 | export function idle(callback?: () => void) {
15 | return Astal.Time.idle(() => void callback?.())
16 | }
17 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/meson.build:
--------------------------------------------------------------------------------
1 |
2 | config = configure_file(
3 | input: 'astal-wp.h.in',
4 | output: 'astal-wp.h',
5 | configuration: {
6 | 'VERSION': meson.project_version(),
7 | 'MAJOR_VERSION': version_split[0],
8 | 'MINOR_VERSION': version_split[1],
9 | 'MICRO_VERSION': version_split[2],
10 | },
11 | )
12 |
13 | astal_wireplumber_inc = include_directories('.', 'astal/wireplumber', 'private')
14 | astal_wireplumber_headers = config
15 |
16 | install_headers(astal_wireplumber_headers)
17 |
18 | subdir('astal/wireplumber')
19 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/levelbar.vala:
--------------------------------------------------------------------------------
1 | public class Astal.LevelBar : Gtk.LevelBar {
2 | /**
3 | * Corresponds to [property@Gtk.Orientable :orientation].
4 | */
5 | [CCode (notify = false)]
6 | public bool vertical {
7 | get { return orientation == Gtk.Orientation.VERTICAL; }
8 | set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; }
9 | }
10 |
11 | construct {
12 | notify["orientation"].connect(() => {
13 | notify_property("vertical");
14 | });
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/wireplumber/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal_wireplumber',
3 | 'c',
4 | version: '0.1.0',
5 | default_options: ['c_std=gnu11', 'warning_level=3', 'prefix=/usr'],
6 | )
7 |
8 | add_project_arguments([
9 | '-Wno-pedantic',
10 | '-Wno-unused-parameter',
11 | '-DG_LOG_DOMAIN="AstalWp"',
12 | ], language: 'c')
13 |
14 | version_split = meson.project_version().split('.')
15 | lib_so_version = version_split[0] + '.' + version_split[1]
16 |
17 | pkg_config = import('pkgconfig')
18 | gnome = import('gnome')
19 |
20 | subdir('include')
21 | subdir('src')
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question or ask for help
4 | title: ''
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Have you read through the documentation?**
11 | - [ ] [Astal docs](https://aylur.github.io/astal/)
12 | - [ ] [Library references](https://aylur.github.io/astal/guide/libraries/references)
13 | - [ ] [FAQ](https://aylur.github.io/astal/guide/typescript/faq)
14 |
15 | **Describe what have you tried so far**
16 | A description or example code of what you have got so far.
17 |
18 | **Describe your question**
19 | A question.
20 |
--------------------------------------------------------------------------------
/lib/bluetooth/src/utils.vala:
--------------------------------------------------------------------------------
1 | namespace AstalBluetooth {
2 | internal string kebab_case(string pascal_case) {
3 | StringBuilder kebab_case = new StringBuilder();
4 |
5 | for (int i = 0; i < pascal_case.length; i++) {
6 | char c = pascal_case[i];
7 |
8 | if ((c >= 'A') && (c <= 'Z')) {
9 | if (i != 0) {
10 | kebab_case.append_c('-');
11 | }
12 |
13 | kebab_case.append_c((char)(c + 32));
14 | } else {
15 | kebab_case.append_c(c);
16 | }
17 | }
18 |
19 | return kebab_case.str;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi:
--------------------------------------------------------------------------------
1 | [CCode (cprefix = "Astal", gir_namespace = "Astal", lower_case_cprefix = "astal_")]
2 | namespace Astal {
3 | [CCode (cheader_filename = "idle-inhibit.h", type_id = "astal_idle_inhibit_manager_get_type()")]
4 | public class InhibitManager : GLib.Object {
5 | public static unowned InhibitManager? get_default();
6 | public Inhibitor inhibit (Gtk.Window window);
7 | }
8 |
9 | [CCode (cheader_filename = "idle-inhibit.h", free_function = "zwp_idle_inhibitor_v1_destroy")]
10 | [Compact]
11 | public class Inhibitor { }
12 | }
13 |
--------------------------------------------------------------------------------
/lang/gjs/src/gtk4/app.ts:
--------------------------------------------------------------------------------
1 | import GLib from "gi://GLib?version=2.0"
2 | import Gtk from "gi://Gtk?version=4.0"
3 | import Astal from "gi://Astal?version=4.0"
4 | import { mkApp } from "../_app"
5 |
6 | Gtk.init()
7 |
8 | // stop this from leaking into subprocesses
9 | // and gio launch invocations
10 | GLib.unsetenv("LD_PRELOAD")
11 |
12 | // users might want to use Adwaita in which case it has to be initialized
13 | // it might be common pitfall to forget it because `App` is not `Adw.Application`
14 | await import("gi://Adw?version=1")
15 | .then(({ default: Adw }) => Adw.init())
16 | .catch(() => void 0)
17 |
18 | export default mkApp(Astal.Application)
19 |
--------------------------------------------------------------------------------
/lib/auth/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal_auth',
3 | 'c',
4 | version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
5 | default_options: [
6 | 'c_std=gnu11',
7 | 'warning_level=3',
8 | 'prefix=/usr',
9 | ],
10 | )
11 |
12 | add_project_arguments(['-Wno-pedantic'], language: 'c')
13 |
14 | version_split = meson.project_version().split('.')
15 | lib_so_version = version_split[0] + '.' + version_split[1]
16 |
17 | pkg_config = import('pkgconfig')
18 | gnome = import('gnome')
19 |
20 | subdir('include')
21 | subdir('src')
22 |
23 | install_data('pam/astal-auth', install_dir: get_option('sysconfdir') / 'pam.d')
24 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/channel.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_CHANNEL_H
2 | #define ASTAL_WP_CHANNEL_H
3 |
4 | #include
5 |
6 | G_BEGIN_DECLS
7 |
8 | #define ASTAL_WP_TYPE_CHANNEL (astal_wp_channel_get_type())
9 |
10 | G_DECLARE_FINAL_TYPE(AstalWpChannel, astal_wp_channel, ASTAL_WP, CHANNEL, GObject)
11 |
12 | gdouble astal_wp_channel_get_volume(AstalWpChannel *self);
13 | void astal_wp_channel_set_volume(AstalWpChannel *self, gdouble volume);
14 | const gchar *astal_wp_channel_get_name(AstalWpChannel *self);
15 | const gchar *astal_wp_channel_get_volume_icon(AstalWpChannel *self);
16 |
17 | G_END_DECLS
18 |
19 | #endif // !ASTAL_WP_CHANNEL_H
20 |
--------------------------------------------------------------------------------
/lang/gjs/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import eslint from "@eslint/js"
2 | import tseslint from "typescript-eslint"
3 | import stylistic from "@stylistic/eslint-plugin"
4 |
5 | export default tseslint.config({
6 | extends: [
7 | eslint.configs.recommended,
8 | ...tseslint.configs.recommended,
9 | stylistic.configs.customize({
10 | semi: false,
11 | indent: 4,
12 | quotes: "double",
13 | }),
14 | ],
15 | rules: {
16 | "@typescript-eslint/no-explicit-any": "off",
17 | "@stylistic/new-parens": "off",
18 | "@stylistic/brace-style": ["error", "1tbs", { allowSingleLine: true }],
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "nixpkgs": {
4 | "locked": {
5 | "lastModified": 1761672384,
6 | "narHash": "sha256-o9KF3DJL7g7iYMZq9SWgfS1BFlNbsm6xplRjVlOCkXI=",
7 | "owner": "nixos",
8 | "repo": "nixpkgs",
9 | "rev": "08dacfca559e1d7da38f3cf05f1f45ee9bfd213c",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "nixos",
14 | "ref": "nixos-unstable",
15 | "repo": "nixpkgs",
16 | "type": "github"
17 | }
18 | },
19 | "root": {
20 | "inputs": {
21 | "nixpkgs": "nixpkgs"
22 | }
23 | }
24 | },
25 | "root": "root",
26 | "version": 7
27 | }
28 |
--------------------------------------------------------------------------------
/lib/river/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal_river',
3 | 'c',
4 | version: '0.1.0',
5 | default_options: ['c_std=gnu11', 'warning_level=3', 'prefix=/usr'],
6 | )
7 |
8 | add_project_arguments([
9 | '-Wno-pedantic',
10 | '-Wno-unused-parameter',
11 | '-DG_LOG_DOMAIN="AstalRiver"',
12 | ], language: 'c')
13 |
14 | version_split = meson.project_version().split('.')
15 | lib_so_version = version_split[0] + '.' + version_split[1]
16 |
17 | pkg_config = import('pkgconfig')
18 | gnome = import('gnome')
19 |
20 | wayland_glib = subproject('wayland-glib')
21 | wayland_glib_dep = wayland_glib.get_variable('wayland_glib')
22 |
23 | subdir('protocols')
24 | subdir('include')
25 | subdir('src')
26 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astal-docs",
3 | "type": "module",
4 | "devDependencies": {
5 | "vitepress": "latest",
6 | "vue": "latest"
7 | },
8 | "scripts": {
9 | "dev": "vitepress dev --open",
10 | "build": "vitepress build",
11 | "preview": "vitepress preview",
12 | "vitepress": "vitepress",
13 | "lint": "eslint . --fix"
14 | },
15 | "dependencies": {
16 | "devicon": "latest",
17 | "font-logos": "latest"
18 | },
19 | "prettier": {
20 | "semi": false,
21 | "proseWrap": "always",
22 | "overrides": [
23 | {
24 | "files": "**.md",
25 | "options": {
26 | "tabWidth": 4
27 | }
28 | }
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/idle-inhibit.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_IDLE_INHIBITOR_H
2 | #define ASTAL_IDLE_INHIBITOR_H
3 |
4 | #include
5 | #include
6 |
7 | #include "idle-inhibit-unstable-v1-client.h"
8 |
9 | G_BEGIN_DECLS
10 |
11 | #define ASTAL_TYPE_INHIBIT_MANAGER (astal_inhibit_manager_get_type())
12 |
13 | G_DECLARE_FINAL_TYPE(AstalInhibitManager, astal_inhibit_manager, ASTAL, INHIBIT_MANAGER, GObject)
14 |
15 | typedef struct zwp_idle_inhibitor_v1 AstalInhibitor;
16 |
17 | AstalInhibitManager* astal_inhibit_manager_get_default();
18 | AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window);
19 |
20 | G_END_DECLS
21 |
22 | #endif // !ASTAL_IDLE_INHIBITOR_H
23 |
--------------------------------------------------------------------------------
/.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 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/py/README.md:
--------------------------------------------------------------------------------
1 | # Simple Bar Example
2 |
3 | 
4 |
5 | A simple bar for Hyprland using
6 |
7 | - [Battery library](https://aylur.github.io/astal/guide/libraries/battery).
8 | - [Hyprland library](https://aylur.github.io/astal/guide/libraries/hyprland).
9 | - [Mpris library](https://aylur.github.io/astal/guide/libraries/mpris).
10 | - [Network library](https://aylur.github.io/astal/guide/libraries/network).
11 | - [Tray library](https://aylur.github.io/astal/guide/libraries/tray).
12 | - [WirePlumber library](https://aylur.github.io/astal/guide/libraries/wireplumber).
13 | - [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler
14 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/vala/README.md:
--------------------------------------------------------------------------------
1 | # Simple Bar Example
2 |
3 | 
4 |
5 | A simple bar for Hyprland using
6 |
7 | - [Battery library](https://aylur.github.io/astal/guide/libraries/battery).
8 | - [Hyprland library](https://aylur.github.io/astal/guide/libraries/hyprland).
9 | - [Mpris library](https://aylur.github.io/astal/guide/libraries/mpris).
10 | - [Network library](https://aylur.github.io/astal/guide/libraries/network).
11 | - [Tray library](https://aylur.github.io/astal/guide/libraries/tray).
12 | - [WirePlumber library](https://aylur.github.io/astal/guide/libraries/wireplumber).
13 | - [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler
14 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/profile.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_PROFILE_H
2 | #define ASTAL_WP_PROFILE_H
3 |
4 | #include
5 |
6 | #include "enums.h"
7 |
8 | G_BEGIN_DECLS
9 |
10 | #define ASTAL_WP_TYPE_PROFILE (astal_wp_profile_get_type())
11 |
12 | G_DECLARE_FINAL_TYPE(AstalWpProfile, astal_wp_profile, ASTAL_WP, PROFILE, GObject)
13 |
14 | gint astal_wp_profile_get_index(AstalWpProfile *self);
15 | const gchar *astal_wp_profile_get_description(AstalWpProfile *self);
16 | const gchar *astal_wp_profile_get_name(AstalWpProfile *self);
17 | AstalWpAvailable astal_wp_profile_get_available(AstalWpProfile *self);
18 | gint astal_wp_profile_get_priority(AstalWpProfile *self);
19 |
20 | G_END_DECLS
21 |
22 | #endif // !ASTAL_WP_PROFILE_H
23 |
--------------------------------------------------------------------------------
/nix/gi-docgen.patch:
--------------------------------------------------------------------------------
1 | diff --git a/gidocgen/gir/parser.py b/gidocgen/gir/parser.py
2 | index e62835d..7ee60fa 100644
3 | --- a/gidocgen/gir/parser.py
4 | +++ b/gidocgen/gir/parser.py
5 | @@ -288,7 +288,11 @@ class GirParser:
6 |
7 | content = child.text or ""
8 |
9 | - return ast.Doc(content=content, filename=child.attrib['filename'], line=int(child.attrib['line']))
10 | + return ast.Doc(
11 | + content=content,
12 | + filename=child.attrib.get('filename', ''),
13 | + line=int(child.attrib.get('line', 0)),
14 | + )
15 |
16 | def _maybe_parse_source_position(self, node: ET.Element) -> T.Optional[ast.SourcePosition]:
17 | child = node.find('core:source-position', GI_NAMESPACES)
18 |
--------------------------------------------------------------------------------
/lang/gjs/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astal",
3 | "type": "module",
4 | "license": "LGPL-2.1",
5 | "exports": {
6 | ".": "./index.ts",
7 | "./gtk3": "./gtk3/index.ts",
8 | "./gtk4": "./gtk4/index.ts",
9 | "./gtk3/app": "./gtk3/app.ts",
10 | "./gtk4/app": "./gtk4/app.ts",
11 | "./gtk3/widget": "./gtk3/widget.ts",
12 | "./gtk4/widget": "./gtk4/widget.ts",
13 | "./gtk3/jsx-runtime": "./gtk3/jsx-runtime.ts",
14 | "./gtk4/jsx-runtime": "./gtk4/jsx-runtime.ts",
15 | "./binding": "./binding.ts",
16 | "./file": "./file.ts",
17 | "./gobject": "./gobject.ts",
18 | "./process": "./process.ts",
19 | "./time": "./time.ts",
20 | "./variable": "./variable.ts"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal-wp.h.in:
--------------------------------------------------------------------------------
1 |
2 | #ifndef WP_H
3 | #define WP_H
4 |
5 | #include "astal/wireplumber/audio.h"
6 | #include "astal/wireplumber/channel.h"
7 | #include "astal/wireplumber/device.h"
8 | #include "astal/wireplumber/endpoint.h"
9 | #include "astal/wireplumber/enums.h"
10 | #include "astal/wireplumber/node.h"
11 | #include "astal/wireplumber/profile.h"
12 | #include "astal/wireplumber/route.h"
13 | #include "astal/wireplumber/stream.h"
14 | #include "astal/wireplumber/video.h"
15 | #include "astal/wireplumber/wp.h"
16 |
17 | // clang-format off
18 | #define ASTAL_WP_MAJOR_VERSION @MAJOR_VERSION@
19 | #define ASTAL_WP_MINOR_VERSION @MINOR_VERSION@
20 | #define ASTAL_WP_MICRO_VERSION @MICRO_VERSION@
21 | #define ASTAL_WP_VERSION "@VERSION@"
22 | // clang-format on
23 |
24 | #endif // WP_H
25 |
--------------------------------------------------------------------------------
/lang/lua/astal/time.lua:
--------------------------------------------------------------------------------
1 | local lgi = require("lgi")
2 | local Astal = lgi.require("AstalIO", "0.1")
3 | local GObject = lgi.require("GObject", "2.0")
4 |
5 | local M = {}
6 |
7 | M.Time = Astal.Time
8 |
9 | ---@param interval number
10 | ---@param fn function
11 | ---@return { cancel: function, on_now: function }
12 | function M.interval(interval, fn)
13 | return Astal.Time.interval(interval, GObject.Closure(fn))
14 | end
15 |
16 | ---@param timeout number
17 | ---@param fn function
18 | ---@return { cancel: function, on_now: function }
19 | function M.timeout(timeout, fn)
20 | return Astal.Time.timeout(timeout, GObject.Closure(fn))
21 | end
22 |
23 | ---@param fn function
24 | ---@return { cancel: function, on_now: function }
25 | function M.idle(fn)
26 | return Astal.Time.idle(GObject.Closure(fn))
27 | end
28 |
29 | return M
30 |
--------------------------------------------------------------------------------
/lib/river/protocols/meson.build:
--------------------------------------------------------------------------------
1 | wayland_scanner = find_program('wayland-scanner')
2 |
3 | protocols = [
4 | 'river-status-unstable-v1.xml',
5 | 'river-layout-v3.xml',
6 | 'river-control-unstable-v1.xml'
7 | ]
8 |
9 | gen_client_header = generator(
10 | wayland_scanner,
11 | output: ['@BASENAME@-client.h'],
12 | arguments: ['-c', 'client-header', '@INPUT@', '@BUILD_DIR@/@BASENAME@-client.h'],
13 | )
14 |
15 | gen_private_code = generator(
16 | wayland_scanner,
17 | output: ['@BASENAME@.c'],
18 | arguments: ['-c', 'private-code', '@INPUT@', '@BUILD_DIR@/@BASENAME@.c'],
19 | )
20 |
21 | client_protocol_srcs = []
22 |
23 | foreach protocol : protocols
24 | client_header = gen_client_header.process(protocol)
25 | code = gen_private_code.process(protocol)
26 | client_protocol_srcs += [client_header, code]
27 | endforeach
28 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/route.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_ROUTE_H
2 | #define ASTAL_WP_ROUTE_H
3 |
4 | #include
5 |
6 | #include "enums.h"
7 |
8 | G_BEGIN_DECLS
9 |
10 | #define ASTAL_WP_TYPE_ROUTE (astal_wp_route_get_type())
11 |
12 | G_DECLARE_FINAL_TYPE(AstalWpRoute, astal_wp_route, ASTAL_WP, ROUTE, GObject)
13 |
14 | gint astal_wp_route_get_index(AstalWpRoute *self);
15 | const gchar *astal_wp_route_get_description(AstalWpRoute *self);
16 | const gchar *astal_wp_route_get_name(AstalWpRoute *self);
17 | AstalWpDirection astal_wp_route_get_direction(AstalWpRoute *self);
18 | AstalWpAvailable astal_wp_route_get_available(AstalWpRoute *self);
19 | gint astal_wp_route_get_priority(AstalWpRoute *self);
20 | gint astal_wp_route_get_device(AstalWpRoute *self);
21 |
22 | G_END_DECLS
23 |
24 | #endif // !ASTAL_WP_ROUTE_H
25 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [aylur, kotontrion]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: aylur
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 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12 | polar: # Replace with a single Polar username
13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
14 | thanks_dev: # Replace with a single thanks.dev username
15 | custom: ["https://ko-fi.com/kotontrion"]
16 |
--------------------------------------------------------------------------------
/lib/notifd/src/gschema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | false
9 |
10 |
11 | []
12 |
13 |
14 | -1
15 |
16 |
17 | [
18 | 'action-icons',
19 | 'actions',
20 | 'body',
21 | 'body-hyperlinks',
22 | 'body-images',
23 | 'body-markup',
24 | 'icon-static',
25 | 'persistence',
26 | 'sound'
27 | ]
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/label.vala:
--------------------------------------------------------------------------------
1 | using Pango;
2 |
3 | public class Astal.Label : Gtk.Label {
4 | /**
5 | * Shortcut for setting [property@Gtk.Label:ellipsize] to [enum@Pango.EllipsizeMode.END]
6 | */
7 | public bool truncate {
8 | set { ellipsize = value ? EllipsizeMode.END : EllipsizeMode.NONE; }
9 | get { return ellipsize == EllipsizeMode.END; }
10 | }
11 |
12 | /**
13 | * Shortcut for setting [property@Gtk.Label:justify] to [enum@Gtk.Justification.FILL]
14 | */
15 | public new bool justify_fill {
16 | set { justify = value ? Gtk.Justification.FILL : Gtk.Justification.LEFT; }
17 | get { return justify == Gtk.Justification.FILL; }
18 | }
19 |
20 | construct {
21 | notify["ellipsize"].connect(() => notify_property("truncate"));
22 | notify["justify"].connect(() => notify_property("justify_fill"));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/stream.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WIREPLUMBER_STREAM_H
2 | #define ASTAL_WIREPLUMBER_STREAM_H
3 |
4 | #include
5 |
6 | #include "endpoint.h"
7 | #include "node.h"
8 |
9 | G_BEGIN_DECLS
10 |
11 | #define ASTAL_WP_TYPE_STREAM (astal_wp_stream_get_type())
12 |
13 | G_DECLARE_FINAL_TYPE(AstalWpStream, astal_wp_stream, ASTAL_WP, STREAM, AstalWpNode)
14 |
15 | AstalWpMediaRole astal_wp_stream_get_media_role(AstalWpStream *self);
16 | AstalWpMediaCategory astal_wp_stream_get_media_category(AstalWpStream *self);
17 | gint astal_wp_stream_get_target_serial(AstalWpStream *self);
18 | void astal_wp_stream_set_target_serial(AstalWpStream *self, gint serial);
19 | AstalWpEndpoint *astal_wp_stream_get_target_endpoint(AstalWpStream *self);
20 | void astal_wp_stream_set_target_endpoint(AstalWpStream *self, AstalWpEndpoint *target);
21 |
22 | G_END_DECLS
23 |
24 | #endif // !ASTAL_WIREPLUMBER_STREAM_H
25 |
--------------------------------------------------------------------------------
/lang/lua/astal/init.lua:
--------------------------------------------------------------------------------
1 | if not table.unpack then
2 | table.unpack = unpack
3 | end
4 |
5 | local lgi = require("lgi")
6 | local Binding = require("astal.binding")
7 | local File = require("astal.file")
8 | local Process = require("astal.process")
9 | local Time = require("astal.time")
10 | ---@type Variable | fun(v: any): Variable
11 | local Variable = require("astal.variable")
12 |
13 | return {
14 | Variable = Variable,
15 | bind = Binding.new,
16 |
17 | interval = Time.interval,
18 | timeout = Time.timeout,
19 | idle = Time.idle,
20 |
21 | subprocess = Process.subprocess,
22 | exec = Process.exec,
23 | exec_async = Process.exec_async,
24 |
25 | read_file = File.read_file,
26 | read_file_async = File.read_file_async,
27 | write_file = File.write_file,
28 | write_file_async = File.write_file_async,
29 | monitor_file = File.monitor_file,
30 |
31 | require = lgi.require,
32 | }
33 |
--------------------------------------------------------------------------------
/lib/wayland-glib/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'wayland-glib',
3 | 'vala',
4 | 'c',
5 | version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
6 | meson_version: '>= 0.62.0',
7 | default_options: [
8 | 'warning_level=2',
9 | 'werror=false',
10 | 'c_std=gnu11',
11 | ],
12 | )
13 |
14 | version_split = meson.project_version().split('.')
15 |
16 | deps = [
17 | dependency('glib-2.0'),
18 | dependency('gio-2.0'),
19 | dependency('gobject-2.0'),
20 | dependency('wayland-client'),
21 | ]
22 |
23 | sources = [
24 | 'wl-source.vala',
25 | ]
26 |
27 | lib = static_library(
28 | meson.project_name(),
29 | sources,
30 | dependencies: deps,
31 | vala_header: meson.project_name() + '.h',
32 | vala_vapi: meson.project_name() + '.vapi',
33 | )
34 |
35 | wayland_glib = declare_dependency(
36 | link_with: lib,
37 | include_directories: include_directories('.')
38 | )
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/main.in.py:
--------------------------------------------------------------------------------
1 | #!@PYTHON@
2 |
3 | import gi
4 |
5 | gi.require_version("Gio", "2.0")
6 | gi.require_version("GObject", "2.0")
7 | gi.require_version("GLib", "2.0")
8 | gi.require_version("Gtk", "4.0")
9 | gi.require_version("Astal", "4.0")
10 |
11 | gi.require_version("AstalBattery", "0.1")
12 | gi.require_version("AstalWp", "0.1")
13 | gi.require_version("AstalNetwork", "0.1")
14 | gi.require_version("AstalMpris", "0.1")
15 | gi.require_version("AstalPowerProfiles", "0.1")
16 | gi.require_version("AstalTray", "0.1")
17 | gi.require_version("AstalBluetooth", "0.1")
18 |
19 | from gi.repository import Gio
20 | from sys import argv, path
21 | from ctypes import CDLL
22 |
23 | CDLL("@LAYER_SHELL_PREFIX@/lib/libgtk4-layer-shell.so")
24 | path.insert(1, "@PKGDATADIR@")
25 | Gio.Resource.load("@PKGDATADIR@/data.gresource")._register()
26 |
27 |
28 | if __name__ == "__main__":
29 | from app.App import App
30 |
31 | App.main(argv)
32 |
--------------------------------------------------------------------------------
/lib/tray/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }: let
6 | vala-panel-appmenu = pkgs.fetchFromGitLab {
7 | owner = "vala-panel-project";
8 | repo = "vala-panel-appmenu";
9 | rev = "25.04";
10 | hash = "sha256-v5J3nwViNiSKRPdJr+lhNUdKaPG82fShPDlnmix5tlY=";
11 | };
12 |
13 | appmenu-glib-translator = pkgs.stdenv.mkDerivation {
14 | pname = "appmenu-glib-translator";
15 | version = "25.04";
16 |
17 | src = "${vala-panel-appmenu}/subprojects/appmenu-glib-translator";
18 |
19 | buildInputs = with pkgs; [
20 | glib
21 | ];
22 |
23 | nativeBuildInputs = with pkgs; [
24 | gobject-introspection
25 | meson
26 | pkg-config
27 | ninja
28 | vala
29 | ];
30 | };
31 | in
32 | mkAstalPkg {
33 | pname = "astal-tray";
34 | src = ./.;
35 | packages = [pkgs.json-glib appmenu-glib-translator];
36 |
37 | libname = "tray";
38 | authors = "kotontrion";
39 | gir-suffix = "Tray";
40 | description = "StatusNotifierItem implementation";
41 | }
42 |
--------------------------------------------------------------------------------
/lang/gjs/meson.build:
--------------------------------------------------------------------------------
1 | project('astal-gjs')
2 |
3 | dest = get_option('prefix') / get_option('datadir') / 'astal' / 'gjs'
4 |
5 | dependency('astal-io-0.1')
6 |
7 | gtk3 = dependency('astal-3.0', required: false)
8 | gtk4 = dependency('astal-4-4.0', required: false)
9 |
10 | if (not gtk3.found() and not gtk4.found())
11 | error('Neither astal-3.0 nor astal-4.0 was found.')
12 | endif
13 |
14 | install_data(
15 | [
16 | 'src/_app.ts',
17 | 'src/_astal.ts',
18 | 'src/binding.ts',
19 | 'src/file.ts',
20 | 'src/gobject.ts',
21 | 'src/index.ts',
22 | 'src/overrides.ts',
23 | 'src/process.ts',
24 | 'src/time.ts',
25 | 'src/variable.ts',
26 | 'src/package.json',
27 | ],
28 | install_dir: dest,
29 | )
30 |
31 | install_subdir('src/gtk3', install_dir: dest)
32 | install_subdir('src/gtk4', install_dir: dest)
33 |
34 | import('pkgconfig').generate(
35 | description: 'Astal GJS pacakge',
36 | name: meson.project_name(),
37 | install_dir: get_option('libdir') / 'pkgconfig',
38 | variables: {
39 | 'srcdir': dest,
40 | },
41 | )
42 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/src/widget/bin.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * A widget with one child.
3 | * It is useful for deriving subclasses, since it provides common code needed for handling a single child widget.
4 | */
5 | public class Astal.Bin : Gtk.Widget, Gtk.Buildable {
6 | construct { set_layout_manager(new Gtk.BinLayout()); }
7 |
8 | Gtk.Widget _child;
9 | public Gtk.Widget? child {
10 | get { return _child; }
11 | set {
12 | if (_child != null) {
13 | _child.unparent();
14 | }
15 |
16 | if (value != null) {
17 | _child = value;
18 | value.set_parent(this);
19 | }
20 | }
21 | }
22 |
23 | void add_child(Gtk.Builder builder, Object child, string? type) {
24 | if (child is Gtk.Widget) {
25 | this.child = child as Gtk.Widget;
26 | } else {
27 | base.add_child(builder, child, type);
28 | }
29 | }
30 |
31 | ~Bin() {
32 | if (child != null) {
33 | child.unparent();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/vala/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
4 | astal.url = "github:aylur/astal";
5 | };
6 |
7 | outputs = {
8 | self,
9 | nixpkgs,
10 | astal,
11 | }: let
12 | system = "x86_64-linux";
13 | pkgs = nixpkgs.legacyPackages.${system};
14 | in {
15 | packages.${system} = {
16 | default = pkgs.stdenv.mkDerivation {
17 | name = "simple-bar";
18 | src = ./.;
19 |
20 | nativeBuildInputs = with pkgs; [
21 | meson
22 | ninja
23 | pkg-config
24 | vala
25 | gobject-introspection
26 | dart-sass
27 | ];
28 |
29 | buildInputs = [
30 | astal.packages.${system}.astal3
31 | astal.packages.${system}.battery
32 | astal.packages.${system}.wireplumber
33 | astal.packages.${system}.network
34 | astal.packages.${system}.tray
35 | astal.packages.${system}.mpris
36 | astal.packages.${system}.hyprland
37 | ];
38 | };
39 | };
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/README.md:
--------------------------------------------------------------------------------
1 | # Simple Astal Bar example in Vala
2 |
3 | This example shows you how to get a Vala+Blueprint+Sass project going.
4 |
5 | ## Dependencies
6 |
7 | - vala
8 | - meson
9 | - blueprint-compiler
10 | - sass
11 | - astal4
12 | - astal-battery
13 | - astal-wireplumber
14 | - astak-network
15 | - astal-mpris
16 | - astak-power-profiles
17 | - astal-tray
18 | - astal-bluetooth
19 |
20 | ## How to use
21 |
22 | > [!NOTE]
23 | > If you are on Nix, there is an example flake included
24 | > otherwise feel free to `rm flake.nix`
25 |
26 | - developing
27 |
28 | ```sh
29 | meson setup build --wipe --prefix "$(pwd)/result"
30 | meson install -C build
31 | ./result/bin/simple-bar
32 | ```
33 |
34 | - installing
35 |
36 | ```sh
37 | meson setup build --wipe
38 | meson install -C build
39 | simple-bar
40 | ```
41 |
42 | - adding new vala files will also have to be listed in `meson.build`
43 | - adding new scss files requires no additional steps as long as they are imported from `style.scss`
44 | - adding new ui (blueprint) files will also have to be listed in `meson.build` and in `gresource.xml`
45 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/endpoint.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WIREPLUMBER_ENDPOINT_H
2 | #define ASTAL_WIREPLUMBER_ENDPOINT_H
3 |
4 | #include
5 |
6 | #include "device.h"
7 | #include "node.h"
8 |
9 | G_BEGIN_DECLS
10 |
11 | #define ASTAL_WP_TYPE_ENDPOINT (astal_wp_endpoint_get_type())
12 |
13 | G_DECLARE_FINAL_TYPE(AstalWpEndpoint, astal_wp_endpoint, ASTAL_WP, ENDPOINT, AstalWpNode)
14 |
15 | guint astal_wp_endpoint_get_device_id(AstalWpEndpoint* self);
16 | AstalWpDevice* astal_wp_endpoint_get_device(AstalWpEndpoint* self);
17 | gboolean astal_wp_endpoint_get_is_default(AstalWpEndpoint* self);
18 | void astal_wp_endpoint_set_is_default(AstalWpEndpoint* self, gboolean is_default);
19 |
20 | guint astal_wp_endpoint_get_route_id(AstalWpEndpoint* self);
21 | void astal_wp_endpoint_set_route_id(AstalWpEndpoint* self, guint route_id);
22 | AstalWpRoute* astal_wp_endpoint_get_route(AstalWpEndpoint* self);
23 | void astal_wp_endpoint_set_route(AstalWpEndpoint* self, AstalWpRoute* route);
24 | GList* astal_wp_endpoint_get_routes(AstalWpEndpoint* self);
25 |
26 | G_END_DECLS
27 |
28 | #endif // !ASTAL_WIREPLUMBER_ENDPOINT_H
29 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/README.md:
--------------------------------------------------------------------------------
1 | # Simple Astal Bar example in Python
2 |
3 | This example shows you how to get a Python+Blueprint+Sass project going.
4 |
5 | ## Dependencies
6 |
7 | - python3
8 | - pygobject
9 | - meson
10 | - blueprint-compiler
11 | - sass
12 | - astal4
13 | - astal-battery
14 | - astal-wireplumber
15 | - astak-network
16 | - astal-mpris
17 | - astak-powerprofiles
18 | - astal-tray
19 | - astal-bluetooth
20 |
21 | ## How to use
22 |
23 | > [!NOTE]
24 | > If you are on Nix, there is an example flake included
25 | > otherwise feel free to `rm flake.nix`
26 |
27 | - developing
28 |
29 | ```sh
30 | meson setup build --wipe --prefix "$(pwd)/result"
31 | meson install -C build
32 | ./result/bin/simple-bar
33 | ```
34 |
35 | - installing
36 |
37 | ```sh
38 | meson setup build --wipe
39 | meson install -C build
40 | simple-bar
41 | ```
42 |
43 | - adding new python files will also have to be listed in `meson.build`
44 | - adding new scss files requires no additional steps as long as they are imported from `style.scss`
45 | - adding new ui (blueprint) files will also have to be listed in `meson.build` and in `gresource.xml`
46 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/video.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WIREPLUMBER_VIDEO_H
2 | #define ASTAL_WIREPLUMBER_VIDEO_H
3 |
4 | #include
5 |
6 | #include "device.h"
7 | #include "endpoint.h"
8 | #include "stream.h"
9 |
10 | G_BEGIN_DECLS
11 |
12 | #define ASTAL_WP_TYPE_VIDEO (astal_wp_video_get_type())
13 |
14 | G_DECLARE_FINAL_TYPE(AstalWpVideo, astal_wp_video, ASTAL_WP, VIDEO, GObject)
15 |
16 | AstalWpEndpoint *astal_wp_video_get_source(AstalWpVideo *self, guint id);
17 | AstalWpEndpoint *astal_wp_video_get_sink(AstalWpVideo *self, guint id);
18 | AstalWpStream *astal_wp_video_get_recorder(AstalWpVideo *self, guint id);
19 | AstalWpStream *astal_wp_video_get_stream(AstalWpVideo *self, guint id);
20 | AstalWpDevice *astal_wp_video_get_device(AstalWpVideo *self, guint id);
21 |
22 | GList *astal_wp_video_get_sources(AstalWpVideo *self);
23 | GList *astal_wp_video_get_sinks(AstalWpVideo *self);
24 | GList *astal_wp_video_get_recorders(AstalWpVideo *self);
25 | GList *astal_wp_video_get_streams(AstalWpVideo *self);
26 | GList *astal_wp_video_get_devices(AstalWpVideo *self);
27 |
28 | G_END_DECLS
29 |
30 | #endif // !ASTAL_WIREPLUMBER_VIDEO_H
31 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | You can contribute by:
4 |
5 | - [Suggesting new features](https://github.com/Aylur/astal/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=)
6 | - [Reporting bugs](https://github.com/Aylur/astal/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=)
7 | - Improving docs with additional contexts and examples
8 | - adding more distros to sections about installations e.g [building from source](https://aylur.github.io/astal/guide/getting-started/installation#building-from-source)
9 | - Adding more example projects to [examples](https://github.com/Aylur/astal/tree/main/examples)
10 | - Adding new language support/binding. For these open a PR for discussions.
11 | - Adding new libraries e.g support for more wayland compositors
12 | - [Adding](https://github.com/Aylur/astal/tree/main/docs#add-your-creation-to-the-showcases-page) your project to the [showcases page](https://aylur.github.io/astal/showcases/).
13 | - Creating packaging for distributions
14 |
15 | ## Adding new libraries
16 |
17 | Write libraries preferably in Vala. Only choose C if some dependency is only available in C e.g wayland.
18 |
--------------------------------------------------------------------------------
/lib/bluetooth/src/battery.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * Object representing a [[https://github.com/bluez/bluez/blob/master/doc/org.bluez.Battery.rst|battery]].
3 | */
4 | internal class AstalBluetooth.Battery : Object {
5 | private IBattery proxy;
6 |
7 | internal ObjectPath object_path { owned get; private set; }
8 |
9 | internal Battery(IBattery proxy) {
10 | this.proxy = proxy;
11 | this.object_path = (ObjectPath)proxy.g_object_path;
12 | proxy.g_properties_changed.connect((props) => {
13 | var map = (HashTable)props;
14 | foreach (var key in map.get_keys()) {
15 | var prop = kebab_case(key);
16 | if (get_class().find_property(prop) != null) {
17 | notify_property(prop);
18 | }
19 | }
20 | });
21 | }
22 |
23 | /**
24 | * The percentage of battery left as an unsigned 8-bit integer.
25 | */
26 | public uint percentage { get { return proxy.percentage; } }
27 |
28 | /**
29 | * Describes where the battery information comes from.
30 | */
31 | public string source { owned get { return proxy.source; } }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/vala/meson.build:
--------------------------------------------------------------------------------
1 | project('simple-bar', 'vala', 'c')
2 |
3 | bindir = get_option('prefix') / get_option('bindir')
4 | libdir = get_option('prefix') / get_option('libdir')
5 |
6 | pkgconfig_deps = [
7 | dependency('glib-2.0'),
8 | dependency('gobject-2.0'),
9 | dependency('gtk+-3.0'),
10 | dependency('libnm'),
11 | dependency('astal-io-0.1'),
12 | dependency('astal-3.0'),
13 | dependency('astal-battery-0.1'),
14 | dependency('astal-wireplumber-0.1'),
15 | dependency('astal-network-0.1'),
16 | dependency('astal-tray-0.1'),
17 | dependency('astal-mpris-0.1'),
18 | dependency('astal-hyprland-0.1'),
19 | ]
20 |
21 | # needed for GLib.Math
22 | deps = pkgconfig_deps + meson.get_compiler('c').find_library('m')
23 |
24 | main = configure_file(
25 | input: 'app.in.vala',
26 | output: 'app.vala',
27 | configuration: {
28 | 'STYLE': run_command(
29 | find_program('sass'),
30 | meson.project_source_root() / 'style.scss',
31 | ).stdout(),
32 | },
33 | )
34 |
35 | sources = files(
36 | 'widget/Bar.vala',
37 | )
38 |
39 | executable(
40 | 'simple-bar',
41 | [sources, main],
42 | dependencies: deps,
43 | install: true,
44 | install_dir: bindir,
45 | )
46 |
--------------------------------------------------------------------------------
/lib/river/include/river-private.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_RIVER_OUTPUT_PRIVATE_H
2 | #define ASTAL_RIVER_OUTPUT_PRIVATE_H
3 |
4 | #include
5 |
6 | #include "astal-river.h"
7 | #include "river-control-unstable-v1-client.h"
8 | #include "river-layout-v3-client.h"
9 | #include "river-status-unstable-v1-client.h"
10 |
11 | G_BEGIN_DECLS
12 |
13 | AstalRiverOutput *astal_river_output_new(guint id, struct wl_output *wl_output,
14 | struct zriver_status_manager_v1 *status_manager,
15 | struct zriver_control_v1 *river_control,
16 | struct wl_seat *seat, struct wl_display *wl_display);
17 |
18 | struct wl_output *astal_river_output_get_wl_output(AstalRiverOutput *self);
19 | void astal_river_output_set_focused_view(AstalRiverOutput *self, const gchar *focused_view);
20 |
21 | AstalRiverLayout *astal_river_layout_new(AstalRiverRiver *river,
22 | struct river_layout_manager_v3 *layout_manager,
23 | struct wl_display *wl_display, const gchar *namespace);
24 | G_END_DECLS
25 |
26 | #endif // !ASTAL_RIVER_OUTPUT_PRIVATE_H
27 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | astal = {
5 | url = "github:aylur/astal";
6 | inputs.nixpkgs.follows = "nixpkgs";
7 | };
8 | };
9 |
10 | outputs = {
11 | self,
12 | nixpkgs,
13 | astal,
14 | }: let
15 | system = "x86_64-linux";
16 | pkgs = nixpkgs.legacyPackages.${system};
17 |
18 | nativeBuildInputs = with pkgs; [
19 | meson
20 | ninja
21 | pkg-config
22 | gobject-introspection
23 | wrapGAppsHook4
24 | blueprint-compiler
25 | dart-sass
26 | vala
27 | ];
28 |
29 | astalPackages = with astal.packages.${system}; [
30 | astal4
31 | battery
32 | wireplumber
33 | network
34 | mpris
35 | powerprofiles
36 | tray
37 | bluetooth
38 | ];
39 | in {
40 | packages.${system}.default = pkgs.stdenv.mkDerivation {
41 | name = "simple-bar";
42 | src = ./.;
43 | inherit nativeBuildInputs;
44 | buildInputs = astalPackages;
45 | };
46 |
47 | devShells.${system}.default = pkgs.mkShell {
48 | packages = nativeBuildInputs ++ astalPackages;
49 | };
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/lib/cava/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | mkAstalPkg,
3 | pkgs,
4 | ...
5 | }: let
6 | libcava = pkgs.stdenv.mkDerivation rec {
7 | pname = "cava";
8 | version = "0.10.6";
9 |
10 | src = pkgs.fetchFromGitHub {
11 | owner = "LukashonakV";
12 | repo = "cava";
13 | rev = "0.10.6";
14 | hash = "sha256-63be1wypMiqhPA6sjMebmFE6yKpTj/bUE53sMWun554=";
15 | };
16 |
17 | buildInputs = with pkgs; [
18 | alsa-lib
19 | libpulseaudio
20 | ncurses
21 | iniparser
22 | sndio
23 | SDL2
24 | libGL
25 | portaudio
26 | jack2
27 | pipewire
28 | ];
29 |
30 | propagatedBuildInputs = with pkgs; [
31 | fftw
32 | ];
33 |
34 | nativeBuildInputs = with pkgs; [
35 | autoreconfHook
36 | autoconf-archive
37 | pkgconf
38 | meson
39 | ninja
40 | ];
41 |
42 | preAutoreconf = ''
43 | echo ${version} > version
44 | '';
45 | };
46 | in
47 | mkAstalPkg {
48 | pname = "astal-cava";
49 | src = ./.;
50 | packages = [libcava];
51 |
52 | libname = "cava";
53 | authors = "kotontrion";
54 | gir-suffix = "Cava";
55 | description = "Audio visualization library using cava";
56 | }
57 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Astal Docs
2 |
3 | This directory contains the Astal documentation and Library references. Hosted
4 | at [aylur.github.io/astal](https://aylur.github.io/astal/) and
5 | [aylur.github.io/libastal](https://aylur.github.io/libastal/)
6 |
7 | ## Commands
8 |
9 | | Command | Action |
10 | | :---------------- | :------------------------------------------ |
11 | | `npm install` | Installs dependencies |
12 | | `npm run dev` | Starts local dev server at `localhost:5173` |
13 | | `npm run build` | Build your production site to `./dist/` |
14 | | `npm run preview` | Preview your build locally |
15 |
16 | ## Add your creation to the showcases page
17 |
18 | 1. Add your image as a webp to `public/showcase`
19 | 2. Add it to `showcases/showcases.ts`
20 | - `src` should be `/astal/showcase/your-name-optional-title.webp`
21 | - `url` should point to the source code of the showcased widget/setup
22 | - `author` should be your name/nickname
23 |
24 | ```
25 | .
26 | ├── public/showcase
27 | │ └── your-name-optional-title.webp # 1. add image
28 | └── showcases/
29 | └── showcases.ts # 2. add information
30 | ```
31 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | astal = {
5 | url = "github:aylur/astal";
6 | inputs.nixpkgs.follows = "nixpkgs";
7 | };
8 | };
9 |
10 | outputs = {
11 | self,
12 | nixpkgs,
13 | astal,
14 | }: let
15 | system = "x86_64-linux";
16 | pkgs = nixpkgs.legacyPackages.${system};
17 |
18 | nativeBuildInputs = with pkgs; [
19 | meson
20 | ninja
21 | pkg-config
22 | gobject-introspection
23 | wrapGAppsHook4
24 | blueprint-compiler
25 | dart-sass
26 | esbuild
27 | ];
28 |
29 | astalPackages = with astal.packages.${system}; [
30 | astal4
31 | battery
32 | wireplumber
33 | network
34 | mpris
35 | powerprofiles
36 | tray
37 | bluetooth
38 | ];
39 | in {
40 | packages.${system}.default = pkgs.stdenv.mkDerivation {
41 | name = "simple-bar";
42 | src = ./.;
43 | inherit nativeBuildInputs;
44 | buildInputs = astalPackages ++ [pkgs.gjs];
45 | };
46 |
47 | devShells.${system}.default = pkgs.mkShell {
48 | packages = nativeBuildInputs ++ astalPackages ++ [pkgs.gjs];
49 | };
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/lang/lua/astal-dev-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "astal"
2 | version = "dev-1"
3 |
4 | source = {
5 | url = "git+https://github.com/aylur/astal",
6 | }
7 |
8 | description = {
9 | summary = "lua bindings for libastal.",
10 | homepage = "https://aylur.github.io/astal/",
11 | license = "LGPL-2.1",
12 | }
13 |
14 | dependencies = {
15 | "lua >= 5.1, < 5.4",
16 | "lgi >= 0.9.2",
17 | }
18 |
19 | build = {
20 | type = "builtin",
21 | modules = {
22 | ["astal.binding"] = "astal/binding.lua",
23 | ["astal.file"] = "astal/file.lua",
24 | ["astal.init"] = "astal/init.lua",
25 | ["astal.process"] = "astal/process.lua",
26 | ["astal.time"] = "astal/time.lua",
27 | ["astal.variable"] = "astal/variable.lua",
28 | ["astal.gtk3.app"] = "astal/gtk3/app.lua",
29 | ["astal.gtk3.init"] = "astal/gtk3/init.lua",
30 | ["astal.gtk3.astalify"] = "astal/gtk3/astalify.lua",
31 | ["astal.gtk3.widget"] = "astal/gtk3/widget.lua",
32 | -- ["astal.gtk4.app"] = "astal/gtk4/app.lua",
33 | -- ["astal.gtk4.init"] = "astal/gtk4/init.lua",
34 | -- ["astal.gtk4.astalify"] = "astal/gtk4/astalify.lua",
35 | -- ["astal.gtk4.widget"] = "astal/gtk4/widget.lua",
36 | },
37 | }
38 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/stack.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * Subclass of [class@Gtk.Stack] that has a children setter which
3 | * invokes [method@Gt.Stack.add_named] with the child's [property@Gtk.Widget:name] property.
4 | */
5 | public class Astal.Stack : Gtk.Stack {
6 | /**
7 | * Same as [property@Gtk.Stack:visible-child-name].
8 | */
9 | [CCode (notify = false)]
10 | public string shown {
11 | get { return visible_child_name; }
12 | set { visible_child_name = value; }
13 | }
14 |
15 | public List children {
16 | set { _set_children(value); }
17 | owned get { return get_children(); }
18 | }
19 |
20 | private void _set_children(List arr) {
21 | foreach(var child in get_children()) {
22 | remove(child);
23 | }
24 |
25 | var i = 0;
26 | foreach(var child in arr) {
27 | if (child.name != null) {
28 | add_named(child, child.name);
29 | } else {
30 | add_named(child, (++i).to_string());
31 | }
32 | }
33 | }
34 |
35 | construct {
36 | notify["visible_child_name"].connect(() => {
37 | notify_property("shown");
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "astal": {
4 | "inputs": {
5 | "nixpkgs": [
6 | "nixpkgs"
7 | ]
8 | },
9 | "locked": {
10 | "lastModified": 1754893912,
11 | "narHash": "sha256-kzU/3A4k+d3PsgMLohzSh4KJybTqvzqibUVqV2yXCGY=",
12 | "owner": "aylur",
13 | "repo": "astal",
14 | "rev": "5d4eef66392b0dff99a63a4f39ff886624bd69dd",
15 | "type": "github"
16 | },
17 | "original": {
18 | "owner": "aylur",
19 | "repo": "astal",
20 | "type": "github"
21 | }
22 | },
23 | "nixpkgs": {
24 | "locked": {
25 | "lastModified": 1756266583,
26 | "narHash": "sha256-cr748nSmpfvnhqSXPiCfUPxRz2FJnvf/RjJGvFfaCsM=",
27 | "owner": "nixos",
28 | "repo": "nixpkgs",
29 | "rev": "8a6d5427d99ec71c64f0b93d45778c889005d9c2",
30 | "type": "github"
31 | },
32 | "original": {
33 | "owner": "nixos",
34 | "ref": "nixos-unstable",
35 | "repo": "nixpkgs",
36 | "type": "github"
37 | }
38 | },
39 | "root": {
40 | "inputs": {
41 | "astal": "astal",
42 | "nixpkgs": "nixpkgs"
43 | }
44 | }
45 | },
46 | "root": "root",
47 | "version": 7
48 | }
49 |
--------------------------------------------------------------------------------
/lib/auth/include/astal-auth.h.in:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_AUTH_PAM_H
2 | #define ASTAL_AUTH_PAM_H
3 |
4 | #include
5 | #include
6 |
7 |
8 | #define ASTAL_AUTH_MAJOR_VERSION @MAJOR_VERSION@
9 | #define ASTAL_AUTH_MINOR_VERSION @MINOR_VERSION@
10 | #define ASTAL_AUTH_MICRO_VERSION @MICRO_VERSION@
11 | #define ASTAL_AUTH_VERSION "@VERSION@"
12 |
13 | G_BEGIN_DECLS
14 |
15 | #define ASTAL_AUTH_TYPE_PAM (astal_auth_pam_get_type())
16 |
17 | G_DECLARE_FINAL_TYPE(AstalAuthPam, astal_auth_pam, ASTAL_AUTH, PAM, GObject)
18 |
19 | void astal_auth_pam_set_username(AstalAuthPam *self, const gchar *username);
20 |
21 | const gchar *astal_auth_pam_get_username(AstalAuthPam *self);
22 |
23 | void astal_auth_pam_set_service(AstalAuthPam *self, const gchar *service);
24 |
25 | const gchar *astal_auth_pam_get_service(AstalAuthPam *self);
26 |
27 | gboolean astal_auth_pam_start_authenticate(AstalAuthPam *self);
28 |
29 | void astal_auth_pam_supply_secret(AstalAuthPam *self, const gchar *secret);
30 |
31 | gboolean astal_auth_pam_authenticate(const gchar *password, GAsyncReadyCallback result_callback,
32 | gpointer user_data);
33 |
34 | gssize astal_auth_pam_authenticate_finish(GAsyncResult *res, GError **error);
35 |
36 | G_END_DECLS
37 |
38 | #endif // !ASTAL_AUTH_PAM_H
39 |
--------------------------------------------------------------------------------
/.github/workflows/vitepress.yml:
--------------------------------------------------------------------------------
1 | name: Deploy VitePress site
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 |
12 | jobs:
13 | build-vitepress:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 | with:
19 | fetch-depth: 0 # Not needed if lastUpdated is not enabled
20 |
21 | - name: Setup Node
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: 20
25 |
26 | - name: Setup Pages
27 | uses: actions/configure-pages@v4
28 |
29 | - name: Install dependencies
30 | run: npm ci
31 | working-directory: docs
32 |
33 | - name: Build with VitePress
34 | run: npm run build
35 | working-directory: docs
36 |
37 | - name: Upload artifact
38 | uses: actions/upload-pages-artifact@v3
39 | with:
40 | path: docs/dist
41 |
42 | deploy-vitepress:
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | needs: build-vitepress
47 | runs-on: ubuntu-latest
48 | steps:
49 | - name: Deploy to GitHub Pages
50 | id: deployment
51 | uses: actions/deploy-pages@v4
52 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | astal = {
5 | url = "github:aylur/astal";
6 | inputs.nixpkgs.follows = "nixpkgs";
7 | };
8 | };
9 |
10 | outputs = {
11 | self,
12 | nixpkgs,
13 | astal,
14 | }: let
15 | system = "x86_64-linux";
16 | pkgs = nixpkgs.legacyPackages.${system};
17 |
18 | python = pkgs.python3.withPackages (ps: [
19 | ps.pygobject3
20 | ]);
21 |
22 | nativeBuildInputs = with pkgs; [
23 | meson
24 | ninja
25 | pkg-config
26 | gobject-introspection
27 | wrapGAppsHook4
28 | blueprint-compiler
29 | dart-sass
30 | ];
31 |
32 | astalPackages = with astal.packages.${system}; [
33 | astal4
34 | battery
35 | wireplumber
36 | network
37 | mpris
38 | powerprofiles
39 | tray
40 | bluetooth
41 | ];
42 | in {
43 | packages.${system}.default = pkgs.stdenv.mkDerivation {
44 | name = "simple-bar";
45 | src = ./.;
46 | inherit nativeBuildInputs;
47 | buildInputs = astalPackages ++ [python];
48 | };
49 |
50 | devShells.${system}.default = pkgs.mkShell {
51 | packages = nativeBuildInputs ++ astalPackages ++ [python];
52 | };
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/lib/hyprland/src/cli.vala:
--------------------------------------------------------------------------------
1 | static bool help;
2 | static bool version;
3 |
4 | const OptionEntry[] options = {
5 | { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
6 | { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null },
7 | { null },
8 | };
9 |
10 | int main(string[] argv) {
11 | try {
12 | var opts = new OptionContext();
13 | opts.add_main_entries(options, null);
14 | opts.set_help_enabled(false);
15 | opts.set_ignore_unknown_options(false);
16 | opts.parse(ref argv);
17 | } catch (OptionError err) {
18 | printerr(err.message);
19 | return 1;
20 | }
21 |
22 | if (help) {
23 | print("Usage:\n");
24 | print(" %s [flags]\n\n", argv[0]);
25 | print("Flags:\n");
26 | print(" -h, --help Print this help and exit\n");
27 | print(" -v, --version Print version number and exit\n");
28 | return 0;
29 | }
30 |
31 | if (version) {
32 | print(AstalHyprland.VERSION);
33 | return 0;
34 | }
35 |
36 | AstalHyprland.Hyprland.get_default().event.connect((event, args) => {
37 | print("{ event: \"%s\", payload: \"%s\" }\n", event, args);
38 | });
39 |
40 | new MainLoop(null, false).run();
41 | return 0;
42 | }
43 |
--------------------------------------------------------------------------------
/lang/lua/astal/file.lua:
--------------------------------------------------------------------------------
1 | local lgi = require("lgi")
2 | local Astal = lgi.require("AstalIO", "0.1")
3 | local GObject = lgi.require("GObject", "2.0")
4 |
5 | local M = {}
6 |
7 | ---@param path string
8 | ---@return string
9 | function M.read_file(path)
10 | return Astal.read_file(path)
11 | end
12 |
13 | ---@param path string
14 | ---@param callback fun(content: string, err: string): nil
15 | function M.read_file_async(path, callback)
16 | Astal.read_file_async(path, function(_, res)
17 | local content, err = Astal.read_file_finish(res)
18 | callback(content, err)
19 | end)
20 | end
21 |
22 | ---@param path string
23 | ---@param content string
24 | function M.write_file(path, content)
25 | Astal.write_file(path, content)
26 | end
27 |
28 | ---@param path string
29 | ---@param content string
30 | ---@param callback? fun(err: string): nil
31 | function M.write_file_async(path, content, callback)
32 | Astal.write_file_async(path, content, function(_, res)
33 | if type(callback) == "function" then
34 | callback(Astal.write_file_finish(res))
35 | end
36 | end)
37 | end
38 |
39 | ---@param path string
40 | ---@param callback fun(file: string, event: integer): nil
41 | function M.monitor_file(path, callback)
42 | return Astal.monitor_file(path, GObject.Closure(callback))
43 | end
44 |
45 | return M
46 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/README.md:
--------------------------------------------------------------------------------
1 | # Simple Astal Bar example in TypeScript
2 |
3 | This example shows you how to get a TypeScript+Blueprint+Sass project going.
4 |
5 | ## Dependencies
6 |
7 | - gjs
8 | - meson
9 | - esbuild
10 | - blueprint-compiler
11 | - sass
12 | - astal4
13 | - astal-battery
14 | - astal-wireplumber
15 | - astak-network
16 | - astal-mpris
17 | - astak-power-profiles
18 | - astal-tray
19 | - astal-bluetooth
20 |
21 | ## How to use
22 |
23 | > [!NOTE]
24 | > If you are on Nix, there is an example flake included
25 | > otherwise feel free to `rm flake.nix`
26 |
27 | - generate types with `ts-for-gir`
28 |
29 | ```sh
30 | # might take a while
31 | # also, don't worry about warning and error logs
32 | npx @ts-for-gir/cli generate --ignoreVersionConflicts
33 | ```
34 |
35 | - developing
36 |
37 | ```sh
38 | meson setup build --wipe --prefix "$(pwd)/result"
39 | meson install -C build
40 | ./result/bin/simple-bar
41 | ```
42 |
43 | - installing
44 |
45 | ```sh
46 | meson setup build --wipe
47 | meson install -C build
48 | simple-bar
49 | ```
50 |
51 | - adding new typescript files requires no additional steps
52 | - adding new scss files requires no additional steps as long as they are imported from `style.scss`
53 | - adding new ui (blueprint) files will also have to be listed in `meson.build` and in `gresource.xml`
54 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/audio.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WIREPLUMBER_AUDIO_H
2 | #define ASTAL_WIREPLUMBER_AUDIO_H
3 |
4 | #include
5 |
6 | #include "device.h"
7 | #include "endpoint.h"
8 | #include "stream.h"
9 |
10 | G_BEGIN_DECLS
11 |
12 | #define ASTAL_WP_TYPE_AUDIO (astal_wp_audio_get_type())
13 |
14 | G_DECLARE_FINAL_TYPE(AstalWpAudio, astal_wp_audio, ASTAL_WP, AUDIO, GObject)
15 |
16 | AstalWpEndpoint *astal_wp_audio_get_speaker(AstalWpAudio *self, guint id);
17 | AstalWpEndpoint *astal_wp_audio_get_microphone(AstalWpAudio *self, guint id);
18 | AstalWpStream *astal_wp_audio_get_recorder(AstalWpAudio *self, guint id);
19 | AstalWpStream *astal_wp_audio_get_stream(AstalWpAudio *self, guint id);
20 | AstalWpNode *astal_wp_audio_get_node(AstalWpAudio *self, guint id);
21 | AstalWpDevice *astal_wp_audio_get_device(AstalWpAudio *self, guint id);
22 |
23 | AstalWpEndpoint *astal_wp_audio_get_default_speaker(AstalWpAudio *self);
24 | AstalWpEndpoint *astal_wp_audio_get_default_microphone(AstalWpAudio *self);
25 |
26 | GList *astal_wp_audio_get_microphones(AstalWpAudio *self);
27 | GList *astal_wp_audio_get_speakers(AstalWpAudio *self);
28 | GList *astal_wp_audio_get_recorders(AstalWpAudio *self);
29 | GList *astal_wp_audio_get_streams(AstalWpAudio *self);
30 | GList *astal_wp_audio_get_devices(AstalWpAudio *self);
31 |
32 | G_END_DECLS
33 |
34 | #endif // !ASTAL_WIREPLUMBER_AUDIO_H
35 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/wp.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WIREPLUMBER_H
2 | #define ASTAL_WIREPLUMBER_H
3 |
4 | #include
5 |
6 | #include "audio.h"
7 | #include "device.h"
8 | #include "enums.h"
9 | #include "node.h"
10 | #include "video.h"
11 |
12 | G_BEGIN_DECLS
13 |
14 | #define ASTAL_WP_TYPE_WP (astal_wp_wp_get_type())
15 |
16 | G_DECLARE_FINAL_TYPE(AstalWpWp, astal_wp_wp, ASTAL_WP, WP, GObject)
17 |
18 | AstalWpWp* astal_wp_wp_get_default();
19 | AstalWpWp* astal_wp_get_default();
20 |
21 | AstalWpAudio* astal_wp_wp_get_audio(AstalWpWp* self);
22 | AstalWpVideo* astal_wp_wp_get_video(AstalWpWp* self);
23 | AstalWpVideo* astal_wp_video_new(AstalWpWp* wp);
24 | AstalWpAudio* astal_wp_audio_new(AstalWpWp* wp);
25 |
26 | AstalWpNode* astal_wp_wp_get_node(AstalWpWp* self, guint id);
27 | GList* astal_wp_wp_get_nodes(AstalWpWp* self);
28 | AstalWpNode* astal_wp_wp_get_node_by_serial(AstalWpWp* self, gint serial);
29 |
30 | AstalWpDevice* astal_wp_wp_get_device(AstalWpWp* self, guint id);
31 | GList* astal_wp_wp_get_devices(AstalWpWp* self);
32 |
33 | AstalWpEndpoint* astal_wp_wp_get_default_speaker(AstalWpWp* self);
34 | AstalWpEndpoint* astal_wp_wp_get_default_microphone(AstalWpWp* self);
35 |
36 | AstalWpScale astal_wp_wp_get_scale(AstalWpWp* self);
37 | void astal_wp_wp_set_scale(AstalWpWp* self, AstalWpScale scale);
38 |
39 | G_END_DECLS
40 |
41 | #endif // !ASTAL_WIREPLUMBER_H
42 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/private/node-private.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_NODE_PRIV_H
2 | #define ASTAL_WP_NODE_PRIV_H
3 |
4 | #include
5 | #include
6 |
7 | #include "endpoint.h"
8 | #include "node.h"
9 | #include "stream.h"
10 | #include "wp.h"
11 |
12 | G_BEGIN_DECLS
13 |
14 | AstalWpStream *astal_wp_stream_new(WpNode *node, WpPlugin *mixer, AstalWpWp *wp);
15 |
16 | AstalWpEndpoint *astal_wp_endpoint_new(WpNode *node, WpPlugin *mixer, WpPlugin *defaults,
17 | AstalWpWp *wp);
18 | AstalWpEndpoint *astal_wp_endpoint_new_default(AstalWpWp *wp);
19 |
20 | void astal_wp_endpoint_init_as_default(AstalWpEndpoint *self, WpPlugin *mixer, WpPlugin *defaults,
21 | AstalWpMediaClass type);
22 |
23 | void astal_wp_node_update_default(AstalWpNode *self, gboolean is_default);
24 | void astal_wp_node_update_volume(AstalWpNode *self);
25 | void astal_wp_node_set_channel_volume(AstalWpNode *self, const gchar *name, gdouble volume);
26 | void astal_wp_node_set_icon(AstalWpNode *self, const gchar *icon);
27 | void astal_wp_node_set_node(AstalWpNode *self, WpNode *node);
28 | void astal_wp_node_set_mixer(AstalWpNode *self, WpPlugin *mixer);
29 | void astal_wp_node_set_type(AstalWpNode *self, AstalWpMediaClass type);
30 |
31 | void astal_wp_node_properties_changed(AstalWpNode *self);
32 | G_END_DECLS
33 |
34 | #endif // !ASTAL_WP_NODE_PRIV_H
35 |
--------------------------------------------------------------------------------
/lang/gjs/src/file.ts:
--------------------------------------------------------------------------------
1 | import Astal from "gi://AstalIO"
2 | import Gio from "gi://Gio?version=2.0"
3 |
4 | export { Gio }
5 |
6 | export function readFile(path: string): string {
7 | return Astal.read_file(path) || ""
8 | }
9 |
10 | export function readFileAsync(path: string): Promise {
11 | return new Promise((resolve, reject) => {
12 | Astal.read_file_async(path, (_, res) => {
13 | try {
14 | resolve(Astal.read_file_finish(res) || "")
15 | } catch (error) {
16 | reject(error)
17 | }
18 | })
19 | })
20 | }
21 |
22 | export function writeFile(path: string, content: string): void {
23 | Astal.write_file(path, content)
24 | }
25 |
26 | export function writeFileAsync(path: string, content: string): Promise {
27 | return new Promise((resolve, reject) => {
28 | Astal.write_file_async(path, content, (_, res) => {
29 | try {
30 | resolve(Astal.write_file_finish(res))
31 | } catch (error) {
32 | reject(error)
33 | }
34 | })
35 | })
36 | }
37 |
38 | export function monitorFile(
39 | path: string,
40 | callback: (file: string, event: Gio.FileMonitorEvent) => void,
41 | ): Gio.FileMonitor {
42 | return Astal.monitor_file(path, (file: string, event: Gio.FileMonitorEvent) => {
43 | callback(file, event)
44 | })!
45 | }
46 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/scrollable.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * Subclass of [class@Gtk.ScrolledWindow] which has its policy default to
3 | * [enum@Gtk.PolicyType.AUTOMATIC].
4 | *
5 | * Its css selector is `scrollable`.
6 | * Its child getter returns the child of the inner
7 | * [class@Gtk.Viewport], instead of the viewport.
8 | */
9 | public class Astal.Scrollable : Gtk.ScrolledWindow {
10 | private Gtk.PolicyType _hscroll = Gtk.PolicyType.AUTOMATIC;
11 | private Gtk.PolicyType _vscroll = Gtk.PolicyType.AUTOMATIC;
12 |
13 | public Gtk.PolicyType hscroll {
14 | get { return _hscroll; }
15 | set {
16 | _hscroll = value;
17 | set_policy(value, vscroll);
18 | }
19 | }
20 |
21 | public Gtk.PolicyType vscroll {
22 | get { return _vscroll; }
23 | set {
24 | _vscroll = value;
25 | set_policy(hscroll, value);
26 | }
27 | }
28 |
29 | static construct {
30 | set_css_name("scrollable");
31 | }
32 |
33 | construct {
34 | if (hadjustment != null)
35 | hadjustment = new Gtk.Adjustment(0,0,0,0,0,0);
36 |
37 | if (vadjustment != null)
38 | vadjustment = new Gtk.Adjustment(0,0,0,0,0,0);
39 | }
40 |
41 | public new Gtk.Widget get_child() {
42 | var ch = base.get_child();
43 | if (ch is Gtk.Viewport) {
44 | return ch.get_child();
45 | }
46 | return ch;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/wayland-glib/wl-source.vala:
--------------------------------------------------------------------------------
1 | namespace WlGlib {
2 | public class WlSource : Source {
3 |
4 | public Wl.Display display;
5 | public void* fd;
6 | public int error;
7 |
8 | public override bool dispatch(SourceFunc callback) {
9 | IOCondition revents = this.query_unix_fd(this.fd);
10 | if (this.error > 0 || (revents & (IOCondition.ERR | IOCondition.HUP)) != 0) {
11 | errno = this.error;
12 | if(callback != null) return callback();
13 | return Source.REMOVE;
14 | }
15 | if (((revents & IOCondition.IN) != 0) && this.display.dispatch() < 0) {
16 | if(callback != null) return callback();
17 | return Source.REMOVE;
18 | }
19 | return Source.CONTINUE;
20 | }
21 |
22 | public override bool check() {
23 | IOCondition revents = this.query_unix_fd(this.fd);
24 | return revents > 0;
25 | }
26 |
27 | public override bool prepare(out int timeout) {
28 | if(this.display.flush() < 0)
29 | this.error = errno;
30 | timeout = -1;
31 | return false;
32 | }
33 |
34 | public WlSource() {
35 | base();
36 | this.display = new Wl.Display.connect(null);
37 | if(this.display == null) return;
38 | this.fd = this.add_unix_fd(this.display.get_fd(),
39 | IOCondition.IN | IOCondition.ERR | IOCondition.HUP);
40 | this.attach(null);
41 | }
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | outputs = {
3 | self,
4 | nixpkgs,
5 | }: let
6 | forAllSystems = nixpkgs.lib.genAttrs ["x86_64-linux" "aarch64-linux"];
7 | in {
8 | packages = forAllSystems (system: let
9 | pkgs = nixpkgs.legacyPackages.${system};
10 | mkPkg = src:
11 | import src {
12 | inherit self pkgs;
13 | mkAstalPkg = import ./nix/mkAstalPkg.nix pkgs;
14 | };
15 | in {
16 | default = self.packages.${system}.io;
17 | docs = import ./docs {inherit self pkgs;};
18 |
19 | io = mkPkg ./lib/astal/io;
20 | astal3 = mkPkg ./lib/astal/gtk3;
21 | astal4 = mkPkg ./lib/astal/gtk4;
22 | apps = mkPkg ./lib/apps;
23 | auth = mkPkg ./lib/auth;
24 | battery = mkPkg ./lib/battery;
25 | bluetooth = mkPkg ./lib/bluetooth;
26 | cava = mkPkg ./lib/cava;
27 | greet = mkPkg ./lib/greet;
28 | hyprland = mkPkg ./lib/hyprland;
29 | mpris = mkPkg ./lib/mpris;
30 | network = mkPkg ./lib/network;
31 | notifd = mkPkg ./lib/notifd;
32 | powerprofiles = mkPkg ./lib/powerprofiles;
33 | river = mkPkg ./lib/river;
34 | tray = mkPkg ./lib/tray;
35 | wireplumber = mkPkg ./lib/wireplumber;
36 | });
37 |
38 | devShells = forAllSystems (system:
39 | import ./nix/devshell.nix {
40 | inherit self;
41 | pkgs = nixpkgs.legacyPackages.${system};
42 | });
43 | };
44 |
45 | inputs = {
46 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
47 | };
48 | }
49 |
--------------------------------------------------------------------------------
/docs/showcases/Showcases.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
36 |
37 |
38 |
64 |
--------------------------------------------------------------------------------
/lib/river/src/astal-river.c:
--------------------------------------------------------------------------------
1 | #include "astal-river.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "gio/gio.h"
8 |
9 | GMainLoop* loop;
10 |
11 | void print_json(AstalRiverRiver* river) {
12 | JsonNode* json = json_gobject_serialize(G_OBJECT(river));
13 |
14 | gchar* json_str = json_to_string(json, FALSE);
15 | g_print("%s\n", json_str);
16 | json_node_free(json);
17 | g_free(json_str);
18 | }
19 |
20 | int main(int argc, char** argv) {
21 | gboolean daemon = FALSE;
22 |
23 | int opt;
24 | const char* optstring = "d";
25 |
26 | static struct option long_options[] = {{"daemon", no_argument, NULL, 'd'}, {NULL, 0, NULL, 0}};
27 |
28 | while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) {
29 | switch (opt) {
30 | case 'd':
31 | daemon = TRUE;
32 | break;
33 | default:
34 | g_print("Usage: %s [-d]\n", argv[0]);
35 | exit(EXIT_FAILURE);
36 | }
37 | }
38 |
39 | GError* error = NULL;
40 | AstalRiverRiver* river = g_initable_new(ASTAL_RIVER_TYPE_RIVER, NULL, &error, NULL);
41 | if (error) {
42 | g_critical("%s\n", error->message);
43 | exit(EXIT_FAILURE);
44 | }
45 | if (daemon) {
46 | loop = g_main_loop_new(NULL, FALSE);
47 | g_signal_connect(river, "changed", G_CALLBACK(print_json), NULL);
48 | g_main_loop_run(loop);
49 | } else {
50 | print_json(river);
51 | g_object_unref(river);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/box.vala:
--------------------------------------------------------------------------------
1 | public class Astal.Box : Gtk.Box {
2 | /**
3 | * Corresponds to [property@Gtk.Orientable :orientation].
4 | */
5 | [CCode (notify = false)]
6 | public bool vertical {
7 | get { return orientation == Gtk.Orientation.VERTICAL; }
8 | set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; }
9 | }
10 |
11 | public List children {
12 | set { _set_children(value); }
13 | owned get { return get_children(); }
14 | }
15 |
16 | public new Gtk.Widget child {
17 | owned get { return _get_child(); }
18 | set { _set_child(value); }
19 | }
20 |
21 | construct {
22 | notify["orientation"].connect(() => {
23 | notify_property("vertical");
24 | });
25 | }
26 |
27 | private void _set_child(Gtk.Widget child) {
28 | var list = new List();
29 | list.append(child);
30 | _set_children(list);
31 | }
32 |
33 | private Gtk.Widget? _get_child() {
34 | foreach(var child in get_children())
35 | return child;
36 |
37 | return null;
38 | }
39 |
40 | private void _set_children(List arr) {
41 | foreach(var child in get_children()) {
42 | remove(child);
43 | }
44 |
45 | foreach(var child in arr)
46 | add(child);
47 | }
48 |
49 | public Box(bool vertical, List children) {
50 | this.vertical = vertical;
51 | _set_children(children);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/src/widget/box.vala:
--------------------------------------------------------------------------------
1 | [Version (deprecated = true, deprecated_since="", replacement="")]
2 | public class Astal.Box : Gtk.Box {
3 | /**
4 | * Corresponds to [property@Gtk.Orientable :orientation].
5 | */
6 | [CCode (notify = false)]
7 | public bool vertical {
8 | get { return orientation == Gtk.Orientation.VERTICAL; }
9 | set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; }
10 | }
11 |
12 | construct {
13 | notify["orientation"].connect(() => {
14 | notify_property("vertical");
15 | });
16 | }
17 |
18 | public List children {
19 | set {
20 | foreach (var child in children) {
21 | remove(child);
22 | }
23 | foreach (var child in value) {
24 | append(child);
25 | }
26 | }
27 | owned get {
28 | var list = new List();
29 | var child = get_first_child();
30 | while (child != null) {
31 | list.append(child);
32 | child = child.get_next_sibling();
33 | }
34 | return list;
35 | }
36 | }
37 |
38 | public Gtk.Widget? child {
39 | owned get {
40 | foreach (var child in children) {
41 | return child;
42 | }
43 | return null;
44 | }
45 | set {
46 | var list = new List();
47 | list.append(child);
48 | this.children = children;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/auth/src/meson.build:
--------------------------------------------------------------------------------
1 | srcs = files(
2 | 'pam.c',
3 | )
4 |
5 | deps = [dependency('gobject-2.0'), dependency('gio-2.0'), dependency('pam')]
6 |
7 | astal_auth_lib = library(
8 | 'astal-auth',
9 | sources: srcs,
10 | include_directories: astal_auth_inc,
11 | dependencies: deps,
12 | version: meson.project_version(),
13 | install: true,
14 | )
15 |
16 | libastal_auth = declare_dependency(link_with: astal_auth_lib, include_directories: astal_auth_inc)
17 |
18 | executable(
19 | 'astal-auth',
20 | files('astal-auth.c'),
21 | dependencies: [dependency('gobject-2.0'), libastal_auth],
22 | install: true,
23 | )
24 |
25 | pkg_config_name = 'astal-auth-' + lib_so_version
26 |
27 | if get_option('introspection')
28 | gir = gnome.generate_gir(
29 | astal_auth_lib,
30 | sources: srcs + astal_auth_headers,
31 | nsversion: '0.1',
32 | namespace: 'AstalAuth',
33 | symbol_prefix: 'astal_auth',
34 | identifier_prefix: 'AstalAuth',
35 | includes: ['GObject-2.0', 'Gio-2.0'],
36 | header: 'astal-auth.h',
37 | export_packages: pkg_config_name,
38 | install: true,
39 | )
40 |
41 | if get_option('vapi')
42 | gnome.generate_vapi(
43 | pkg_config_name,
44 | sources: [gir[0]],
45 | packages: ['gobject-2.0', 'gio-2.0'],
46 | install: true,
47 | )
48 | endif
49 | endif
50 |
51 | pkg_config.generate(
52 | name: 'astal-auth',
53 | version: meson.project_version(),
54 | libraries: [astal_auth_lib],
55 | filebase: pkg_config_name,
56 | subdirs: 'astal',
57 | description: 'astal authentication module',
58 | url: 'https://github.com/astal-sh/auth',
59 | )
60 |
--------------------------------------------------------------------------------
/.github/workflows/gi-docs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy GI docs
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | permissions:
8 | contents: write
9 | pages: write
10 | id-token: write
11 |
12 | jobs:
13 | gi-docs:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Install Nix
17 | uses: DeterminateSystems/nix-installer-action@main
18 | with:
19 | logger: pretty
20 |
21 | - name: Checkout Source Repository
22 | uses: actions/checkout@v4
23 | with:
24 | path: src
25 |
26 | - name: Build Documentation
27 | run: |
28 | cd src
29 | nix build .#docs --print-build-logs
30 |
31 | - name: Checkout Destination Repo
32 | uses: actions/checkout@v4
33 | with:
34 | token: ${{ secrets.token }}
35 | repository: aylur/aylur.github.io
36 | ref: main
37 | path: dist
38 |
39 | - name: Configure Git
40 | run: |
41 | git config --global user.email "github-actions[bot]@users.noreply.github.com"
42 | git config --global user.name "GitHub Actions Bot"
43 |
44 | - name: Clean and Copy Files
45 | run: |
46 | rm -rf dist/libastal
47 | mkdir dist/libastal
48 | cp -r src/result/* dist/libastal
49 |
50 | - name: Push to Pages Repo
51 | run: |
52 | cd dist
53 | git add .
54 | if [ -n "$(git diff --cached)" ]; then
55 | git commit -m "Deployed from https://github.com/${{ github.repository }}/commit/${{ github.sha }}"
56 | git push origin main
57 | fi
58 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/device.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_DEVICE_H
2 | #define ASTAL_WP_DEVICE_H
3 |
4 | #include
5 |
6 | #include "profile.h"
7 | #include "route.h"
8 |
9 | G_BEGIN_DECLS
10 |
11 | #define ASTAL_WP_TYPE_DEVICE (astal_wp_device_get_type())
12 |
13 | G_DECLARE_FINAL_TYPE(AstalWpDevice, astal_wp_device, ASTAL_WP, DEVICE, GObject)
14 |
15 | guint astal_wp_device_get_id(AstalWpDevice *self);
16 | const gchar *astal_wp_device_get_description(AstalWpDevice *self);
17 | const gchar *astal_wp_device_get_icon(AstalWpDevice *self);
18 | AstalWpDeviceType astal_wp_device_get_device_type(AstalWpDevice *self);
19 | const gchar *astal_wp_device_get_form_factor(AstalWpDevice *self);
20 |
21 | AstalWpProfile *astal_wp_device_get_profile(AstalWpDevice *self, gint id);
22 | GList *astal_wp_device_get_profiles(AstalWpDevice *self);
23 | void astal_wp_device_set_active_profile_id(AstalWpDevice *self, int profile_id);
24 | gint astal_wp_device_get_active_profile_id(AstalWpDevice *self);
25 |
26 | gint astal_wp_device_get_input_route_id(AstalWpDevice *self);
27 | gint astal_wp_device_get_output_route_id(AstalWpDevice *self);
28 | AstalWpRoute *astal_wp_device_get_route(AstalWpDevice *self, gint id);
29 | void astal_wp_device_set_route(AstalWpDevice *self, AstalWpRoute *route, guint card_device);
30 | GList *astal_wp_device_get_routes(AstalWpDevice *self);
31 | GList *astal_wp_device_get_input_routes(AstalWpDevice *self);
32 | GList *astal_wp_device_get_output_routes(AstalWpDevice *self);
33 |
34 | gchar *astal_wp_device_get_pw_property(AstalWpDevice *self, const gchar *key);
35 | G_END_DECLS
36 |
37 | #endif // !ASTAL_WP_DEVICE_H
38 |
--------------------------------------------------------------------------------
/nix/devshell.nix:
--------------------------------------------------------------------------------
1 | {
2 | self,
3 | pkgs,
4 | }: let
5 | lua = pkgs.lua.withPackages (ps: [
6 | ps.lgi
7 | (ps.luaPackages.toLuaModule (pkgs.stdenv.mkDerivation {
8 | name = "astal";
9 | src = "${self}/lang/lua/astal";
10 | dontBuild = true;
11 | installPhase = ''
12 | mkdir -p $out/share/lua/${ps.lua.luaversion}/astal
13 | cp -r * $out/share/lua/${ps.lua.luaversion}/astal
14 | '';
15 | }))
16 | ]);
17 |
18 | python = pkgs.python3.withPackages (ps: [
19 | ps.pygobject3
20 | ps.pygobject-stubs
21 | ]);
22 |
23 | buildInputs = with pkgs; [
24 | wrapGAppsHook3
25 | gobject-introspection
26 | meson
27 | pkg-config
28 | ninja
29 | vala
30 | gtk3
31 | gtk4
32 | gtk-layer-shell
33 | gtk4-layer-shell
34 | json-glib
35 | pam
36 | gvfs
37 | networkmanager
38 | gdk-pixbuf
39 | wireplumber
40 | libdbusmenu-gtk3
41 | wayland
42 | blueprint-compiler
43 | libadwaita
44 | wayland-scanner
45 | dart-sass
46 | esbuild
47 | lua
48 | python
49 | gjs
50 | ];
51 |
52 | dev = with pkgs; [
53 | nodejs
54 | mesonlsp
55 | vala-language-server
56 | vtsls
57 | vscode-langservers-extracted
58 | markdownlint-cli2
59 | pyright
60 | ruff
61 | uncrustify
62 | ];
63 | in {
64 | default = pkgs.mkShell {
65 | packages = buildInputs ++ dev;
66 | };
67 | astal = pkgs.mkShell {
68 | packages =
69 | buildInputs
70 | ++ dev
71 | ++ builtins.attrValues (
72 | builtins.removeAttrs self.packages.${pkgs.stdenv.hostPlatform.system} ["docs"]
73 | );
74 | };
75 | }
76 |
--------------------------------------------------------------------------------
/lib/tray/src/cli.vala:
--------------------------------------------------------------------------------
1 | static bool version;
2 | static bool daemonize;
3 |
4 | const OptionEntry[] options = {
5 | { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, "Print version number", null },
6 | { "daemonize", 'd', OptionFlags.NONE, OptionArg.NONE, ref daemonize, "Monitor the systemtray", null },
7 | { null },
8 | };
9 |
10 | int main(string[] argv) {
11 | try {
12 | var opts = new OptionContext();
13 | opts.add_main_entries(options, null);
14 | opts.set_help_enabled(true);
15 | opts.set_ignore_unknown_options(false);
16 | opts.parse(ref argv);
17 | } catch (OptionError err) {
18 | printerr(err.message);
19 | return 1;
20 | }
21 |
22 | if (version) {
23 | print(AstalTray.VERSION);
24 | return 0;
25 | }
26 |
27 | if (daemonize) {
28 | var loop = new MainLoop();
29 | var tray = new AstalTray.Tray();
30 |
31 | tray.item_added.connect((id) => {
32 | AstalTray.TrayItem item = tray.get_item(id);
33 |
34 | stdout.printf("{\"event\":\"item_added\",\"id\":\"%s\",\"item\":%s}\n",
35 | id, item.to_json_string());
36 | stdout.flush();
37 |
38 | item.changed.connect(() => {
39 | stdout.printf("{\"event\":\"item_changed\",\"id\":\"%s\",\"item\":%s}\n",
40 | id, item.to_json_string());
41 | stdout.flush();
42 | });
43 | });
44 |
45 | tray.item_removed.connect((id) => {
46 | stdout.printf("{\"event\":\"item_removed\",\"id\":\"%s\"}\n", id);
47 | stdout.flush();
48 | });
49 |
50 | loop.run();
51 | }
52 |
53 | return 0;
54 | }
55 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/vala/app.in.vala:
--------------------------------------------------------------------------------
1 | class App : Gtk.Application {
2 | static App instance;
3 |
4 | private Bar bar;
5 |
6 | private void init_css() {
7 | var provider = new Gtk.CssProvider();
8 | provider.load_from_data("""@STYLE@""");
9 |
10 | Gtk.StyleContext.add_provider_for_screen(
11 | Gdk.Screen.get_default(),
12 | provider,
13 | Gtk.STYLE_PROVIDER_PRIORITY_USER
14 | );
15 | }
16 |
17 | // this is the method that will be invoked on `app.run()`
18 | // this is where everything should be initialized and instantiated
19 | public override int command_line(ApplicationCommandLine command_line) {
20 | var argv = command_line.get_arguments();
21 |
22 | if (command_line.is_remote) {
23 | // app is already running we can print to remote
24 | command_line.print_literal("hello from the main instance\n");
25 |
26 | // for example, we could toggle the visibility of the bar
27 | if (argv.length >= 3 && argv[1] == "toggle" && argv[2] == "bar") {
28 | bar.visible = !bar.visible;
29 | }
30 | } else {
31 | // main instance, initialize stuff here
32 | init_css();
33 | add_window((bar = new Bar()));
34 | }
35 |
36 | return 0;
37 | }
38 |
39 | private App() {
40 | application_id = "my.awesome.simple-bar";
41 | flags = ApplicationFlags.HANDLES_COMMAND_LINE;
42 | }
43 |
44 | // entry point of our app
45 | static int main(string[] argv) {
46 | App.instance = new App();
47 | Environment.set_prgname("simple-bar");
48 | return App.instance.run(argv);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/app/App.py:
--------------------------------------------------------------------------------
1 | from gi.repository import Gio, GLib, Gtk, Gdk, GLib
2 | from bar.Bar import Bar
3 |
4 |
5 | class App(Gtk.Application):
6 | __gtype_name__ = "App"
7 |
8 | def _init_css(self):
9 | provider = Gtk.CssProvider()
10 | provider.load_from_resource("/style.css")
11 |
12 | Gtk.StyleContext.add_provider_for_display(
13 | Gdk.Display.get_default(),
14 | provider,
15 | Gtk.STYLE_PROVIDER_PRIORITY_USER,
16 | )
17 |
18 | # this is the method that will be invoked on `app.run()`
19 | # this is where everything should be initialized and instantiated
20 | def do_command_line(self, command_line):
21 | argv = command_line.get_arguments()
22 |
23 | if command_line.get_is_remote():
24 | # app is already running we can print to remote
25 | command_line.print_literal("hello from the main instance\n")
26 |
27 | # or for example, we could toggle the visibility of the bar
28 | if len(argv) >= 3 and argv[1] == "toggle" and argv[2] == "bar":
29 | self.bar.set_visible(not self.bar.get_visible())
30 | else:
31 | # main instance, initialize stuff here
32 | self._init_css()
33 | self.bar = Bar()
34 | self.add_window(self.bar)
35 |
36 | return 0
37 |
38 | def __init__(self) -> None:
39 | super().__init__(
40 | application_id="my.awesome.simple-bar",
41 | flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
42 | )
43 |
44 | @staticmethod
45 | def main(argv):
46 | App.instance = App()
47 | GLib.set_prgname("simple-bar")
48 | return App.instance.run(argv)
49 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/meson.build:
--------------------------------------------------------------------------------
1 | pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
2 | bindir = get_option('prefix') / get_option('bindir')
3 |
4 | blp = find_program('blueprint-compiler', required: true)
5 | sass = find_program('sass', required: true)
6 | python = find_program('python3', required: true)
7 | layer_shell = dependency('gtk4-layer-shell-0')
8 |
9 | blueprint_sources = files(
10 | 'bar/Bar.blp',
11 | )
12 |
13 | # transplie blueprints
14 | ui = custom_target(
15 | 'blueprint',
16 | input: blueprint_sources,
17 | output: '.',
18 | command: [
19 | blp,
20 | 'batch-compile',
21 | '@OUTPUT@',
22 | '@CURRENT_SOURCE_DIR@',
23 | '@INPUT@',
24 | ],
25 | )
26 |
27 | # bundle styles
28 | css = custom_target(
29 | 'scss',
30 | input: files('style.scss'),
31 | command: [sass, '@INPUT@', '@OUTPUT@'],
32 | output: 'style.css',
33 | )
34 |
35 | # compiling ui and css into a binary
36 | import('gnome').compile_resources(
37 | 'data',
38 | files('gresource.xml'),
39 | dependencies: [ui, css],
40 | gresource_bundle: true,
41 | install: true,
42 | install_dir: pkgdatadir,
43 | )
44 |
45 | # install python sources
46 | install_data(
47 | 'app/__init__.py',
48 | 'app/App.py',
49 | install_dir: pkgdatadir / 'app',
50 | )
51 |
52 | install_data(
53 | 'bar/__init__.py',
54 | 'bar/Bar.py',
55 | install_dir: pkgdatadir / 'bar',
56 | )
57 |
58 | # configure the main python entry file
59 | configure_file(
60 | input: 'main.in.py',
61 | output: meson.project_name(),
62 | configuration: {
63 | 'PYTHON': python.full_path(),
64 | 'LAYER_SHELL_PREFIX': layer_shell.get_variable('prefix'),
65 | 'PKGDATADIR': pkgdatadir,
66 | },
67 | install: true,
68 | install_dir: bindir,
69 | )
70 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/meson.build:
--------------------------------------------------------------------------------
1 | pkgdatadir = get_option('prefix') / get_option('datadir')
2 | bindir = get_option('prefix') / get_option('bindir')
3 | blp = find_program('blueprint-compiler', required: true)
4 | sass = find_program('sass', required: true)
5 |
6 | dependencies = [
7 | dependency('gtk4-layer-shell-0'),
8 | dependency('astal-io-0.1'),
9 | dependency('glib-2.0'),
10 | dependency('astal-4-4.0'),
11 | dependency('astal-battery-0.1'),
12 | dependency('astal-wireplumber-0.1'),
13 | dependency('astal-network-0.1'),
14 | dependency('libnm'),
15 | dependency('astal-mpris-0.1'),
16 | dependency('astal-power-profiles-0.1'),
17 | dependency('astal-tray-0.1'),
18 | dependency('astal-bluetooth-0.1'),
19 | ]
20 |
21 | blueprint_sources = files(
22 | 'bar/Bar.blp',
23 | )
24 |
25 | vala_sources = files(
26 | 'bar/Bar.vala',
27 | 'App.vala',
28 | )
29 |
30 | # transplie blueprints
31 | ui = custom_target(
32 | 'blueprint',
33 | input: blueprint_sources,
34 | output: '.',
35 | command: [
36 | blp,
37 | 'batch-compile',
38 | '@OUTPUT@',
39 | '@CURRENT_SOURCE_DIR@',
40 | '@INPUT@',
41 | ],
42 | )
43 |
44 | # bundle scss files
45 | css = custom_target(
46 | 'scss',
47 | input: files('style.scss'),
48 | command: [sass, '@INPUT@', '@OUTPUT@'],
49 | output: ['style.css'],
50 | )
51 |
52 | # compiling data files into a binary
53 | resource = import('gnome').compile_resources(
54 | 'data',
55 | files('gresource.xml'),
56 | dependencies: [ui, css],
57 | source_dir: meson.current_build_dir(),
58 | )
59 |
60 | executable(
61 | meson.project_name(),
62 | dependencies: dependencies,
63 | sources: [vala_sources, resource],
64 | link_args: ['-lm'], # Link math library
65 | install: true,
66 | install_dir: bindir,
67 | )
68 |
--------------------------------------------------------------------------------
/lib/astal/gtk4/src/widget/slider.vala:
--------------------------------------------------------------------------------
1 | public class Astal.Slider : Gtk.Scale {
2 | construct {
3 | if (adjustment == null)
4 | adjustment = new Gtk.Adjustment(0,0,0,0,0,0);
5 |
6 | if (max == 0 && min == 0) {
7 | max = 1;
8 | }
9 |
10 | if (step == 0) {
11 | step = 0.05;
12 | }
13 |
14 | if (page == 0) {
15 | page = 0.01;
16 | }
17 |
18 | this.change_value.connect((range, scroll, value) => {
19 | if (this.value == value) {
20 | this.notify_property("value");
21 | }
22 | });
23 | }
24 |
25 | /**
26 | * Value of this slider. Defaults to `0`.
27 | */
28 | [CCode (notify = false)]
29 | public double value {
30 | get { return adjustment.value; }
31 | set { adjustment.value = value; }
32 | }
33 |
34 | /**
35 | * Minimum possible value of this slider. Defaults to `0`.
36 | */
37 | public double min {
38 | get { return adjustment.lower; }
39 | set { adjustment.lower = value; }
40 | }
41 |
42 | /**
43 | * Maximum possible value of this slider. Defaults to `1`.
44 | */
45 | public double max {
46 | get { return adjustment.upper; }
47 | set { adjustment.upper = value; }
48 | }
49 |
50 | /**
51 | * Size of step increments. Defaults to `0.05`.
52 | */
53 | public double step {
54 | get { return adjustment.step_increment; }
55 | set { adjustment.step_increment = value; }
56 | }
57 |
58 | /**
59 | * Size of page increments. Defaults to `0.01`.
60 | */
61 | public double page {
62 | get { return adjustment.page_increment; }
63 | set { adjustment.page_increment = value; }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/App.vala:
--------------------------------------------------------------------------------
1 | class App : Gtk.Application {
2 | // alternative is to rely on GLib.Application.get_default
3 | static App instance;
4 |
5 | private Bar bar;
6 |
7 | private void init_css() {
8 | var provider = new Gtk.CssProvider();
9 | provider.load_from_resource("/style.css");
10 |
11 | Gtk.StyleContext.add_provider_for_display(
12 | Gdk.Display.get_default(),
13 | provider,
14 | Gtk.STYLE_PROVIDER_PRIORITY_USER
15 | );
16 | }
17 |
18 | // this is the method that will be invoked on `app.run()`
19 | // this is where everything should be initialized and instantiated
20 | public override int command_line(ApplicationCommandLine command_line) {
21 | var argv = command_line.get_arguments();
22 |
23 | if (command_line.is_remote) {
24 | // app is already running we can print to remote
25 | command_line.print_literal("hello from the main instance\n");
26 |
27 | // for example, we could toggle the visibility of the bar
28 | if (argv.length >= 3 && argv[1] == "toggle" && argv[2] == "bar") {
29 | bar.visible = !bar.visible;
30 | }
31 | } else {
32 | // main instance, initialize stuff here
33 | init_css();
34 | add_window((bar = new Bar()));
35 | }
36 |
37 | return 0;
38 | }
39 |
40 | private App() {
41 | application_id = "my.awesome.simple-bar";
42 | flags = ApplicationFlags.HANDLES_COMMAND_LINE;
43 | }
44 |
45 | // entry point of our app
46 | static int main(string[] argv) {
47 | App.instance = new App();
48 | Environment.set_prgname("simple-bar");
49 | return App.instance.run(argv);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lang/gjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "astal",
3 | "version": "0.1.0",
4 | "description": "Building blocks for building linux desktop shell",
5 | "type": "module",
6 | "author": "Aylur",
7 | "license": "LGPL-2.1",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/aylur/astal.git",
11 | "directory": "lang/gjs"
12 | },
13 | "funding": {
14 | "type": "kofi",
15 | "url": "https://ko-fi.com/aylur"
16 | },
17 | "exports": {
18 | ".": "./index.ts",
19 | "./gtk3": "./src/gtk3/index.ts",
20 | "./gtk4": "./src/gtk4/index.ts",
21 | "./gtk3/app": "./src/gtk3/app.ts",
22 | "./gtk4/app": "./src/gtk4/app.ts",
23 | "./gtk3/widget": "./src/gtk3/widget.ts",
24 | "./gtk4/widget": "./src/gtk4/widget.ts",
25 | "./gtk3/jsx-runtime": "./src/gtk3/jsx-runtime.ts",
26 | "./gtk4/jsx-runtime": "./src/gtk4/jsx-runtime.ts",
27 | "./binding": "./src/binding.ts",
28 | "./file": "./src/file.ts",
29 | "./gobject": "./src/gobject.ts",
30 | "./process": "./src/process.ts",
31 | "./time": "./src/time.ts",
32 | "./variable": "./src/variable.ts"
33 | },
34 | "engines": {
35 | "gjs": ">=1.79.0"
36 | },
37 | "os": [
38 | "linux"
39 | ],
40 | "devDependencies": {
41 | "@eslint/js": "^9.12.0",
42 | "@stylistic/eslint-plugin": "^2.9.0",
43 | "@ts-for-gir/cli": "^4.0.0-beta.16",
44 | "@types/eslint__js": "^8.42.3",
45 | "eslint": "^8.57.1",
46 | "typescript": "^5.6.3",
47 | "typescript-eslint": "^7.18.0"
48 | },
49 | "scripts": {
50 | "lint": "eslint . --fix",
51 | "types": "ts-for-gir generate -o @girs --ignoreVersionConflicts"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/battery/src/cli.vala:
--------------------------------------------------------------------------------
1 | static bool help;
2 | static bool version;
3 | static bool monitor;
4 | static bool pretty;
5 |
6 | const OptionEntry[] options = {
7 | { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
8 | { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null },
9 | { "monitor", 'm', OptionFlags.NONE, OptionArg.NONE, ref monitor, null, null },
10 | { "pretty", 'p', OptionFlags.NONE, OptionArg.NONE, ref pretty, null, null },
11 | { null },
12 | };
13 |
14 | int main(string[] argv) {
15 | try {
16 | var opts = new OptionContext();
17 | opts.add_main_entries(options, null);
18 | opts.set_help_enabled(false);
19 | opts.set_ignore_unknown_options(false);
20 | opts.parse(ref argv);
21 | } catch (OptionError err) {
22 | printerr(err.message);
23 | return 1;
24 | }
25 |
26 | if (help) {
27 | print("Usage:\n");
28 | print(" %s [flags]\n\n", argv[0]);
29 | print("Flags:\n");
30 | print(" -h, --help Print this help and exit\n");
31 | print(" -v, --version Print version number and exit\n");
32 | print(" -m, --monitor Monitor property changes\n");
33 | print(" -p, --pretty Pretty print json output\n");
34 | return 0;
35 | }
36 |
37 | if (version) {
38 | print(AstalBattery.VERSION);
39 | return 0;
40 | }
41 |
42 | var battery = AstalBattery.get_default();
43 | print_state(battery);
44 |
45 | if (monitor) {
46 | battery.notify.connect(() => { print_state(battery); });
47 | new GLib.MainLoop(null, false).run();
48 | }
49 |
50 | return 0;
51 | }
52 |
53 | void print_state(AstalBattery.Device battery) {
54 | stdout.printf("%s\n", Json.to_string(Json.gobject_serialize(battery), pretty));
55 | stdout.flush();
56 | }
57 |
--------------------------------------------------------------------------------
/lib/notifd/src/action.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * Notification action.
3 | */
4 | public class AstalNotifd.Action : Object {
5 | /** Id of this action. */
6 | public string id { construct set; get; default = "1"; }
7 |
8 | /** Label of this action that should be displayed to user. */
9 | public string label { construct set; get; default = ""; }
10 |
11 | /** Emitted when the notification this action was added to invoked this action. */
12 | public signal void invoked();
13 |
14 | public Action(string id, string label) {
15 | Object(id: id, label: label);
16 | }
17 |
18 | private Notification _notification;
19 | internal Notification notification {
20 | get { return _notification; }
21 | set {
22 | if (_notification == null) {
23 | _notification = value;
24 | value.invoked.connect((action_id) => {
25 | if (action_id == id) {
26 | invoked();
27 | }
28 | });
29 | }
30 | }
31 | }
32 |
33 | /**
34 | * Invoke this action.
35 | * Note that this method just notifies the client that this action was invoked
36 | * by the user. If for example this notification persists through the lifetime
37 | * of the sending application this action will have no effect.
38 | */
39 | public void invoke() {
40 | if (notification == null) {
41 | critical("cannot invoke action: not added to any notification");
42 | } else {
43 | notification.invoked(id);
44 | }
45 | }
46 |
47 | internal static List new_list(string[] strv) {
48 | var actions = new List();
49 | for (var i = 0; i < strv.length; i += 2) {
50 | actions.append(new Action(strv[i], strv[i + 1]));
51 | }
52 | return actions;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/river/src/meson.build:
--------------------------------------------------------------------------------
1 | srcs = files(
2 | 'river-output.c',
3 | 'river-layout.c',
4 | 'river.c'
5 | )
6 |
7 | deps = [
8 | dependency('gobject-2.0'),
9 | dependency('gio-2.0'),
10 | dependency('wayland-client'),
11 | dependency('json-glib-1.0'),
12 | wayland_glib_dep
13 | ]
14 |
15 | astal_river_lib = library(
16 | 'astal-river',
17 | sources: srcs + client_protocol_srcs,
18 | include_directories: astal_river_inc,
19 | dependencies: deps,
20 | version: meson.project_version(),
21 | install: true,
22 | )
23 |
24 | libastal_river = declare_dependency(link_with: astal_river_lib, include_directories: astal_river_inc)
25 |
26 | executable(
27 | 'astal-river',
28 | files('astal-river.c'),
29 | dependencies: [
30 | dependency('gobject-2.0'),
31 | dependency('gio-2.0'),
32 | dependency('json-glib-1.0'),
33 | libastal_river,
34 | ],
35 | install: true,
36 | )
37 |
38 | pkg_config_name = 'astal-river-' + lib_so_version
39 |
40 | if get_option('introspection')
41 | gir = gnome.generate_gir(
42 | astal_river_lib,
43 | sources: srcs + astal_river_headers,
44 | nsversion: '0.1',
45 | namespace: 'AstalRiver',
46 | symbol_prefix: 'astal_river',
47 | identifier_prefix: 'AstalRiver',
48 | includes: ['GObject-2.0', 'Gio-2.0'],
49 | header: 'astal-river.h',
50 | export_packages: pkg_config_name,
51 | install: true,
52 | )
53 |
54 | if get_option('vapi')
55 | gnome.generate_vapi(
56 | pkg_config_name,
57 | sources: [gir[0]],
58 | packages: ['gobject-2.0', 'gio-2.0'],
59 | install: true,
60 | )
61 | endif
62 | endif
63 |
64 | pkg_config.generate(
65 | name: 'astal-river',
66 | version: meson.project_version(),
67 | libraries: [astal_river_lib],
68 | filebase: pkg_config_name,
69 | subdirs: 'astal',
70 | description: 'astal riverentication module',
71 | url: 'https://github.com/astal-sh/river',
72 | )
73 |
--------------------------------------------------------------------------------
/docs/guide/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Arch
4 |
5 | maintainer: [@kotontrion](https://github.com/kotontrion)
6 |
7 | ```sh [Every Library]
8 | yay -S libastal-meta
9 | ```
10 |
11 | ## Nix
12 |
13 | maintainer: [@Aylur](https://github.com/Aylur)
14 |
15 | Read more about it on the [nix page](./nix#astal)
16 |
17 | ## Building From Source
18 |
19 | Most libraries will require you to follow these three steps to build and install
20 | them. These steps are shown for each library on their individual pages.
21 |
22 | 1. Install the following dependencies
23 |
24 | :::code-group
25 |
26 | ```sh [ Arch]
27 | sudo pacman -Syu \
28 | meson vala valadoc gobject-introspection \
29 | gtk3 gtk-layer-shell \
30 | gtk4 gtk4-layer-shell
31 | ```
32 |
33 | ```sh [ Fedora]
34 | sudo dnf install \
35 | meson vala valadoc gobject-introspection-devel wayland-protocols-devel \
36 | gtk3-devel gtk-layer-shell-devel \
37 | gtk4-devel gtk4-layer-shell-devel
38 | ```
39 |
40 | ```sh [ Ubuntu]
41 | sudo apt install \
42 | meson valac valadoc gobject-introspection libgirepository1.0-dev \
43 | libgtk-3-dev libgtk-layer-shell-dev \
44 | libgtk-4-dev libgtk4-layer-shell-dev
45 | ```
46 |
47 | :::
48 |
49 | 2. Clone the repo
50 |
51 | ```sh
52 | git clone https://github.com/aylur/astal.git
53 | ```
54 |
55 | 3. Build and install with `meson`
56 |
57 | ::: code-group
58 |
59 | ```sh [astal-io]
60 | cd lib/astal/io
61 | meson setup build
62 | meson install -C build
63 | ```
64 |
65 | ```sh [astal3]
66 | cd lib/astal/gtk3
67 | meson setup build
68 | meson install -C build
69 | ```
70 |
71 | ```sh [astal4]
72 | cd lib/astal/gtk4
73 | meson setup build
74 | meson install -C build
75 | ```
76 |
77 | :::
78 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/overlay.vala:
--------------------------------------------------------------------------------
1 | public class Astal.Overlay : Gtk.Overlay {
2 | public bool pass_through { get; set; }
3 |
4 | /**
5 | * First [property@Astal.Overlay:overlays] element.
6 | *
7 | * WARNING: setting this value will remove every overlay but the first.
8 | */
9 | public Gtk.Widget? overlay {
10 | get { return overlays.nth_data(0); }
11 | set {
12 | foreach (var ch in get_children()) {
13 | if (ch != child)
14 | remove(ch);
15 | }
16 |
17 | if (value != null)
18 | add_overlay(value);
19 | }
20 | }
21 |
22 | /**
23 | * Sets the overlays of this Overlay. [method@Gtk.Overlay.add_overlay].
24 | */
25 | public List overlays {
26 | owned get { return get_children(); }
27 | set {
28 | foreach (var ch in get_children()) {
29 | if (ch != child)
30 | remove(ch);
31 | }
32 |
33 | foreach (var ch in value)
34 | add_overlay(ch);
35 | }
36 | }
37 |
38 | public new Gtk.Widget? child {
39 | get { return get_child(); }
40 | set {
41 | var ch = get_child();
42 | if (ch != null)
43 | remove(ch);
44 |
45 | if (value != null)
46 | add(value);
47 | }
48 | }
49 |
50 | construct {
51 | notify["pass-through"].connect(() => {
52 | update_pass_through();
53 | });
54 | }
55 |
56 | private void update_pass_through() {
57 | foreach (var child in get_children())
58 | set_overlay_pass_through(child, pass_through);
59 | }
60 |
61 | public new void add_overlay(Gtk.Widget widget) {
62 | base.add_overlay(widget);
63 | set_overlay_pass_through(widget, pass_through);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/docs/showcases/showcases.ts:
--------------------------------------------------------------------------------
1 | type Showcase = {
2 | image: string
3 | url: string
4 | icon?: string // https://devicon.dev/
5 | title?: string
6 | description?: string
7 | author: string
8 | }
9 |
10 | // TODO: support more layouts
11 | type Grid = T | [T, T]
12 |
13 | export default [
14 | {
15 | image: "/astal/showcase/aylur.webp",
16 | url: "https://github.com/Aylur/dotfiles",
17 | icon: "devicon-typescript-plain",
18 | title: "Marble Shell",
19 | author: "Aylur",
20 | },
21 | {
22 | image: "/astal/showcase/tokyob0t.webp",
23 | url: "https://github.com/tokyob0t/dotfiles",
24 | icon: "devicon-lua-plain",
25 | title: "Tokyob0t's Desktop",
26 | description: "Abandonad toda esperanza, vosotros que entráis aquí.",
27 | author: "tokyob0t",
28 | },
29 | {
30 | image: "/astal/showcase/kotontrion-kompass.webp",
31 | url: "https://github.com/kotontrion/kompass",
32 | icon: "devicon-vala-plain",
33 | title: "kompass",
34 | author: "kotontrion",
35 | },
36 | {
37 | image: "/astal/showcase/ezerinz.webp",
38 | url: "https://github.com/ezerinz/epik-shell",
39 | icon: "devicon-javascript-plain",
40 | title: "Epik Shell",
41 | author: "ezerinz",
42 | },
43 | {
44 | image: "/astal/showcase/hyprpanel_showcase.webp",
45 | url: "https://github.com/Jas-SinghFSU/hyprpanel",
46 | icon: "devicon-javascript-plain",
47 | title: "HyprPanel",
48 | author: "Jas",
49 | },
50 | {
51 | image: "/astal/showcase/retrozinndev-colorshell.webp",
52 | url: "https://github.com/retrozinndev/colorshell",
53 | icon: "devicon-typescript-plain",
54 | title: "colorshell",
55 | author: "retrozinndev",
56 | },
57 | {
58 | image: "/astal/showcase/delta-shell.webp",
59 | url: "https://github.com/Sinomor/delta-shell/",
60 | icon: "devicon-typescript-plain",
61 | title: "Delta Shell",
62 | author: "Sinomor",
63 | },
64 |
65 | // add more showcases here~
66 | ] satisfies Grid[]
67 |
--------------------------------------------------------------------------------
/lib/hyprland/src/workspace.vala:
--------------------------------------------------------------------------------
1 | namespace AstalHyprland {
2 | public class Workspace : Object {
3 | public signal void removed();
4 |
5 | public List _clients = new List();
6 |
7 | public int id { get; private set; }
8 | public string name { get; private set; }
9 | public Monitor monitor { get; private set; }
10 | public List clients { owned get { return _clients.copy(); } }
11 | public bool has_fullscreen { get; private set; }
12 | public Client last_client { get; private set; }
13 |
14 | public Workspace.dummy(int id, Monitor ? monitor) {
15 | this.id = id;
16 | this.name = id.to_string();
17 | this.monitor = monitor;
18 | }
19 |
20 | internal List filter_clients() {
21 | var hyprland = Hyprland.get_default();
22 | var list = new List();
23 | foreach (var client in hyprland.clients) {
24 | if (client.workspace == this) {
25 | list.append(client);
26 | }
27 | }
28 |
29 | return list;
30 | }
31 |
32 | internal void sync(Json.Object obj) {
33 | var hyprland = Hyprland.get_default();
34 |
35 | id = (int)obj.get_int_member("id");
36 | name = obj.get_string_member("name");
37 | has_fullscreen = obj.get_boolean_member("hasfullscreen");
38 |
39 | monitor = hyprland.get_monitor((int)obj.get_int_member("monitorID"));
40 | last_client = hyprland.get_client(obj.get_string_member("lastwindow"));
41 |
42 | var list = filter_clients();
43 | if (_clients.length() != list.length()) {
44 | _clients = list.copy();
45 | notify_property("clients");
46 | }
47 | }
48 |
49 | public void focus() {
50 | Hyprland.get_default().dispatch("workspace", id.to_string());
51 | }
52 |
53 | public void move_to(Monitor m) {
54 | Hyprland.get_default().dispatch("moveworkspacetomonitor", id.to_string() + " " + m.id.to_string());
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/wireplumber/include/astal/wireplumber/node.h:
--------------------------------------------------------------------------------
1 | #ifndef ASTAL_WP_NODE_H
2 | #define ASTAL_WP_NODE_H
3 |
4 | #include
5 |
6 | #include "channel.h"
7 | #include "enums.h"
8 |
9 | G_BEGIN_DECLS
10 |
11 | #define ASTAL_WP_TYPE_NODE (astal_wp_node_get_type())
12 |
13 | G_DECLARE_DERIVABLE_TYPE(AstalWpNode, astal_wp_node, ASTAL_WP, NODE, GObject)
14 |
15 | struct _AstalWpNodeClass {
16 | GObjectClass parent_class;
17 |
18 | void (*params_changed)(AstalWpNode *self, const gchar *id);
19 |
20 | void (*metadata_changed)(AstalWpNode *self, const gchar *key, const gchar *type,
21 | const gchar *value);
22 | };
23 |
24 | void astal_wp_node_set_volume(AstalWpNode *self, gdouble volume);
25 | gdouble astal_wp_node_get_volume(AstalWpNode *self);
26 |
27 | void astal_wp_node_set_mute(AstalWpNode *self, gboolean mute);
28 | gboolean astal_wp_node_get_mute(AstalWpNode *self);
29 |
30 | gboolean astal_wp_node_get_lock_channels(AstalWpNode *self);
31 | void astal_wp_node_set_lock_channels(AstalWpNode *self, gboolean lock_channels);
32 |
33 | AstalWpMediaClass astal_wp_node_get_media_class(AstalWpNode *self);
34 | guint astal_wp_node_get_id(AstalWpNode *self);
35 | const gchar *astal_wp_node_get_description(AstalWpNode *self);
36 | const gchar *astal_wp_node_get_name(AstalWpNode *self);
37 | const gchar *astal_wp_node_get_icon(AstalWpNode *self);
38 | const gchar *astal_wp_node_get_volume_icon(AstalWpNode *self);
39 | gint astal_wp_node_get_serial(AstalWpNode *self);
40 | const gchar *astal_wp_node_get_path(AstalWpNode *self);
41 | AstalWpNodeState astal_wp_node_get_state(AstalWpNode *self);
42 |
43 | GList *astal_wp_node_get_channels(AstalWpNode *self);
44 |
45 | gchar *astal_wp_node_get_pw_property(AstalWpNode *self, const gchar *key);
46 |
47 | void astal_wp_node_metadata_changed(AstalWpNode *self, const gchar *key, const gchar *type,
48 | const gchar *value);
49 | void astal_wp_node_params_changed(AstalWpNode *self, const gchar *id);
50 |
51 | G_END_DECLS
52 |
53 | #endif // !ASTAL_WP_NODE_H
54 |
--------------------------------------------------------------------------------
/docs/guide/libraries/network.md:
--------------------------------------------------------------------------------
1 | # Network
2 |
3 | Wrapper library over [networkmanager](https://networkmanager.dev/) to better
4 | integrate with Astal.
5 |
6 | ## Usage
7 |
8 | You can browse the
9 | [Network reference](https://aylur.github.io/libastal/network).
10 |
11 | ### CLI
12 |
13 | There is no CLI for this library, use the one provided by networkmanager.
14 |
15 | ```sh
16 | nmcli --help
17 | ```
18 |
19 | ### Library
20 |
21 | :::code-group
22 |
23 | ```js [ JavaScript]
24 | import Network from "gi://AstalNetwork"
25 |
26 | const network = Network.get_default()
27 |
28 | print(network.wifi.ssid)
29 | ```
30 |
31 | ```py [ Python]
32 | from gi.repository import AstalNetwork as Network
33 |
34 | network = Network.get_default()
35 |
36 | print(network.get_wifi().get_ssid())
37 | ```
38 |
39 | ```lua [ Lua]
40 | local Network = require("lgi").require("AstalNetwork")
41 |
42 | local network = Network.get_default()
43 |
44 | print(network.wifi.ssid)
45 | ```
46 |
47 | ```vala [ Vala]
48 | var network = AstalNetwork.get_default();
49 |
50 | print(network.wifi.ssid);
51 | ```
52 |
53 | :::
54 |
55 | ## Installation
56 |
57 | 1. install dependencies
58 |
59 | :::code-group
60 |
61 | ```sh [ Arch]
62 | sudo pacman -Syu meson vala valadoc libnm gobject-introspection
63 | ```
64 |
65 | ```sh [ Fedora]
66 | sudo dnf install meson vala valadoc NetworkManager-libnm-devel gobject-introspection-devel
67 | ```
68 |
69 | ```sh [ Ubuntu]
70 | sudo apt install meson valac valadoc libnm-dev gobject-introspection
71 | ```
72 |
73 | :::
74 |
75 | 2. clone repo
76 |
77 | ```sh
78 | git clone https://github.com/aylur/astal.git
79 | cd astal/lib/network
80 | ```
81 |
82 | 3. install
83 |
84 | ```sh
85 | meson setup build
86 | meson install -C build
87 | ```
88 |
--------------------------------------------------------------------------------
/lib/cava/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'astal-cava',
3 | 'c',
4 | version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(),
5 | default_options: ['c_std=gnu11', 'warning_level=3', 'prefix=/usr'],
6 | )
7 |
8 | add_project_arguments(['-Wno-pedantic', '-Wno-unused-parameter'], language: 'c')
9 |
10 | version_split = meson.project_version().split('.')
11 | lib_so_version = version_split[0] + '.' + version_split[1]
12 |
13 | pkg_config = import('pkgconfig')
14 | gnome = import('gnome')
15 |
16 | srcs = files(
17 | 'astal-cava.h',
18 | 'cava.c',
19 | )
20 |
21 | install_headers('astal-cava.h')
22 |
23 | cava = dependency(
24 | 'cava',
25 | version: '>=0.10.6',
26 | required: true,
27 | fallback: ['cava', 'cava_dep'],
28 | )
29 |
30 | deps = [
31 | dependency('gobject-2.0'),
32 | dependency('gio-2.0'),
33 | cava,
34 | ]
35 |
36 | astal_cava_lib = library(
37 | 'astal-cava',
38 | sources: srcs,
39 | dependencies: deps,
40 | version: meson.project_version(),
41 | install: true,
42 | )
43 |
44 | libastal_cava = declare_dependency(link_with: astal_cava_lib)
45 |
46 | pkg_config_name = 'astal-cava-' + lib_so_version
47 |
48 | if get_option('introspection')
49 | gir = gnome.generate_gir(
50 | astal_cava_lib,
51 | sources: srcs,
52 | nsversion: '0.1',
53 | namespace: 'AstalCava',
54 | symbol_prefix: 'astal_cava',
55 | identifier_prefix: 'AstalCava',
56 | includes: ['GObject-2.0', 'Gio-2.0'],
57 | header: 'astal-cava.h',
58 | export_packages: pkg_config_name,
59 | install: true,
60 | )
61 |
62 | if get_option('vapi')
63 | gnome.generate_vapi(
64 | pkg_config_name,
65 | sources: [gir[0]],
66 | packages: ['gobject-2.0', 'gio-2.0'],
67 | install: true,
68 | )
69 | endif
70 | endif
71 |
72 | pkg_config.generate(
73 | name: 'astal-cava',
74 | version: meson.project_version(),
75 | libraries: [astal_cava_lib],
76 | filebase: pkg_config_name,
77 | subdirs: 'astal',
78 | description: 'audio analyzing service using cava',
79 | url: 'https://github.com/Aylur/astal',
80 | )
81 |
--------------------------------------------------------------------------------
/docs/guide/libraries/river.md:
--------------------------------------------------------------------------------
1 | # River
2 |
3 | Library and CLI tool for monitoring the
4 | [River Wayland Compositor](https://isaacfreund.com/software/river/).
5 |
6 | ## Usage
7 |
8 | You can browse the [River reference](https://aylur.github.io/libastal/river).
9 |
10 | ### CLI
11 |
12 | ```sh
13 | astal-river --help
14 | ```
15 |
16 | ### Library
17 |
18 | :::code-group
19 |
20 | ```js [ JavaScript]
21 | import River from "gi://AstalRiver"
22 |
23 | const river = River.get_default()
24 |
25 | for (const output of river.get_outputs()) {
26 | print(output.name)
27 | }
28 | ```
29 |
30 | ```py [ Python]
31 | from gi.repository import AstalRiver as River
32 |
33 | river = River.get_default()
34 |
35 | for output in river.get_outputs():
36 | print(output.get_name())
37 | ```
38 |
39 | ```lua [ Lua]
40 | local River = require("lgi").require("AstalRiver")
41 |
42 | local river = River.River.get_default()
43 |
44 | for _, o in ipairs(river.outputs) do
45 | print(o.name)
46 | end
47 | ```
48 |
49 | ```vala [ Vala]
50 | var river = AstalRiver.get_default();
51 |
52 | foreach (var output in river.get_outputs()) {
53 | print(output.name);
54 | }
55 | ```
56 |
57 | :::
58 |
59 | ## Installation
60 |
61 | 1. install dependencies
62 |
63 | :::code-group
64 |
65 | ```sh [ Arch]
66 | sudo pacman -Syu meson json-glib gobject-introspection
67 | ```
68 |
69 | ```sh [ Fedora]
70 | sudo dnf install meson gcc json-glib-devel gobject-introspection-devel
71 | ```
72 |
73 | ```sh [ Ubuntu]
74 | sudo apt install meson libjson-glib-dev gobject-introspection
75 | ```
76 |
77 | :::
78 |
79 | 2. clone repo
80 |
81 | ```sh
82 | git clone https://github.com/aylur/astal.git
83 | cd astal/lib/river
84 | ```
85 |
86 | 3. install
87 |
88 | ```sh
89 | meson setup build
90 | meson install -C build
91 | ```
92 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/App.ts:
--------------------------------------------------------------------------------
1 | import GObject from "gi://GObject"
2 | import Gio from "gi://Gio"
3 | import GLib from "gi://GLib"
4 | import Gtk from "gi://Gtk?version=4.0"
5 | import Gdk from "gi://Gdk?version=4.0"
6 | import Astal from "gi://Astal?version=4.0"
7 | import Bar from "./bar/Bar"
8 |
9 | export default class App extends Astal.Application {
10 | static {
11 | GObject.registerClass(this)
12 | }
13 |
14 | static instance: App
15 | private bar!: Bar
16 |
17 | constructor() {
18 | super({
19 | applicationId: "my.awesome.simple-bar",
20 | flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
21 | })
22 | }
23 |
24 | private initCss() {
25 | const provider = new Gtk.CssProvider()
26 | provider.load_from_resource("/style.css")
27 |
28 | Gtk.StyleContext.add_provider_for_display(
29 | Gdk.Display.get_default()!,
30 | provider,
31 | Gtk.STYLE_PROVIDER_PRIORITY_USER,
32 | )
33 | }
34 |
35 | vfunc_command_line(command_line: Gio.ApplicationCommandLine): number {
36 | const argv = command_line.get_arguments()
37 |
38 | if (command_line.isRemote) {
39 | // app is already running we can print to remote
40 | command_line.print_literal("hello from the main instance\n")
41 |
42 | // for example, we could toggle the visibility of the bar
43 | if (argv.length >= 2 && argv[0] == "toggle" && argv[1] == "bar") {
44 | this.bar.visible = !this.bar.visible
45 | }
46 |
47 | // exit remote process
48 | command_line.done()
49 | } else {
50 | // main instance, initialize stuff here
51 | this.initCss()
52 | this.bar = new Bar()
53 | this.add_window(this.bar)
54 | }
55 |
56 | return 0
57 | }
58 |
59 | // entry point of our app
60 | static async main(argv: string[]): Promise {
61 | App.instance = new App()
62 | GLib.set_prgname("simple-bar")
63 | return App.instance.runAsync(argv)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/docs/guide/libraries/battery.md:
--------------------------------------------------------------------------------
1 | # Battery
2 |
3 | Library and CLI tool for monitoring [upowerd](https://upower.freedesktop.org/)
4 | devices.
5 |
6 | ## Usage
7 |
8 | You can browse the
9 | [Battery reference](https://aylur.github.io/libastal/battery).
10 |
11 | ### CLI
12 |
13 | ```sh
14 | astal-battery --help
15 | ```
16 |
17 | ### Library
18 |
19 | :::code-group
20 |
21 | ```js [ JavaScript]
22 | import Battery from "gi://AstalBattery"
23 |
24 | const battery = Battery.get_default()
25 |
26 | print(battery.percentage)
27 | ```
28 |
29 | ```py [ Python]
30 | from gi.repository import AstalBattery as Battery
31 |
32 | battery = Battery.get_default()
33 |
34 | print(battery.get_percentage())
35 | ```
36 |
37 | ```lua [ Lua]
38 | local Battery = require("lgi").require("AstalBattery")
39 |
40 | local battery = Battery.get_default()
41 |
42 | print(battery.percentage)
43 | ```
44 |
45 | ```vala [ Vala]
46 | var battery = AstalBattery.get_default();
47 |
48 | print(@"$(battery.percentage)\n");
49 | ```
50 |
51 | :::
52 |
53 | ## Installation
54 |
55 | 1. install dependencies
56 |
57 | :::code-group
58 |
59 | ```sh [ Arch]
60 | sudo pacman -Syu meson vala valadoc json-glib gobject-introspection
61 | ```
62 |
63 | ```sh [ Fedora]
64 | sudo dnf install meson vala valadoc json-glib-devel gobject-introspection-devel
65 | ```
66 |
67 | ```sh [ Ubuntu]
68 | sudo apt install meson valac valadoc libjson-glib-dev gobject-introspection
69 | ```
70 |
71 | :::
72 |
73 | ::: info
74 |
75 | Although UPower is not a direct build dependency, it should be
76 | self-explanatory that the daemon is required to be available at runtime.
77 |
78 | :::
79 |
80 | 2. clone repo
81 |
82 | ```sh
83 | git clone https://github.com/aylur/astal.git
84 | cd astal/lib/battery
85 | ```
86 |
87 | 3. install
88 |
89 | ```sh
90 | meson setup build
91 | meson install -C build
92 | ```
93 |
--------------------------------------------------------------------------------
/lib/hyprland/src/structs.vala:
--------------------------------------------------------------------------------
1 | namespace AstalHyprland {
2 | public class Bind : Object {
3 | public bool locked { get; construct set; }
4 | public bool mouse { get; construct set; }
5 | public bool release { get; construct set; }
6 | public bool repeat { get; construct set; }
7 | public bool long_press { get; construct set; }
8 | public bool non_consuming { get; construct set; }
9 | public bool has_description { get; construct set; }
10 | public int64 modmask { get; construct set; }
11 | public string submap { get; construct set; }
12 | public string key { get; construct set; }
13 | public int64 keycode { get; construct set; }
14 | public bool catch_all { get; construct set; }
15 | public string description { get; construct set; }
16 | public string dispatcher { get; construct set; }
17 | public string arg { get; construct set; }
18 |
19 | internal Bind.from_json(Json.Object obj) {
20 | locked = obj.get_boolean_member("locked");
21 | mouse = obj.get_boolean_member("mouse");
22 | release = obj.get_boolean_member("release");
23 | repeat = obj.get_boolean_member("repeat");
24 | long_press = (obj.get_member("longPress") ? .get_boolean()) ?? false;
25 | non_consuming = obj.get_boolean_member("non_consuming");
26 | has_description = (obj.get_member("has_description") ? .get_boolean()) ?? false;
27 | modmask = obj.get_int_member("modmask");
28 | submap = obj.get_string_member("submap");
29 | key = obj.get_string_member("key");
30 | keycode = obj.get_int_member("keycode");
31 | catch_all = obj.get_boolean_member("catch_all");
32 | description = (obj.get_member("description") ? .get_string()) ?? "";
33 | dispatcher = obj.get_string_member("dispatcher");
34 | arg = obj.get_string_member("arg");
35 | }
36 | }
37 |
38 | public class Position : Object {
39 | public int x { get; construct set; }
40 | public int y { get; construct set; }
41 |
42 | internal Position.cursorpos(string pos) {
43 | var xy = pos.split(",");
44 | x = int.parse(xy[0].strip());
45 | y = int.parse(xy[1].strip());
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/guide/libraries/hyprland.md:
--------------------------------------------------------------------------------
1 | # Hyprland
2 |
3 | Library and CLI tool for monitoring the
4 | [Hyprland socket](https://wiki.hyprland.org/IPC/).
5 |
6 | ## Usage
7 |
8 | You can browse the
9 | [Hyprland reference](https://aylur.github.io/libastal/hyprland).
10 |
11 | ### CLI
12 |
13 | ```sh
14 | astal-hyprland # starts monitoring
15 | ```
16 |
17 | ### Library
18 |
19 | :::code-group
20 |
21 | ```js [ JavaScript]
22 | import Hyprland from "gi://AstalHyprland"
23 |
24 | const hyprland = Hyprland.get_default()
25 |
26 | for (const client of hyprland.get_clients()) {
27 | print(client.title)
28 | }
29 | ```
30 |
31 | ```py [ Python]
32 | from gi.repository import AstalHyprland as Hyprland
33 |
34 | hyprland = Hyprland.get_default()
35 |
36 | for client in hyprland.get_clients():
37 | print(client.get_title())
38 | ```
39 |
40 | ```lua [ Lua]
41 | local Hyprland = require("lgi").require("AstalHyprland")
42 |
43 | local hyprland = Hyprland.get_default()
44 |
45 | for _, c in ipairs(hyprland.clients) do
46 | print(c.title)
47 | end
48 | ```
49 |
50 | ```vala [ Vala]
51 | var hyprland = AstalHyprland.get_default();
52 |
53 | foreach (var client in hyprland.clients) {
54 | print(client.title);
55 | }
56 | ```
57 |
58 | :::
59 |
60 | ## Installation
61 |
62 | 1. install dependencies
63 |
64 | :::code-group
65 |
66 | ```sh [ Arch]
67 | sudo pacman -Syu meson vala valadoc json-glib gobject-introspection
68 | ```
69 |
70 | ```sh [ Fedora]
71 | sudo dnf install meson vala valadoc json-glib-devel gobject-introspection-devel
72 | ```
73 |
74 | ```sh [ Ubuntu]
75 | sudo apt install meson valac valadoc libjson-glib-dev gobject-introspection
76 | ```
77 |
78 | :::
79 |
80 | 2. clone repo
81 |
82 | ```sh
83 | git clone https://github.com/aylur/astal.git
84 | cd astal/lib/hyprland
85 | ```
86 |
87 | 3. install
88 |
89 | ```sh
90 | meson setup build
91 | meson install -C build
92 | ```
93 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/meson.build:
--------------------------------------------------------------------------------
1 | pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
2 | bindir = get_option('prefix') / get_option('bindir')
3 | blp = find_program('blueprint-compiler', required: true)
4 | sass = find_program('sass', required: true)
5 | esbuild = find_program('esbuild', required: true)
6 | gjs = find_program('gjs', required: true)
7 | layer_shell = dependency('gtk4-layer-shell-0')
8 |
9 | blueprint_sources = files(
10 | 'bar/Bar.blp',
11 | )
12 |
13 | # transplie blueprints
14 | ui = custom_target(
15 | 'blueprint',
16 | input: blueprint_sources,
17 | output: '.',
18 | command: [
19 | blp,
20 | 'batch-compile',
21 | '@OUTPUT@',
22 | '@CURRENT_SOURCE_DIR@',
23 | '@INPUT@',
24 | ],
25 | )
26 |
27 | # bundle ts files
28 | js = custom_target(
29 | 'typescript',
30 | input: files('App.ts'),
31 | command: [
32 | esbuild,
33 | '--bundle', '@INPUT@',
34 | '--format=esm',
35 | '--outfile=@OUTPUT@',
36 | '--sourcemap=inline',
37 | '--external:gi://*',
38 | '--external:gettext',
39 | '--external:system',
40 | ],
41 | output: 'index.js',
42 | )
43 |
44 | # bundle scss files
45 | css = custom_target(
46 | 'scss',
47 | input: files('style.scss'),
48 | command: [sass, '@INPUT@', '@OUTPUT@'],
49 | output: 'style.css',
50 | )
51 |
52 | # compiling source files into a binary
53 | import('gnome').compile_resources(
54 | 'data',
55 | files('gresource.xml'),
56 | dependencies: [ui, css, js],
57 | gresource_bundle: true,
58 | install: true,
59 | install_dir: pkgdatadir,
60 | )
61 |
62 | # gresource can't be run with gjs, we still need an entry script
63 | configure_file(
64 | input: files('main.in.js'),
65 | output: 'main.js',
66 | configuration: {
67 | 'GJS': gjs.full_path(),
68 | 'PKGDATADIR': pkgdatadir,
69 | },
70 | install: true,
71 | install_dir: pkgdatadir,
72 | )
73 |
74 | # and we need to wrap the entry script to preload gtk4-layer-shell
75 | configure_file(
76 | input: 'main.in.sh',
77 | output: meson.project_name(),
78 | configuration: {
79 | 'LAYER_SHELL_PREFIX': layer_shell.get_variable('prefix'),
80 | 'INDEX': pkgdatadir / 'main.js',
81 | },
82 | install: true,
83 | install_dir: bindir,
84 | )
85 |
--------------------------------------------------------------------------------
/lang/gjs/src/process.ts:
--------------------------------------------------------------------------------
1 | import Astal from "gi://AstalIO"
2 |
3 | type Args = {
4 | cmd: string | string[]
5 | out?: (stdout: string) => void
6 | err?: (stderr: string) => void
7 | }
8 |
9 | export type Process = Astal.Process
10 | export const Process = Astal.Process
11 |
12 | export function subprocess(args: Args): Astal.Process
13 |
14 | export function subprocess(
15 | cmd: string | string[],
16 | onOut?: (stdout: string) => void,
17 | onErr?: (stderr: string) => void,
18 | ): Astal.Process
19 |
20 | export function subprocess(
21 | argsOrCmd: Args | string | string[],
22 | onOut: (stdout: string) => void = print,
23 | onErr: (stderr: string) => void = printerr,
24 | ) {
25 | const args = Array.isArray(argsOrCmd) || typeof argsOrCmd === "string"
26 | const { cmd, err, out } = {
27 | cmd: args ? argsOrCmd : argsOrCmd.cmd,
28 | err: args ? onErr : argsOrCmd.err || onErr,
29 | out: args ? onOut : argsOrCmd.out || onOut,
30 | }
31 |
32 | const proc = Array.isArray(cmd)
33 | ? Astal.Process.subprocessv(cmd)
34 | : Astal.Process.subprocess(cmd)
35 |
36 | proc.connect("stdout", (_, stdout: string) => out(stdout))
37 | proc.connect("stderr", (_, stderr: string) => err(stderr))
38 | return proc
39 | }
40 |
41 | /** @throws {GLib.Error} Throws stderr */
42 | export function exec(cmd: string | string[]) {
43 | return Array.isArray(cmd)
44 | ? Astal.Process.execv(cmd)
45 | : Astal.Process.exec(cmd)
46 | }
47 |
48 | export function execAsync(cmd: string | string[]): Promise {
49 | return new Promise((resolve, reject) => {
50 | if (Array.isArray(cmd)) {
51 | Astal.Process.exec_asyncv(cmd, (_, res) => {
52 | try {
53 | resolve(Astal.Process.exec_asyncv_finish(res))
54 | } catch (error) {
55 | reject(error)
56 | }
57 | })
58 | } else {
59 | Astal.Process.exec_async(cmd, (_, res) => {
60 | try {
61 | resolve(Astal.Process.exec_finish(res))
62 | } catch (error) {
63 | reject(error)
64 | }
65 | })
66 | }
67 | })
68 | }
69 |
--------------------------------------------------------------------------------
/lang/gjs/src/gtk4/jsx-runtime.ts:
--------------------------------------------------------------------------------
1 | import Gtk from "gi://Gtk?version=4.0"
2 | import { type BindableChild } from "./astalify.js"
3 | import { mergeBindings, jsx as _jsx } from "../_astal.js"
4 | import * as Widget from "./widget.js"
5 |
6 | export function Fragment({ children = [], child }: {
7 | child?: BindableChild
8 | children?: Array
9 | }) {
10 | if (child) children.push(child)
11 | return mergeBindings(children)
12 | }
13 |
14 | export function jsx(
15 | ctor: keyof typeof ctors | typeof Gtk.Widget,
16 | props: any,
17 | ) {
18 | return _jsx(ctors, ctor as any, props)
19 | }
20 |
21 | const ctors = {
22 | box: Widget.Box,
23 | button: Widget.Button,
24 | centerbox: Widget.CenterBox,
25 | // circularprogress: Widget.CircularProgress,
26 | // drawingarea: Widget.DrawingArea,
27 | entry: Widget.Entry,
28 | image: Widget.Image,
29 | label: Widget.Label,
30 | levelbar: Widget.LevelBar,
31 | overlay: Widget.Overlay,
32 | revealer: Widget.Revealer,
33 | slider: Widget.Slider,
34 | stack: Widget.Stack,
35 | switch: Widget.Switch,
36 | window: Widget.Window,
37 | menubutton: Widget.MenuButton,
38 | popover: Widget.Popover,
39 | }
40 |
41 | declare global {
42 | // eslint-disable-next-line @typescript-eslint/no-namespace
43 | namespace JSX {
44 | type Element = Gtk.Widget
45 | type ElementClass = Gtk.Widget
46 | interface IntrinsicElements {
47 | box: Widget.BoxProps
48 | button: Widget.ButtonProps
49 | centerbox: Widget.CenterBoxProps
50 | // circularprogress: Widget.CircularProgressProps
51 | // drawingarea: Widget.DrawingAreaProps
52 | entry: Widget.EntryProps
53 | image: Widget.ImageProps
54 | label: Widget.LabelProps
55 | levelbar: Widget.LevelBarProps
56 | overlay: Widget.OverlayProps
57 | revealer: Widget.RevealerProps
58 | slider: Widget.SliderProps
59 | stack: Widget.StackProps
60 | switch: Widget.SwitchProps
61 | window: Widget.WindowProps
62 | menubutton: Widget.MenuButtonProps
63 | popover: Widget.PopoverProps
64 | }
65 | }
66 | }
67 |
68 | export const jsxs = jsx
69 |
--------------------------------------------------------------------------------
/lib/apps/src/cli.vala:
--------------------------------------------------------------------------------
1 | static bool help;
2 | static bool version;
3 | static string search;
4 | static string launch;
5 | static bool json;
6 |
7 | const OptionEntry[] options = {
8 | { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
9 | { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null },
10 | { "search", 's', OptionFlags.NONE, OptionArg.STRING, ref search, null, null },
11 | { "launch", 'l', OptionFlags.NONE, OptionArg.STRING, ref launch, null, null },
12 | { "json", 'j', OptionFlags.NONE, OptionArg.NONE, ref json, null, null },
13 | { null },
14 | };
15 |
16 | int main(string[] argv) {
17 | try {
18 | var opts = new OptionContext();
19 | opts.add_main_entries(options, null);
20 | opts.set_help_enabled(false);
21 | opts.set_ignore_unknown_options(false);
22 | opts.parse(ref argv);
23 | } catch (OptionError err) {
24 | printerr(err.message);
25 | return 1;
26 | }
27 |
28 | if (help) {
29 | print("Usage:\n");
30 | print(" %s [flags]\n\n", argv[0]);
31 | print("Flags:\n");
32 | print(" -h, --help Print this help and exit\n");
33 | print(" -v, --version Print version number and exit\n");
34 | print(" -s, --search Sort by a search term\n");
35 | print(" -l, --launch Launch an application\n");
36 | print(" -j, --json Print list in json format\n");
37 | return 0;
38 | }
39 |
40 | if (version) {
41 | print(AstalApps.VERSION);
42 | return 0;
43 | }
44 |
45 | var apps = new AstalApps.Apps();
46 |
47 | if (launch != null) {
48 | apps.query(launch).first().data.launch();
49 | return 0;
50 | }
51 |
52 | if (json) {
53 | var b = new Json.Builder().begin_array();
54 | foreach (var app in apps.query(search)) {
55 | b.add_value(app.to_json());
56 | }
57 |
58 | var generator = new Json.Generator();
59 | generator.set_root(b.end_array().get_root());
60 | stdout.printf(generator.to_data(null));
61 | } else {
62 | foreach (var app in apps.query(search)) {
63 | stdout.printf("%s\n", app.entry);
64 | }
65 | }
66 |
67 | return 0;
68 | }
69 |
--------------------------------------------------------------------------------
/lang/lua/astal/process.lua:
--------------------------------------------------------------------------------
1 | local lgi = require("lgi")
2 | local Astal = lgi.require("AstalIO", "0.1")
3 |
4 | local M = {}
5 |
6 | M.Process = Astal.Process
7 |
8 | ---@param commandline string | string[]
9 | ---@param on_stdout? fun(out: string): nil
10 | ---@param on_stderr? fun(err: string): nil
11 | ---@return { kill: function } | nil proc
12 | function M.subprocess(commandline, on_stdout, on_stderr)
13 | on_stdout = on_stdout or function(out)
14 | io.stdout:write(tostring(out) .. "\n")
15 | end
16 |
17 | on_stderr = on_stderr or function(err)
18 | io.stderr:write(tostring(err) .. "\n")
19 | end
20 |
21 |
22 | local proc, err
23 |
24 | if type(commandline) == "table" then
25 | proc, err = Astal.Process.subprocessv(commandline)
26 | else
27 | proc, err = Astal.Process.subprocess(commandline)
28 | end
29 |
30 | if err ~= nil then
31 | err(err)
32 | return nil
33 | end
34 | proc.on_stdout = function(_, stdout)
35 | on_stdout(stdout)
36 | end
37 | proc.on_stderr = function(_, stderr)
38 | on_stderr(stderr)
39 | end
40 | return proc
41 | end
42 |
43 | ---@param commandline string | string[]
44 | ---@return string, string
45 | function M.exec(commandline)
46 | if type(commandline) == "table" then
47 | return Astal.Process.execv(commandline)
48 | else
49 | return Astal.Process.exec(commandline)
50 | end
51 | end
52 |
53 | ---@param commandline string | string[]
54 | ---@param callback? fun(out: string, err: string): nil
55 | function M.exec_async(commandline, callback)
56 | callback = callback or function(out, err)
57 | if err ~= nil then
58 | io.stdout:write(tostring(out) .. "\n")
59 | else
60 | io.stderr:write(tostring(err) .. "\n")
61 | end
62 | end
63 |
64 | if type(commandline) == "table" then
65 | Astal.Process.exec_asyncv(commandline, function(_, res)
66 | local out, err = Astal.Process.exec_asyncv_finish(res)
67 | callback(out, err)
68 | end)
69 | else
70 | Astal.Process.exec_async(commandline, function(_, res)
71 | local out, err = Astal.Process.exec_finish(res)
72 | callback(out, err)
73 | end)
74 | end
75 | end
76 |
77 | return M
78 |
--------------------------------------------------------------------------------
/lib/battery/src/ifaces.vala:
--------------------------------------------------------------------------------
1 | [DBus(name = "org.freedesktop.UPower")]
2 | private interface AstalBattery.IUPower : DBusProxy {
3 | public abstract ObjectPath[] enumerate_devices() throws Error;
4 | public abstract ObjectPath get_display_device() throws Error;
5 | public abstract string get_critical_action() throws Error;
6 |
7 | public signal void device_added(ObjectPath object_path);
8 | public signal void device_removed(ObjectPath object_path);
9 |
10 | public abstract string daemon_version { owned get; }
11 | public abstract bool on_battery { get; }
12 | public abstract bool lid_is_closed { get; }
13 | public abstract bool lid_is_present { get; }
14 | }
15 |
16 | [DBus(name = "org.freedesktop.UPower.Device")]
17 | private interface AstalBattery.IUPowerDevice : DBusProxy {
18 | public abstract uint Type { get; }
19 | public abstract string native_path { owned get; }
20 | public abstract string vendor { owned get; }
21 | public abstract string model { owned get; }
22 | public abstract string serial { owned get; }
23 | public abstract uint64 update_time { get; }
24 | public abstract bool power_supply { get; }
25 | public abstract bool has_history { get; }
26 | public abstract bool has_statistics { get; }
27 | public abstract bool online { get; }
28 | public abstract double energy { get; }
29 | public abstract double energy_empty { get; }
30 | public abstract double energy_full { get; }
31 | public abstract double energy_full_design { get; }
32 | public abstract double energy_rate { get; }
33 | public abstract double voltage { get; }
34 | public abstract int32 charge_cycles { get; }
35 | public abstract double luminosity { get; }
36 | public abstract int64 time_to_empty { get; }
37 | public abstract int64 time_to_full { get; }
38 | public abstract double percentage { get; }
39 | public abstract double temperature { get; }
40 | public abstract bool is_present { get; }
41 | public abstract uint state { get; }
42 | public abstract bool is_rechargable { get; }
43 | public abstract double capacity { get; }
44 | public abstract uint technology { get; }
45 | public abstract uint32 warning_level { get; }
46 | public abstract uint32 battery_level { get; }
47 | public abstract string icon_name { owned get; }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/guide/libraries/powerprofiles.md:
--------------------------------------------------------------------------------
1 | # Power Profiles
2 |
3 | Library and CLI tool for monitoring [upowerd](https://upower.freedesktop.org/)
4 | powerprofiles.
5 |
6 | ## Usage
7 |
8 | You can browse the
9 | [PowerProfiles reference](https://aylur.github.io/libastal/powerprofiles).
10 |
11 | ### CLI
12 |
13 | ```sh
14 | astal-power-profiles --help
15 | ```
16 |
17 | ### Library
18 |
19 | :::code-group
20 |
21 | ```js [ JavaScript]
22 | import PowerProfiles from "gi://AstalPowerProfiles"
23 |
24 | const powerprofiles = PowerProfiles.get_default()
25 |
26 | print(powerprofiles.activeProfile)
27 | ```
28 |
29 | ```py [ Python]
30 | from gi.repository import AstalPowerProfiles as PowerProfiles
31 |
32 | powerprofiles = PowerProfiles.get_default()
33 |
34 | print(powerprofiles.get_active_profile())
35 | ```
36 |
37 | ```lua [ Lua]
38 | local PowerProfiles = require("lgi").require("AstalPowerProfiles")
39 |
40 | local powerprofiles = PowerProfiles.get_default()
41 |
42 | print(powerprofiles.active_profile)
43 | ```
44 |
45 | ```vala [ Vala]
46 | var powerprofiles = AstalPowerProfiles.get_default();
47 |
48 | print(powerprofiles.activeProfile);
49 | ```
50 |
51 | :::
52 |
53 | ## Installation
54 |
55 | 1. install dependencies
56 |
57 | :::code-group
58 |
59 | ```sh [ Arch]
60 | sudo pacman -Syu meson vala valadoc json-glib gobject-introspection
61 | ```
62 |
63 | ```sh [ Fedora]
64 | sudo dnf install meson vala valadoc json-glib-devel gobject-introspection-devel
65 | ```
66 |
67 | ```sh [ Ubuntu]
68 | sudo apt install meson valac valadoc libjson-glib-dev gobject-introspection
69 | ```
70 |
71 | :::
72 |
73 | ::: info
74 |
75 | Although UPower is not a direct build dependency, it should be
76 | self-explanatory that the daemon is required to be available at runtime.
77 |
78 | :::
79 |
80 | 2. clone repo
81 |
82 | ```sh
83 | git clone https://github.com/aylur/astal.git
84 | cd astal/lib/powerprofiles
85 | ```
86 |
87 | 3. install
88 |
89 | ```sh
90 | meson setup build
91 | meson install -C build
92 | ```
93 |
--------------------------------------------------------------------------------
/docs/guide/libraries/cava.md:
--------------------------------------------------------------------------------
1 | # Cava
2 |
3 | Audio visualizer using [cava](https://github.com/karlstav/cava).
4 |
5 | ## Usage
6 |
7 | You can browse the [Cava reference](https://aylur.github.io/libastal/cava).
8 |
9 | ### CLI
10 |
11 | There is no CLI for this library, use the one provided by cava.
12 |
13 | ```sh
14 | cava
15 | ```
16 |
17 | ### Library
18 |
19 | :::code-group
20 |
21 | ```js [ JavaScript]
22 | import Cava from "gi://AstalCava"
23 |
24 | const cava = Cava.get_default()
25 |
26 | cava.connect("notify::values", () => {
27 | print(cava.get_values())
28 | })
29 | ```
30 |
31 | ```py [ Python]
32 | from gi.repository import AstalCava as Cava
33 |
34 | cava = Cava.get_default()
35 |
36 | def callback(self, pspec):
37 | print(cava.get_values())
38 |
39 | cava.connect("notify::values", callback)
40 | ```
41 |
42 | ```lua [ Lua]
43 | local Cava = require("lgi").require("AstalCava")
44 |
45 | local cava = Cava.get_default()
46 |
47 | cava.on_notify.values = function()
48 | print(cava.values)
49 | end
50 | ```
51 |
52 | ```vala [ Vala]
53 | var cava = AstalCava.get_default();
54 |
55 | cava.notify["values"].connect(() => {
56 | foreach (var value in cava.values) {
57 | print(value);
58 | }
59 | });
60 | ```
61 |
62 | :::
63 |
64 | ## Installation
65 |
66 | 1. install dependencies
67 |
68 | Note that it requires [libcava](https://github.com/LukashonakV/cava), a fork
69 | of cava, which provides cava as a shared library.
70 |
71 | :::code-group
72 |
73 | ```sh [ Arch]
74 | sudo pacman -Syu meson vala gobject-introspection
75 | paru -S libcava
76 | ```
77 |
78 | ```sh [ Fedora]
79 | # Not yet documented
80 | ```
81 |
82 | ```sh [ Ubuntu]
83 | # Not yet documented
84 | ```
85 |
86 | :::
87 |
88 | 2. clone repo
89 |
90 | ```sh
91 | git clone https://github.com/aylur/astal.git
92 | cd astal/lib/cava
93 | ```
94 |
95 | 3. install
96 |
97 | ```sh
98 | meson setup build
99 | meson install -C build
100 | ```
101 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/js/src/bar/Bar.blp:
--------------------------------------------------------------------------------
1 | using Gtk 4.0;
2 | using Astal 4.0;
3 |
4 | template $Bar: Astal.Window {
5 | CenterBox centerbox {
6 | start-widget: Box {
7 | MenuButton {
8 | Label {
9 | label: bind template.clock;
10 | }
11 |
12 | popover: Popover popover {
13 | Calendar calendar {
14 | show-day-names: true;
15 | show-heading: true;
16 | show-week-numbers: true;
17 | }
18 | };
19 | }
20 | };
21 |
22 | center-widget: Box {
23 | Box {
24 | visible: bind template.mpris-visible;
25 |
26 | Image {
27 | file: bind template.mpris-art;
28 | }
29 |
30 | Label {
31 | label: bind template.mpris-label;
32 | }
33 | }
34 | };
35 |
36 | end-widget: Box {
37 | spacing: 4;
38 |
39 | Image {
40 | visible: bind template.bluetooth-visible;
41 | icon-name: "bluetooth-symbolic";
42 | }
43 |
44 | Image {
45 | icon-name: bind template.power-profile-icon;
46 | }
47 |
48 | Image {
49 | icon-name: bind template.network-icon;
50 | }
51 |
52 | Box {
53 | Image {
54 | icon-name: bind template.volume-icon;
55 | }
56 |
57 | Scale {
58 | width-request: 100;
59 | change-value => $change_volume();
60 |
61 | adjustment: Adjustment {
62 | value: bind template.volume;
63 | lower: 0;
64 | upper: 1;
65 | };
66 | }
67 | }
68 |
69 | Box {
70 | Image {
71 | icon-name: bind template.battery-icon;
72 | }
73 |
74 | Label {
75 | label: bind template.battery-label;
76 | }
77 | }
78 |
79 | Box traybox {
80 | spacing: 4;
81 | }
82 | };
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/py/src/bar/Bar.blp:
--------------------------------------------------------------------------------
1 | using Gtk 4.0;
2 | using Astal 4.0;
3 |
4 | template $Bar: Astal.Window {
5 | CenterBox centerbox {
6 | start-widget: Box {
7 | MenuButton {
8 | Label {
9 | label: bind template.clock;
10 | }
11 |
12 | popover: Popover popover {
13 | Calendar calendar {
14 | show-day-names: true;
15 | show-heading: true;
16 | show-week-numbers: true;
17 | }
18 | };
19 | }
20 | };
21 |
22 | center-widget: Box {
23 | Box {
24 | visible: bind template.mpris-visible;
25 |
26 | Image {
27 | file: bind template.mpris-art;
28 | }
29 |
30 | Label {
31 | label: bind template.mpris-label;
32 | }
33 | }
34 | };
35 |
36 | end-widget: Box {
37 | spacing: 4;
38 |
39 | Image {
40 | visible: bind template.bluetooth-visible;
41 | icon-name: "bluetooth-symbolic";
42 | }
43 |
44 | Image {
45 | icon-name: bind template.power-profile-icon;
46 | }
47 |
48 | Image {
49 | icon-name: bind template.network-icon;
50 | }
51 |
52 | Box {
53 | Image {
54 | icon-name: bind template.volume-icon;
55 | }
56 |
57 | Scale {
58 | width-request: 100;
59 | change-value => $change_volume();
60 |
61 | adjustment: Adjustment {
62 | value: bind template.volume;
63 | lower: 0;
64 | upper: 1;
65 | };
66 | }
67 | }
68 |
69 | Box {
70 | Image {
71 | icon-name: bind template.battery-icon;
72 | }
73 |
74 | Label {
75 | label: bind template.battery-label;
76 | }
77 | }
78 |
79 | Box traybox {
80 | spacing: 4;
81 | }
82 | };
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/examples/gtk4/simple-bar/vala/src/bar/Bar.blp:
--------------------------------------------------------------------------------
1 | using Gtk 4.0;
2 | using Astal 4.0;
3 |
4 | template $Bar: Astal.Window {
5 | CenterBox centerbox {
6 | start-widget: Box {
7 | MenuButton {
8 | Label {
9 | label: bind template.clock;
10 | }
11 |
12 | popover: Popover popover {
13 | Calendar calendar {
14 | show-day-names: true;
15 | show-heading: true;
16 | show-week-numbers: true;
17 | }
18 | };
19 | }
20 | };
21 |
22 | center-widget: Box {
23 | Box {
24 | visible: bind template.mpris-visible;
25 |
26 | Image {
27 | file: bind template.mpris-art;
28 | }
29 |
30 | Label {
31 | label: bind template.mpris-label;
32 | }
33 | }
34 | };
35 |
36 | end-widget: Box {
37 | spacing: 4;
38 |
39 | Image {
40 | visible: bind template.bluetooth-visible;
41 | icon-name: "bluetooth-symbolic";
42 | }
43 |
44 | Image {
45 | icon-name: bind template.power-profile-icon;
46 | }
47 |
48 | Image {
49 | icon-name: bind template.network-icon;
50 | }
51 |
52 | Box {
53 | Image {
54 | icon-name: bind template.volume-icon;
55 | }
56 |
57 | Scale {
58 | width-request: 100;
59 | change-value => $change_volume();
60 |
61 | adjustment: Adjustment {
62 | value: bind template.volume;
63 | lower: 0;
64 | upper: 1;
65 | };
66 | }
67 | }
68 |
69 | Box {
70 | Image {
71 | icon-name: bind template.battery-icon;
72 | }
73 |
74 | Label {
75 | label: bind template.battery-label;
76 | }
77 | }
78 |
79 | Box traybox {
80 | spacing: 4;
81 | }
82 | };
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/examples/gtk3/simple-bar/py/app.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import gi
3 |
4 | gi.require_version("Astal", "3.0")
5 | gi.require_version("Gtk", "3.0")
6 | gi.require_version("Gdk", "3.0")
7 | gi.require_version("Gio", "2.0")
8 | gi.require_version("GObject", "2.0")
9 |
10 | gi.require_version("AstalBattery", "0.1")
11 | gi.require_version("AstalWp", "0.1")
12 | gi.require_version("AstalNetwork", "0.1")
13 | gi.require_version("AstalTray", "0.1")
14 | gi.require_version("AstalMpris", "0.1")
15 | gi.require_version("AstalHyprland", "0.1")
16 |
17 | import sys
18 | import subprocess
19 | from gi.repository import Gtk, Gdk, Gio, GLib
20 | from widget.Bar import Bar
21 | from pathlib import Path
22 |
23 | scss = str(Path(__file__).parent.resolve() / "style.scss")
24 | css = "/tmp/style.css"
25 |
26 |
27 | class App(Gtk.Application):
28 | __gtype_name__ = "App"
29 |
30 | def _init_css(self):
31 | subprocess.run(["sass", scss, css])
32 | provider = Gtk.CssProvider()
33 | provider.load_from_path(css)
34 |
35 | Gtk.StyleContext.add_provider_for_screen(
36 | Gdk.Screen.get_default(),
37 | provider,
38 | Gtk.STYLE_PROVIDER_PRIORITY_USER,
39 | )
40 |
41 | # this is the method that will be invoked on `app.run()`
42 | # this is where everything should be initialized and instantiated
43 | def do_command_line(self, command_line):
44 | argv = command_line.get_arguments()
45 |
46 | if command_line.get_is_remote():
47 | # app is already running we can print to remote
48 | command_line.print_literal("hello from the main instance\n")
49 |
50 | # or for example, we could toggle the visibility of the bar
51 | if len(argv) >= 3 and argv[1] == "toggle" and argv[2] == "bar":
52 | self.bar.set_visible(not self.bar.get_visible())
53 | else:
54 | # main instance, initialize stuff here
55 | self._init_css()
56 | self.bar = Bar()
57 | self.add_window(self.bar)
58 |
59 | return 0
60 |
61 | def __init__(self) -> None:
62 | super().__init__(
63 | application_id="my.awesome.simple-bar",
64 | flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
65 | )
66 |
67 |
68 | if __name__ == "__main__":
69 | app = App()
70 | GLib.set_prgname("simple-bar")
71 | app.run(sys.argv)
72 |
--------------------------------------------------------------------------------
/lib/astal/gtk3/src/widget/centerbox.vala:
--------------------------------------------------------------------------------
1 | public class Astal.CenterBox : Gtk.Box, Gtk.Buildable {
2 | /**
3 | * Corresponds to [property@Gtk.Orientable :orientation].
4 | */
5 | [CCode (notify = false)]
6 | public bool vertical {
7 | get { return orientation == Gtk.Orientation.VERTICAL; }
8 | set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; }
9 | }
10 |
11 | construct {
12 | notify["orientation"].connect(() => {
13 | notify_property("vertical");
14 | });
15 | }
16 |
17 | static construct {
18 | set_css_name("centerbox");
19 | }
20 |
21 | private Gtk.Widget _start_widget;
22 | public Gtk.Widget start_widget {
23 | get { return _start_widget; }
24 | set {
25 | if (_start_widget != null)
26 | remove(_start_widget);
27 |
28 | if (value != null)
29 | pack_start(value, true, true, 0);
30 | }
31 | }
32 |
33 | private Gtk.Widget _end_widget;
34 | public Gtk.Widget end_widget {
35 | get { return _end_widget; }
36 | set {
37 | if (_end_widget != null)
38 | remove(_end_widget);
39 |
40 | if (value != null)
41 | pack_end(value, true, true, 0);
42 | }
43 | }
44 |
45 | public Gtk.Widget center_widget {
46 | get { return get_center_widget(); }
47 | set {
48 | if (center_widget != null)
49 | remove(center_widget);
50 |
51 | if (value != null)
52 | set_center_widget(value);
53 | }
54 | }
55 |
56 | void add_child(Gtk.Builder builder, Object child, string? type) {
57 | if (child is Gtk.Widget) {
58 | switch (type) {
59 | case "start":
60 | start_widget = child as Gtk.Widget;
61 | break;
62 | case "center":
63 | center_widget = child as Gtk.Widget;
64 | break;
65 | case "end":
66 | end_widget = child as Gtk.Widget;
67 | break;
68 | default:
69 | base.add_child(builder, child, type);
70 | break;
71 | }
72 | } else {
73 | base.add_child(builder, child, type);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/bluetooth/src/interfaces.vala:
--------------------------------------------------------------------------------
1 | [DBus(name = "org.bluez.Adapter1")]
2 | private interface AstalBluetooth.IAdapter : DBusProxy {
3 | public abstract void remove_device(ObjectPath device) throws Error;
4 | public abstract void start_discovery() throws Error;
5 | public abstract void stop_discovery() throws Error;
6 |
7 | public abstract string[] uuids { owned get; }
8 | public abstract bool discoverable { get; set; }
9 | public abstract bool discovering { get; }
10 | public abstract bool pairable { get; set; }
11 | public abstract bool powered { get; set; }
12 | public abstract string address { owned get; }
13 | public abstract string alias { owned get; set; }
14 | public abstract string modalias { owned get; }
15 | public abstract string name { owned get; }
16 | public abstract uint class { get; }
17 | public abstract uint discoverable_timeout { get; set; }
18 | public abstract uint pairable_timeout { get; set; }
19 | }
20 |
21 | [DBus(name = "org.bluez.Device1")]
22 | private interface AstalBluetooth.IDevice : DBusProxy {
23 | public abstract void cancel_pairing() throws Error;
24 | public abstract async void connect() throws Error;
25 | public abstract void connect_profile(string uuid) throws Error;
26 | public abstract async void disconnect() throws Error;
27 | public abstract void disconnect_profile(string uuid) throws Error;
28 | public abstract void pair() throws Error;
29 |
30 | public abstract string[] uuids { owned get; }
31 | public abstract bool blocked { get; set; }
32 | public abstract bool connected { get; }
33 | public abstract bool legacy_pairing { get; }
34 | public abstract bool paired { get; }
35 | public abstract bool trusted { get; set; }
36 | public abstract int16 rssi { get; }
37 | public abstract ObjectPath adapter { owned get; }
38 | public abstract string address { owned get; }
39 | public abstract string alias { owned get; set; }
40 | public abstract string icon { owned get; }
41 | public abstract string modalias { owned get; }
42 | public abstract string name { owned get; }
43 | public abstract uint16 appearance { get; }
44 | public abstract uint32 class { get; }
45 | }
46 |
47 | [DBus(name = "org.bluez.Battery1")]
48 | private interface AstalBluetooth.IBattery : DBusProxy {
49 | public abstract uint8 percentage { get; }
50 | public abstract string source { owned get; }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/greet/src/cli.vala:
--------------------------------------------------------------------------------
1 | static bool help;
2 | static bool version;
3 | static string username;
4 | static string password;
5 | static string cmd;
6 | [CCode(array_length = false, array_null_terminated = true)]
7 | static string[] env;
8 |
9 | const OptionEntry[] options = {
10 | { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref version, null, null },
11 | { "help", 'h', OptionFlags.NONE, OptionArg.NONE, ref help, null, null },
12 | { "username", 'u', OptionFlags.NONE, OptionArg.STRING, ref username, null, null },
13 | { "password", 'p', OptionFlags.NONE, OptionArg.STRING, ref password, null, null },
14 | { "cmd", 'c', OptionFlags.NONE, OptionArg.STRING, ref cmd, null, null },
15 | { "env", 'e', OptionFlags.NONE, OptionArg.STRING_ARRAY, ref env, null, null },
16 | { null },
17 | };
18 |
19 | async int main(string[] argv) {
20 | try {
21 | var opts = new OptionContext();
22 | opts.add_main_entries(options, null);
23 | opts.set_help_enabled(false);
24 | opts.set_ignore_unknown_options(false);
25 | opts.parse(ref argv);
26 | } catch (OptionError err) {
27 | printerr(err.message);
28 | return 1;
29 | }
30 |
31 | if (help) {
32 | print("Usage:\n");
33 | print(" %s [flags]\n\n", argv[0]);
34 | print("Flags:\n");
35 | print(" -h, --help Print this help and exit\n");
36 | print(" -v, --version Print version number and exit\n");
37 | print(" -u, --username User to login to\n");
38 | print(" -p, --password Password of the user\n");
39 | print(" -c, --cmd Command to start the session with\n");
40 | print(" -e, --env Additional env vars to set for the session\n");
41 | return 0;
42 | }
43 |
44 | if (version) {
45 | printerr(AstalGreet.VERSION);
46 | return 0;
47 | }
48 |
49 | if (username == null) {
50 | printerr("missing username\n");
51 | return 1;
52 | }
53 |
54 | if (password == null) {
55 | printerr("missing password\n");
56 | return 1;
57 | }
58 |
59 | if (cmd == null) {
60 | printerr("missing cmd\n");
61 | return 1;
62 | }
63 |
64 | try {
65 | yield AstalGreet.login_with_env(username, password, cmd, env);
66 | } catch (Error err) {
67 | printerr(err.message);
68 | return 1;
69 | }
70 |
71 | return 0;
72 | }
73 |
--------------------------------------------------------------------------------
/lib/wireplumber/src/meson.build:
--------------------------------------------------------------------------------
1 | srcs = files(
2 | 'audio.c',
3 | 'device.c',
4 | 'node.c',
5 | 'endpoint.c',
6 | 'stream.c',
7 | 'profile.c',
8 | 'route.c',
9 | 'video.c',
10 | 'wireplumber.c',
11 | 'channel.c',
12 | 'enums.c'
13 | )
14 |
15 | deps = [
16 | dependency('gobject-2.0'),
17 | dependency('gio-2.0'),
18 | dependency('wireplumber-0.5'),
19 | ]
20 |
21 | enums = gnome.mkenums_simple(
22 | 'astal-wp-enum-types',
23 | identifier_prefix: 'AstalWp',
24 | symbol_prefix: 'astal_wp',
25 | sources: astal_wireplumber_subheaders,
26 | install_header: true,
27 | install_dir: join_paths(get_option('includedir'), 'astal/wireplumber')
28 | )
29 |
30 | astal_wireplumber_lib = library(
31 | 'astal-wireplumber',
32 | sources: srcs + enums,
33 | include_directories: astal_wireplumber_inc,
34 | dependencies: deps,
35 | version: meson.project_version(),
36 | install: true,
37 | )
38 |
39 | libastal_wireplumber = declare_dependency(link_with: astal_wireplumber_lib, include_directories: astal_wireplumber_inc)
40 |
41 | # astal_wireplumber_executable = executable(
42 | # 'astal-wireplumber',
43 | # files('astal-wireplumber.c'),
44 | # dependencies : [
45 | # dependency('gobject-2.0'),
46 | # dependency('gio-2.0'),
47 | # dependency('json-glib-1.0'),
48 | # libastal_wireplumber
49 | # ],
50 | # install : true)
51 |
52 | pkg_config_name = 'astal-wireplumber-' + lib_so_version
53 |
54 | if get_option('introspection')
55 | gir = gnome.generate_gir(
56 | astal_wireplumber_lib,
57 | sources: srcs + astal_wireplumber_headers + astal_wireplumber_subheaders + enums[1],
58 | nsversion: '0.1',
59 | namespace: 'AstalWp',
60 | symbol_prefix: 'astal_wp',
61 | identifier_prefix: 'AstalWp',
62 | includes: ['GObject-2.0', 'Gio-2.0'],
63 | header: 'astal-wp.h',
64 | export_packages: pkg_config_name,
65 | install: true,
66 | )
67 |
68 | if get_option('vapi')
69 | gnome.generate_vapi(
70 | pkg_config_name,
71 | sources: [gir[0]],
72 | packages: ['gobject-2.0', 'gio-2.0'],
73 | install: true,
74 | )
75 | endif
76 | endif
77 |
78 | pkg_config.generate(
79 | name: 'astal-wireplumber',
80 | version: meson.project_version(),
81 | libraries: [astal_wireplumber_lib],
82 | filebase: pkg_config_name,
83 | subdirs: 'astal',
84 | description: 'astal wireplumber module',
85 | url: 'https://github.com/astal-sh/wireplumber',
86 | )
87 |
--------------------------------------------------------------------------------
/docs/guide/libraries/tray.md:
--------------------------------------------------------------------------------
1 | # Tray
2 |
3 | Library for managing the systemtray by implementing the
4 | [StatusNotifierItem](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/)
5 | protocol.
6 |
7 | ## Usage
8 |
9 | You can browse the [Tray reference](https://aylur.github.io/libastal/tray).
10 |
11 | ### CLI
12 |
13 | ```sh
14 | astal-tray --help
15 | ```
16 |
17 | ### Library
18 |
19 | :::code-group
20 |
21 | ```js [ JavaScript]
22 | import Tray from "gi://AstalTray"
23 |
24 | const tray = Tray.get_default()
25 |
26 | for (const item of tray.get_items()) {
27 | print(item.title)
28 | }
29 | ```
30 |
31 | ```py [ Python]
32 | from gi.repository import AstalTray as Tray
33 |
34 | tray = Tray.get_default()
35 |
36 | for item in tray.get_items():
37 | print(item.title)
38 | ```
39 |
40 | ```lua [ Lua]
41 | local Tray = require("lgi").require("AstalTray")
42 |
43 | local tray = Tray.get_default()
44 |
45 | for _, i in ipairs(tray.items) do
46 | print(i.title)
47 | end
48 | ```
49 |
50 | ```vala [ Vala]
51 | var tray = AstalTray.get_default();
52 |
53 | foreach (var item in tray.get_items()) {
54 | print(item.title);
55 | }
56 | ```
57 |
58 | :::
59 |
60 | ## Installation
61 |
62 | 1. install dependencies
63 |
64 | :::code-group
65 |
66 | ```sh [ Arch]
67 | sudo pacman -Syu meson json-glib gobject-introspection
68 | ```
69 |
70 | ```sh [ Fedora]
71 | sudo dnf install meson json-glib-devel gobject-introspection-devel
72 | ```
73 |
74 | ```sh [ Ubuntu]
75 | sudo apt install meson libjson-glib-dev gobject-introspection
76 | ```
77 |
78 | :::
79 |
80 | 2. install `appmenu-glib-translator`
81 |
82 | ```sh
83 | git clone https://github.com/rilian-la-te/vala-panel-appmenu.git
84 | cd vala-panel-appmenu/subprojects/appmenu-glib-translator
85 | meson setup build
86 | meson install -C build
87 | ```
88 |
89 | 3. clone repo
90 |
91 | ```sh
92 | git clone https://github.com/aylur/astal.git
93 | cd astal/lib/tray
94 | ```
95 |
96 | 4. install
97 |
98 | ```sh
99 | meson setup build
100 | meson install -C build
101 | ```
102 |
--------------------------------------------------------------------------------
/lib/apps/src/fuzzy.vala:
--------------------------------------------------------------------------------
1 | namespace AstalApps {
2 | private int fuzzy_match_string(string pattern, string str) {
3 | const int unmatched_letter_penalty = -1;
4 | int score = 100;
5 | int not_found_score = -10;
6 |
7 | if (pattern.length == 0) return score;
8 | if (str.length < pattern.length) return not_found_score;
9 |
10 | bool found = fuzzy_match_recurse(pattern, str, score, true, out score);
11 | score += unmatched_letter_penalty * (str.length - pattern.length);
12 |
13 | if (!found) score = not_found_score;
14 | return score;
15 | }
16 |
17 | private bool fuzzy_match_recurse(string pattern, string str, int score, bool first_char, out int result) {
18 | result = score;
19 | if (pattern.length == 0) return true;
20 |
21 | int match_idx = 0;
22 | int offset = 0;
23 | unichar search = pattern.casefold().get_char(0);
24 | int best_score = int.MIN;
25 |
26 | while ((match_idx = str.casefold().substring(offset).index_of_char(search)) >= 0) {
27 | offset += match_idx;
28 | int subscore;
29 | bool found = fuzzy_match_recurse(
30 | pattern.substring(1),
31 | str.substring(offset + 1),
32 | compute_score(offset, first_char, str, offset), false, out subscore);
33 | if (!found) break;
34 | best_score = int.max(best_score, subscore);
35 | offset++;
36 | }
37 |
38 | if (best_score == int.MIN) return false;
39 | result += best_score;
40 | return true;
41 | }
42 |
43 | private int compute_score(int jump, bool first_char, string match, int idx) {
44 | const int adjacency_bonus = 15;
45 | const int separator_bonus = 30;
46 | const int camel_bonus = 30;
47 | const int first_letter_bonus = 15;
48 | const int leading_letter_penalty = -5;
49 | const int max_leading_letter_penalty = -15;
50 |
51 | int score = 0;
52 |
53 | if (!first_char && (jump == 0)) {
54 | score += adjacency_bonus;
55 | }
56 | if (!first_char || (jump > 0)) {
57 | if (match[idx].isupper() && match[idx - 1].islower()) {
58 | score += camel_bonus;
59 | }
60 | if (match[idx].isalnum() && !match[idx - 1].isalnum()) {
61 | score += separator_bonus;
62 | }
63 | }
64 | if (first_char && (jump == 0)) {
65 | score += first_letter_bonus;
66 | }
67 | if (first_char) {
68 | score += int.max(leading_letter_penalty * jump, max_leading_letter_penalty);
69 | }
70 |
71 | return score;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/docs/guide/libraries/bluetooth.md:
--------------------------------------------------------------------------------
1 | # Bluetooth
2 |
3 | Library for monitoring [bluez](https://www.bluez.org/) over dbus.
4 |
5 | ## Usage
6 |
7 | You can browse the
8 | [Bluetooth reference](https://aylur.github.io/libastal/bluetooth).
9 |
10 | ### CLI
11 |
12 | There is no CLI for this library, use the one provided by bluez.
13 |
14 | ```sh
15 | bluetoothctl --help
16 | ```
17 |
18 | ### Library
19 |
20 | :::code-group
21 |
22 | ```js [ JavaScript]
23 | import Bluetooth from "gi://AstalBluetooth"
24 |
25 | const bluetooth = Bluetooth.get_default()
26 |
27 | for (const device of bluetooth.get_devices()) {
28 | print(device.name)
29 | }
30 | ```
31 |
32 | ```py [ Python]
33 | from gi.repository import AstalBluetooth as Bluetooth
34 |
35 | bluetooth = Bluetooth.get_default()
36 |
37 | for device in bluetooth.get_devices():
38 | print(device.get_name())
39 | ```
40 |
41 | ```lua [ Lua]
42 | local Bluetooth = require("lgi").require("AstalBluetooth")
43 |
44 | local bluetooth = Bluetooth.get_default()
45 |
46 | for _, d in ipairs(bluetooth.devices) do
47 | print(d.name)
48 | end
49 | ```
50 |
51 | ```vala [ Vala]
52 | var bluetooth = AstalBluetooth.get_default();
53 |
54 | foreach (var device in bluetooth.get_devices()) {
55 | print("%s\n", device.name);
56 | }
57 | ```
58 |
59 | :::
60 |
61 | ## Installation
62 |
63 | 1. install dependencies
64 |
65 | :::code-group
66 |
67 | ```sh [ Arch]
68 | sudo pacman -Syu meson vala valadoc gobject-introspection
69 | ```
70 |
71 | ```sh [ Fedora]
72 | sudo dnf install meson vala valadoc gobject-introspection-devel
73 | ```
74 |
75 | ```sh [ Ubuntu]
76 | sudo apt install meson valac valadoc gobject-introspection
77 | ```
78 |
79 | :::
80 |
81 | ::: info
82 |
83 | Although bluez is not a direct build dependency, it should be
84 | self-explanatory that the daemon is required to be available at runtime.
85 |
86 | :::
87 |
88 | 2. clone repo
89 |
90 | ```sh
91 | git clone https://github.com/aylur/astal.git
92 | cd astal/lib/bluetooth
93 | ```
94 |
95 | 3. install
96 |
97 | ```sh
98 | meson setup build
99 | meson install -C build
100 | ```
101 |
--------------------------------------------------------------------------------
/lang/lua/astal/binding.lua:
--------------------------------------------------------------------------------
1 | local lgi = require("lgi")
2 | local GObject = lgi.require("GObject", "2.0")
3 |
4 | ---@class Binding
5 | ---@field emitter table | Variable | userdata
6 | ---@field property? string
7 | ---@field transform_fn function
8 | ---@overload fun(emitter: table | userdata, property?: string): Binding
9 | local Binding = {}
10 | Binding.__index = Binding
11 |
12 | ---@param emitter table | Variable | userdata
13 | ---@param property? string
14 | ---@return Binding
15 | function Binding.new(emitter, property)
16 | return setmetatable({
17 | emitter = emitter,
18 | property = property,
19 | transform_fn = function(v)
20 | return v
21 | end,
22 | }, Binding)
23 | end
24 |
25 | function Binding:__tostring()
26 | local str = "Binding<" .. tostring(self.emitter)
27 | if self.property ~= nil then
28 | str = str .. ", " .. self.property
29 | end
30 | return str .. ">"
31 | end
32 |
33 | ---@return any
34 | function Binding:get()
35 | if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then
36 | return self.transform_fn(self.emitter[self.property])
37 | elseif type(self.emitter.get) == "function" then
38 | return self.transform_fn(self.emitter:get())
39 | else
40 | error("can not get: Not a GObject or a Variable " + self)
41 | end
42 | end
43 |
44 | ---@param transform fun(value: any): any
45 | ---@return Binding
46 | function Binding:as(transform)
47 | local b = Binding.new(self.emitter, self.property)
48 | b.transform_fn = function(v)
49 | return transform(self.transform_fn(v))
50 | end
51 | return b
52 | end
53 |
54 | ---@param callback fun(value: any)
55 | ---@return function
56 | function Binding:subscribe(callback)
57 | if self.property ~= nil and GObject.Object:is_type_of(self.emitter) then
58 | local id = self.emitter.on_notify:connect(function()
59 | callback(self:get())
60 | end, self.property, false)
61 | return function()
62 | GObject.signal_handler_disconnect(self.emitter, id)
63 | end
64 | elseif type(self.emitter.subscribe) == "function" then
65 | return self.emitter:subscribe(function()
66 | callback(self:get())
67 | end)
68 | else
69 | error("can not subscribe: Not a GObject or a Variable " + self)
70 | end
71 | end
72 |
73 | return setmetatable(Binding, {
74 | __call = function(_, emitter, prop)
75 | return Binding.new(emitter, prop)
76 | end,
77 | })
78 |
--------------------------------------------------------------------------------