diff options
-rw-r--r-- | core/lua/astal/widget.lua | 16 | ||||
-rw-r--r-- | docs/guide/ags/cli-app.md (renamed from docs/ags/cli-app.md) | 0 | ||||
-rw-r--r-- | docs/guide/ags/faq.md (renamed from docs/ags/faq.md) | 86 | ||||
-rw-r--r-- | docs/guide/ags/first-widgets.md (renamed from docs/ags/first-widgets.md) | 9 | ||||
-rw-r--r-- | docs/guide/ags/installation.md (renamed from docs/ags/installation.md) | 10 | ||||
-rw-r--r-- | docs/guide/ags/theming.md (renamed from docs/ags/theming.md) | 0 | ||||
-rw-r--r-- | docs/guide/ags/utilities.md (renamed from docs/ags/utilities.md) | 0 | ||||
-rw-r--r-- | docs/guide/ags/variable.md (renamed from docs/ags/variable.md) | 2 | ||||
-rw-r--r-- | docs/guide/ags/widget.md (renamed from docs/ags/widget.md) | 0 | ||||
-rw-r--r-- | docs/guide/getting-started/installation.md (renamed from docs/getting-started/installation.md) | 0 | ||||
-rw-r--r-- | docs/guide/getting-started/introduction.md (renamed from docs/getting-started/introduction.md) | 2 | ||||
-rw-r--r-- | docs/guide/getting-started/nix.md (renamed from docs/getting-started/nix.md) | 0 | ||||
-rw-r--r-- | docs/guide/getting-started/supported-languages.md (renamed from docs/getting-started/supported-languages.md) | 7 | ||||
-rw-r--r-- | docs/guide/libraries/apps.md (renamed from docs/libraries/apps.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/auth.md (renamed from docs/libraries/auth.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/battery.md (renamed from docs/libraries/battery.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/bluetooth.md (renamed from docs/libraries/bluetooth.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/hyprland.md (renamed from docs/libraries/hyprland.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/mpris.md (renamed from docs/libraries/mpris.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/network.md (renamed from docs/libraries/network.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/notifd.md (renamed from docs/libraries/notifd.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/powerprofiles.md (renamed from docs/libraries/powerprofiles.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/references.md (renamed from docs/libraries/references.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/river.md (renamed from docs/libraries/river.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/tray.md (renamed from docs/libraries/tray.md) | 0 | ||||
-rw-r--r-- | docs/guide/libraries/wireplumber.md (renamed from docs/libraries/wireplumber.md) | 0 | ||||
-rw-r--r-- | docs/index.md | 17 | ||||
-rw-r--r-- | docs/showcases/Showcase.vue | 6 | ||||
-rw-r--r-- | docs/showcases/index.md | 9 | ||||
-rw-r--r-- | docs/showcases/showcases.ts | 4 | ||||
-rw-r--r-- | docs/vitepress.config.ts | 57 | ||||
-rw-r--r-- | examples/js/simple-bar/README.md | 2 | ||||
-rw-r--r-- | examples/lua/simple-bar/README.md | 2 | ||||
-rw-r--r-- | examples/py/simple-bar/README.md | 14 | ||||
-rw-r--r-- | examples/py/simple-bar/__init__.py (renamed from examples/py/starter-bar/__init__.py) | 0 | ||||
-rwxr-xr-x | examples/py/simple-bar/app.py (renamed from examples/py/starter-bar/app.py) | 19 | ||||
-rw-r--r-- | examples/py/simple-bar/style.scss | 88 | ||||
-rw-r--r-- | examples/py/simple-bar/versions.py | 14 | ||||
-rw-r--r-- | examples/py/simple-bar/widget/Bar.py | 257 | ||||
-rw-r--r-- | examples/py/simple-bar/widget/__init__.py (renamed from examples/py/starter-bar/widget/__init__.py) | 0 | ||||
-rw-r--r-- | examples/py/starter-bar/README.md | 7 | ||||
-rw-r--r-- | examples/py/starter-bar/style.css | 4 | ||||
-rw-r--r-- | examples/py/starter-bar/versions.py | 6 | ||||
-rw-r--r-- | examples/py/starter-bar/widget/Bar.py | 48 | ||||
-rw-r--r-- | examples/vala/simple-bar/README.md | 1 | ||||
-rw-r--r-- | examples/vala/simple-bar/app.in.vala | 20 | ||||
-rw-r--r-- | examples/vala/simple-bar/flake.lock | 62 | ||||
-rw-r--r-- | examples/vala/simple-bar/widget/Bar.vala | 7 | ||||
-rw-r--r-- | lib/hyprland/hyprland.vala | 1 | ||||
-rw-r--r-- | lib/river/include/astal-river.h.in | 2 | ||||
-rw-r--r-- | lib/river/include/river-private.h | 3 | ||||
-rw-r--r-- | lib/river/src/river-output.c | 33 | ||||
-rw-r--r-- | lib/river/src/river.c | 2 |
53 files changed, 685 insertions, 132 deletions
diff --git a/core/lua/astal/widget.lua b/core/lua/astal/widget.lua index 5e74440..1f5b07b 100644 --- a/core/lua/astal/widget.lua +++ b/core/lua/astal/widget.lua @@ -116,9 +116,17 @@ end local function astalify(ctor) function ctor:hook(object, signalOrCallback, callback) if GObject.Object:is_type_of(object) and type(signalOrCallback) == "string" then - local id = object["on_" .. signalOrCallback]:connect(function(_, ...) - callback(self, ...) - end) + local id + if string.sub(signalOrCallback, 1, 8) == "notify::" then + local prop = string.gsub(signalOrCallback, "notify::", "") + id = object.on_notify:connect(function() + callback(self, object[prop]) + end, prop, false) + else + id = object["on_" .. signalOrCallback]:connect(function(_, ...) + callback(self, ...) + end) + end self.on_destroy = function() GObject.signal_handler_disconnect(object, id) end @@ -133,7 +141,7 @@ local function astalify(ctor) end function ctor:toggle_class_name(name, on) - Astal.toggle_class_name(self, name, on) + Astal.widget_toggle_class_name(self, name, on) end return function(tbl) diff --git a/docs/ags/cli-app.md b/docs/guide/ags/cli-app.md index ceed56a..ceed56a 100644 --- a/docs/ags/cli-app.md +++ b/docs/guide/ags/cli-app.md diff --git a/docs/ags/faq.md b/docs/guide/ags/faq.md index 61c9bff..2758ef0 100644 --- a/docs/ags/faq.md +++ b/docs/guide/ags/faq.md @@ -272,7 +272,7 @@ notifd.get_notifications() // ✅ ## How to create regular floating windows -Use `Gtk.Window` with [Widget.astalify](/ags/widget#how-to-use-non-builtin-gtk-widgets). +Use `Gtk.Window` with [Widget.astalify](/guide/ags/widget#how-to-use-non-builtin-gtk-widgets). By default `Gtk.Window` is destroyed on close. To prevent this add a handler for `delete-event`. @@ -288,3 +288,87 @@ return <RegularWindow {child} </RegularWindow> ``` + +## Avoiding unnecessary re-rendering + +As mentioned before, any object can be bound that implements the `Subscribable` interface. + +```ts +interface Subscribable<T = unknown> { + subscribe(callback: (value: T) => void): () => void + get(): T +} +``` + +This can be used to our advantage to create a reactive `Map` object. + +```ts +import { type Subscribable } from "astal/binding" +import { Gtk } from "astal" + +export class VarMap<K, T = Gtk.Widget> implements Subscribable { + #subs = new Set<(v: Array<[K, T]>) => void>() + #map: Map<K, T> + + #notifiy() { + const value = this.get() + for (const sub of this.#subs) { + sub(value) + } + } + + #delete(key: K) { + const v = this.#map.get(key) + + if (v instanceof Gtk.Widget) { + v.destroy() + } + + this.#map.delete(key) + } + + constructor(initial?: Iterable<[K, T]>) { + this.#map = new Map(initial) + } + + add(key: K, value: T) { + this.#delete(key) + this.#map.set(key, value) + this.#notifiy() + } + + delete(key: K) { + this.#delete(key) + this.#notifiy() + } + + get() { + return [...this.#map.entries()] + } + + subscribe(callback: (v: Array<[K, T]>) => void) { + this.#subs.add(callback) + return () => this.#subs.delete(callback) + } +} +``` + +And this `VarMap<key, Widget>` can be used as an alternative to `Variable<Array<Widget>>`. + +```tsx +function MappedBox() { + const map = new VarMap([ + [1, <MyWidget id={id} />] + [2, <MyWidget id={id} />] + ]) + + const conns = [ + gobject.connect("added", (_, id) => map.set(id, MyWidget({ id }))), + gobject.connect("removed", (_, id) => map.delete(id, MyWidget({ id }))), + ] + + return <box onDestroy={() => conns.map(id => gobject.disconnect(id))}> + {bind(map).as(arr => arr.sort(([a], [b]) => a - b).map(([,w]) => w))} + </box> +} +``` diff --git a/docs/ags/first-widgets.md b/docs/guide/ags/first-widgets.md index 1b18109..c4c4436 100644 --- a/docs/ags/first-widgets.md +++ b/docs/guide/ags/first-widgets.md @@ -120,7 +120,7 @@ Lowercase tags are builtin widgets, while capital letter is for custom widgets. JSX lets you put markup into JavaScript. Curly braces let you “escape back” into JavaScript so that you can embed some variable -from your code and display it to the user. +from your code and display it. ```tsx function MyWidget() { @@ -264,7 +264,7 @@ return <MyWidget myprop="hello"> The state of widgets are handled with Bindings. A `Binding` lets you connect the state of one [GObject](https://docs.gtk.org/gobject/class.Object.html) to another, in our case it is used to rerender part of a widget based on the state of a `GObject`. -A `GObject` can be a [Variable](./variable) or it can be from a [Library](/libraries/references). +A `GObject` can be a [Variable](./variable) or it can be from a [Library](../libraries/references). We use the `bind` function to create a `Binding` object from a `Variable` or a regular GObject and one of its properties. @@ -313,7 +313,7 @@ return <box> ::: Here is an example of a battery percent label that binds the `percentage` -property of the Battery object from the [Battery Library](/libraries/battery): +property of the Battery object from the [Battery Library](/guide/libraries/battery): ```tsx import Battery from "gi://AstalBattery" @@ -359,7 +359,8 @@ You can disable this behavior by setting the `noImplicityDestroy` property. :::info The above example destroys and recreates every widget in the list everytime the value of the `Variable` changes. There might be cases where you would -want to handle child creation yourself, because you don't want to lose the +want to [handle child creation and deletion](/guide/ags/faq#avoiding-unnecessary-re-rendering) +yourself, because you don't want to lose the inner state of widgets that does not need to be recreated. ::: diff --git a/docs/ags/installation.md b/docs/guide/ags/installation.md index 42ef1c3..0adcf68 100644 --- a/docs/ags/installation.md +++ b/docs/guide/ags/installation.md @@ -53,3 +53,13 @@ go install -ldflags "-X main.astalGjs=$(pkg-config --variable prefix astal-0.1)/ ``` ::: + +:::tip +`go install` installs the `ags` binary to `$GOPATH/bin` so make sure its in your `$PATH`. +You can move it to another directory if you like. For example + +```sh +mv $GOPATH/bin/ags ~/.local/bin/ags +``` + +::: diff --git a/docs/ags/theming.md b/docs/guide/ags/theming.md index ea83e35..ea83e35 100644 --- a/docs/ags/theming.md +++ b/docs/guide/ags/theming.md diff --git a/docs/ags/utilities.md b/docs/guide/ags/utilities.md index 42589d3..42589d3 100644 --- a/docs/ags/utilities.md +++ b/docs/guide/ags/utilities.md diff --git a/docs/ags/variable.md b/docs/guide/ags/variable.md index 06098dd..96e8d38 100644 --- a/docs/ags/variable.md +++ b/docs/guide/ags/variable.md @@ -69,7 +69,7 @@ output in `Variables`. They can poll and watch at the same time, but they can only poll/watch one subprocess. :::warning -The command parameter is passed to [execAsync](/astal/ags/utilities/#executing-external-commands-and-scripts) +The command parameter is passed to [execAsync](/guide/ags/utilities#executing-external-commands-and-scripts) which means they are **not** executed in a shell environment, they do **not** expand env variables like `$HOME`, and they do **not** handle logical operators like `&&` and `||`. diff --git a/docs/ags/widget.md b/docs/guide/ags/widget.md index 1fe755f..1fe755f 100644 --- a/docs/ags/widget.md +++ b/docs/guide/ags/widget.md diff --git a/docs/getting-started/installation.md b/docs/guide/getting-started/installation.md index ef5c6e8..ef5c6e8 100644 --- a/docs/getting-started/installation.md +++ b/docs/guide/getting-started/installation.md diff --git a/docs/getting-started/introduction.md b/docs/guide/getting-started/introduction.md index 7f88362..92d2ff1 100644 --- a/docs/getting-started/introduction.md +++ b/docs/guide/getting-started/introduction.md @@ -19,5 +19,5 @@ Have you ever wanted to write a custom bar, custom notification popups or an applauncher, but gave up because writing a workspace widget, implementing the notification daemon or handling a search filter was too much of a hassle? -Astal libraries have you [covered](/libraries/references), you don't have to worry about these, +Astal libraries have you [covered](/guide/libraries/references), you don't have to worry about these, you just define the layout, style it with CSS and that's it. diff --git a/docs/getting-started/nix.md b/docs/guide/getting-started/nix.md index 2b04bdc..2b04bdc 100644 --- a/docs/getting-started/nix.md +++ b/docs/guide/getting-started/nix.md diff --git a/docs/getting-started/supported-languages.md b/docs/guide/getting-started/supported-languages.md index 06ce51c..4cb7f3a 100644 --- a/docs/getting-started/supported-languages.md +++ b/docs/guide/getting-started/supported-languages.md @@ -2,7 +2,7 @@ ## JavaScript -The main intended usage of Astal is in TypeScript with [AGS](/ags/first-widgets). +The main intended usage of Astal is in TypeScript with [AGS](/guide/ags/first-widgets). It supports JSX and has a state management solution similar to web frameworks. Only a minimal knowledge of JavaScript's syntax is needed to get started. @@ -19,7 +19,7 @@ Examples: Similar to how there is a [TypeScript](https://github.com/Aylur/astal/tree/main/core/gjs) lib for GJS, there is also an accompanying library for [Lua](https://github.com/Aylur/astal/tree/main/core/lua). <!--TODO: open issue and link performance issue--> -Unfortunately, I have encountered very heavy [performance issues]() with [lgi](https://github.com/lgi-devs/lgi), +Unfortunately, I have encountered very heavy [performance issues](https://github.com/aylur/astal) with [lgi](https://github.com/lgi-devs/lgi), and so currently I don't recommend using Lua for full desktop shells, but only for static components that don't render child nodes dynamically, bars and panels for example. @@ -37,7 +37,8 @@ Nonetheless you can still use python the OOP way [pygobject](https://pygobject.g Examples: -- [Starter Bar](https://github.com/Aylur/astal/tree/main/examples/py/starter-bar) +- [Simple Bar](https://github.com/Aylur/astal/tree/main/examples/py/simple-bar) + ## Vala diff --git a/docs/libraries/apps.md b/docs/guide/libraries/apps.md index c53daf0..c53daf0 100644 --- a/docs/libraries/apps.md +++ b/docs/guide/libraries/apps.md diff --git a/docs/libraries/auth.md b/docs/guide/libraries/auth.md index 1f07a17..1f07a17 100644 --- a/docs/libraries/auth.md +++ b/docs/guide/libraries/auth.md diff --git a/docs/libraries/battery.md b/docs/guide/libraries/battery.md index b42d747..b42d747 100644 --- a/docs/libraries/battery.md +++ b/docs/guide/libraries/battery.md diff --git a/docs/libraries/bluetooth.md b/docs/guide/libraries/bluetooth.md index 04d9db2..04d9db2 100644 --- a/docs/libraries/bluetooth.md +++ b/docs/guide/libraries/bluetooth.md diff --git a/docs/libraries/hyprland.md b/docs/guide/libraries/hyprland.md index faf9e50..faf9e50 100644 --- a/docs/libraries/hyprland.md +++ b/docs/guide/libraries/hyprland.md diff --git a/docs/libraries/mpris.md b/docs/guide/libraries/mpris.md index dfe7956..dfe7956 100644 --- a/docs/libraries/mpris.md +++ b/docs/guide/libraries/mpris.md diff --git a/docs/libraries/network.md b/docs/guide/libraries/network.md index afeb5d2..afeb5d2 100644 --- a/docs/libraries/network.md +++ b/docs/guide/libraries/network.md diff --git a/docs/libraries/notifd.md b/docs/guide/libraries/notifd.md index 7e02149..7e02149 100644 --- a/docs/libraries/notifd.md +++ b/docs/guide/libraries/notifd.md diff --git a/docs/libraries/powerprofiles.md b/docs/guide/libraries/powerprofiles.md index 8571c29..8571c29 100644 --- a/docs/libraries/powerprofiles.md +++ b/docs/guide/libraries/powerprofiles.md diff --git a/docs/libraries/references.md b/docs/guide/libraries/references.md index 8f2bd02..8f2bd02 100644 --- a/docs/libraries/references.md +++ b/docs/guide/libraries/references.md diff --git a/docs/libraries/river.md b/docs/guide/libraries/river.md index 4818d0b..4818d0b 100644 --- a/docs/libraries/river.md +++ b/docs/guide/libraries/river.md diff --git a/docs/libraries/tray.md b/docs/guide/libraries/tray.md index c8d093b..c8d093b 100644 --- a/docs/libraries/tray.md +++ b/docs/guide/libraries/tray.md diff --git a/docs/libraries/wireplumber.md b/docs/guide/libraries/wireplumber.md index 5f1daab..5f1daab 100644 --- a/docs/libraries/wireplumber.md +++ b/docs/guide/libraries/wireplumber.md diff --git a/docs/index.md b/docs/index.md index 46f28da..f22ae19 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,13 +10,13 @@ hero: actions: - theme: brand text: What is Astal? - link: /getting-started/introduction + link: /guide/getting-started/introduction - theme: alt text: Get Started - link: /getting-started/installation + link: /guide/getting-started/installation - theme: alt text: References - link: /libraries/references + link: /guide/libraries/references features: - title: Use Your Preferred Language @@ -29,12 +29,6 @@ features: icon: <i style="color:var(--vp-c-brand-3)" class="fl-gtk"></i> details: With Astal you work with Gtk directly. You are not limited to only a set of them. --- -<script setup> -import Showcases from './showcases/Showcases.vue' -</script> - -<Showcases /> - <!--TODO: add icons for buttons https://github.com/vuejs/vitepress/pull/3795--> <style> @@ -64,6 +58,11 @@ import Showcases from './showcases/Showcases.vue' font-weight: bold; } + .VPFooter { + background-color: transparent !important; + border: none; + } + .VPNavBar:not(.top) { background-color: transparent !important; -webkit-backdrop-filter: blur(16px); diff --git a/docs/showcases/Showcase.vue b/docs/showcases/Showcase.vue index 5873a8e..a5a1e01 100644 --- a/docs/showcases/Showcase.vue +++ b/docs/showcases/Showcase.vue @@ -1,7 +1,7 @@ <script setup> const props = defineProps({ src: { type: String, required: true }, - author: { type: String, required: true }, + label: { type: String, required: true }, url: { type: String, required: true } }); </script> @@ -9,10 +9,10 @@ const props = defineProps({ <template> <figure> <a :href="url"> - <img :src="src" :alt="author" /> + <img :src="src" :alt="label" /> </a> <figcaption> - <span>Author: {{ author }}</span> + <span>{{ label }}</span> </figcaption> </figure> </template> diff --git a/docs/showcases/index.md b/docs/showcases/index.md new file mode 100644 index 0000000..21b54b7 --- /dev/null +++ b/docs/showcases/index.md @@ -0,0 +1,9 @@ +--- +layout: home +--- + +<script setup> +import Showcases from './Showcases.vue' +</script> + +<Showcases /> diff --git a/docs/showcases/showcases.ts b/docs/showcases/showcases.ts index fa0a7bb..138ae7f 100644 --- a/docs/showcases/showcases.ts +++ b/docs/showcases/showcases.ts @@ -1,5 +1,5 @@ type Showcase = { - author: string + label: string src: string url: string } @@ -10,6 +10,6 @@ type Grid<T> = T | [T, T, T, T] export default [ - { author: "Aylur", src: "/astal/showcase/aylur1.png", url: "https://github.com/Aylur/dotfiles" }, + { label: "Placeholder (this is an ags v1 screenshot)", src: "/astal/showcase/aylur1.png", url: "https://github.com/Aylur/dotfiles" }, // add more showcases here ] satisfies Array<Grid<Showcase>> diff --git a/docs/vitepress.config.ts b/docs/vitepress.config.ts index aa6aae2..25d9e36 100644 --- a/docs/vitepress.config.ts +++ b/docs/vitepress.config.ts @@ -13,7 +13,6 @@ export default defineConfig({ cleanUrls: true, lastUpdated: true, - ignoreDeadLinks: true, // FIXME: head: [ ["link", { rel: "icon", href: "/astal/icon.svg" }], @@ -27,18 +26,30 @@ export default defineConfig({ copyright: 'Logo is created by VDawg' }, - nav: [{ - text: "0.1.0", - items: [ - { text: "Contributing", link: github("/blob/main/CONTRIBUTING.md") }, - { text: "Changelog", link: github("/blob/main/CHANGELOG.md") }, - ], - }], + nav: [ + { + text: "Showcases", + link: "/showcases", + activeMatch: "/showcases/", + }, + { + text: "Guide", + link: "/guide/getting-started/installation", + activeMatch: "/guide/", + }, + { + text: "0.1.0", + items: [ + { text: "Contributing", link: github("/blob/main/CONTRIBUTING.md") }, + { text: "Changelog", link: github("/blob/main/CHANGELOG.md") }, + ], + } + ], sidebar: [ { text: "Getting Started", - base: "/getting-started", + base: "/guide/getting-started", collapsed: false, items: [ { text: "Introduction", link: "/introduction" }, @@ -48,7 +59,7 @@ export default defineConfig({ }, { text: "AGS", - base: "/ags", + base: "/guide/ags", collapsed: false, items: [ { text: "Installation", link: "/installation" }, @@ -65,20 +76,20 @@ export default defineConfig({ text: "Libraries", collapsed: true, items: [ - { text: "References", link: "/libraries/references" }, + { text: "References", link: "/guide/libraries/references" }, { text: "Astal", link: "https://aylur.github.io/libastal" }, - { text: "Apps", link: "/libraries/apps" }, - { text: "Auth", link: "/libraries/auth" }, - { text: "Battery", link: "/libraries/battery" }, - { text: "Bluetooth", link: "/libraries/bluetooth" }, - { text: "Hyprland", link: "/libraries/hyprland" }, - { text: "Mpris", link: "/libraries/mpris" }, - { text: "Network", link: "/libraries/network" }, - { text: "Notifd", link: "/libraries/notifd" }, - { text: "PowerProfiles", link: "/libraries/powerprofiles" }, - { text: "River", link: "/libraries/river" }, - { text: "Tray", link: "/libraries/tray" }, - { text: "WirePlumber", link: "/libraries/wireplumber" }, + { text: "Apps", link: "/guide/libraries/apps" }, + { text: "Auth", link: "/guide/libraries/auth" }, + { text: "Battery", link: "/guide/libraries/battery" }, + { text: "Bluetooth", link: "/guide/libraries/bluetooth" }, + { text: "Hyprland", link: "/guide/libraries/hyprland" }, + { text: "Mpris", link: "/guide/libraries/mpris" }, + { text: "Network", link: "/guide/libraries/network" }, + { text: "Notifd", link: "/guide/libraries/notifd" }, + { text: "PowerProfiles", link: "/guide/libraries/powerprofiles" }, + { text: "River", link: "/guide/libraries/river" }, + { text: "Tray", link: "/guide/libraries/tray" }, + { text: "WirePlumber", link: "/guide/libraries/wireplumber" }, ], }, ], diff --git a/examples/js/simple-bar/README.md b/examples/js/simple-bar/README.md index 8f733da..997e4bf 100644 --- a/examples/js/simple-bar/README.md +++ b/examples/js/simple-bar/README.md @@ -9,3 +9,5 @@ A simple bar for Hyprland using - [Hyprland library](https://aylur.github.io/astal/libraries/hyprland). - [Mpris library](https://aylur.github.io/astal/libraries/mpris). - [Network library](https://aylur.github.io/astal/libraries/network). +- [Tray library](https://aylur.github.io/astal/libraries/tray). +- [WirePlumber library](https://aylur.github.io/astal/libraries/wireplumber). diff --git a/examples/lua/simple-bar/README.md b/examples/lua/simple-bar/README.md index 1cebdac..d33e57d 100644 --- a/examples/lua/simple-bar/README.md +++ b/examples/lua/simple-bar/README.md @@ -9,4 +9,6 @@ A simple bar for Hyprland using - [Hyprland library](https://aylur.github.io/astal/libraries/hyprland). - [Mpris library](https://aylur.github.io/astal/libraries/mpris). - [Network library](https://aylur.github.io/astal/libraries/network). +- [Tray library](https://aylur.github.io/astal/libraries/tray). +- [WirePlumber library](https://aylur.github.io/astal/libraries/wireplumber). - [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler diff --git a/examples/py/simple-bar/README.md b/examples/py/simple-bar/README.md new file mode 100644 index 0000000..d33e57d --- /dev/null +++ b/examples/py/simple-bar/README.md @@ -0,0 +1,14 @@ +# Simple Bar Example + + + +A simple bar for Hyprland using + +- [Audio library](https://aylur.github.io/astal/libraries/audio). +- [Battery library](https://aylur.github.io/astal/libraries/battery). +- [Hyprland library](https://aylur.github.io/astal/libraries/hyprland). +- [Mpris library](https://aylur.github.io/astal/libraries/mpris). +- [Network library](https://aylur.github.io/astal/libraries/network). +- [Tray library](https://aylur.github.io/astal/libraries/tray). +- [WirePlumber library](https://aylur.github.io/astal/libraries/wireplumber). +- [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler diff --git a/examples/py/starter-bar/__init__.py b/examples/py/simple-bar/__init__.py index e69de29..e69de29 100644 --- a/examples/py/starter-bar/__init__.py +++ b/examples/py/simple-bar/__init__.py diff --git a/examples/py/starter-bar/app.py b/examples/py/simple-bar/app.py index 287d0f8..f5a8a80 100755 --- a/examples/py/starter-bar/app.py +++ b/examples/py/simple-bar/app.py @@ -1,27 +1,32 @@ #!/usr/bin/env python3 +import sys import versions from gi.repository import Astal, Gio from widget.Bar import Bar from pathlib import Path -css = str(Path(__file__).parent.resolve() / "style.css") +scss = str(Path(__file__).parent.resolve() / "style.scss") +css = "/tmp/style.css" class App(Astal.Application): - def __init__(self): - super().__init__() - self.acquire_socket() - self.run(None) - def do_request(self, msg: str, conn: Gio.SocketConnection) -> None: print(msg) Astal.write_sock(conn, "hello") def do_activate(self) -> None: self.hold() + Astal.Process.execv(["sass", scss, css]) self.apply_css(css, True) for mon in self.get_monitors(): self.add_window(Bar(mon)) -App() +instance_name = "simple-bar" +app = App(instance_name=instance_name) + +if __name__ == "__main__": + if app.acquire_socket(): + app.run(None) + else: + print(Astal.Application.send_message(instance_name, "".join(sys.argv[1:]))) diff --git a/examples/py/simple-bar/style.scss b/examples/py/simple-bar/style.scss new file mode 100644 index 0000000..f98286e --- /dev/null +++ b/examples/py/simple-bar/style.scss @@ -0,0 +1,88 @@ +$bg: #212223; +$fg: #f1f1f1; +$accent: #378DF7; +$radius: 7px; + +window.Bar { + border: none; + box-shadow: none; + background-color: $bg; + color: $fg; + font-size: 1.1em; + font-weight: bold; + + button { + all: unset; + background-color: transparent; + + &:hover label { + background-color: transparentize($fg, 0.84); + border-color: transparentize($accent, 0.8); + } + + &:active label { + background-color: transparentize($fg, 0.8) + } + } + + label { + transition: 200ms; + padding: 0 8px; + margin: 2px; + border-radius: $radius; + border: 1pt solid transparent; + } + + .Workspaces .focused label { + color: $accent; + border-color: $accent; + } + + .FocusedClient { + color: $accent; + } + + .Media .Cover { + min-height: 1.2em; + min-width: 1.2em; + border-radius: $radius; + background-position: center; + background-size: contain; + } + + .Battery label { + padding-left: 0; + margin-left: 0; + } + + .AudioSlider { + * { + all: unset; + } + + icon { + margin-right: .6em; + } + + margin: 0 1em; + + trough { + background-color: transparentize($fg, 0.8); + border-radius: $radius; + } + + highlight { + background-color: $accent; + min-height: .8em; + border-radius: $radius; + } + + slider { + background-color: $fg; + border-radius: $radius; + min-height: 1em; + min-width: 1em; + margin: -.2em; + } + } +} diff --git a/examples/py/simple-bar/versions.py b/examples/py/simple-bar/versions.py new file mode 100644 index 0000000..a8a1ab8 --- /dev/null +++ b/examples/py/simple-bar/versions.py @@ -0,0 +1,14 @@ +import gi + +gi.require_version("Astal", "0.1") +gi.require_version("Gtk", "3.0") +gi.require_version("Gdk", "3.0") +gi.require_version("Gio", "2.0") +gi.require_version("GObject", "2.0") + +gi.require_version("AstalBattery", "0.1") +gi.require_version("AstalWp", "0.1") +gi.require_version("AstalNetwork", "0.1") +gi.require_version("AstalTray", "0.1") +gi.require_version("AstalMpris", "0.1") +gi.require_version("AstalHyprland", "0.1") diff --git a/examples/py/simple-bar/widget/Bar.py b/examples/py/simple-bar/widget/Bar.py new file mode 100644 index 0000000..89581f1 --- /dev/null +++ b/examples/py/simple-bar/widget/Bar.py @@ -0,0 +1,257 @@ +import math +from gi.repository import ( + Astal, + Gtk, + Gdk, + GLib, + GObject, + AstalBattery as Battery, + AstalWp as Wp, + AstalNetwork as Network, + AstalTray as Tray, + AstalMpris as Mpris, + AstalHyprland as Hyprland, +) + +SYNC = GObject.BindingFlags.SYNC_CREATE + + +class Workspaces(Gtk.Box): + def __init__(self) -> None: + super().__init__() + Astal.widget_set_class_names(self, ["Workspaces"]) + hypr = Hyprland.get_default() + hypr.connect("notify::workspaces", self.sync) + hypr.connect("notify::focused-workspace", self.sync) + self.sync() + + def sync(self, *_): + hypr = Hyprland.get_default() + for child in self.get_children(): + child.destroy() + + for ws in hypr.get_workspaces(): + self.add(self.button(ws)) + + def button(self, ws): + hypr = Hyprland.get_default() + btn = Gtk.Button(visible=True) + btn.add(Gtk.Label(visible=True, label=ws.get_id())) + + if hypr.get_focused_workspace() == ws: + Astal.widget_set_class_names(btn, ["focused"]) + + btn.connect("clicked", lambda *_: ws.focus()) + return btn + + +class FocusedClient(Gtk.Label): + def __init__(self) -> None: + super().__init__() + Astal.widget_set_class_names(self, ["Focused"]) + Hyprland.get_default().connect("notify::focused-client", self.sync) + self.sync() + + def sync(self, *_): + client = Hyprland.get_default().get_focused_client() + if client is None: + return self.set_label("") + + client.bind_property("title", self, "label", SYNC) + + +class Media(Gtk.Box): + def __init__(self) -> None: + super().__init__() + self.players = {} + mpris = Mpris.get_default() + Astal.widget_set_class_names(self, ["Media"]) + mpris.connect("notify::players", self.sync) + self.sync() + + def sync(self): + mpris = Mpris.get_default() + for child in self.get_children(): + child.destroy() + + if len(mpris.get_players()) == 0: + self.add(Gtk.Label(visible=True, label="Nothing Playing")) + return + + player = mpris.get_players()[0] + label = Gtk.Label(visible=True) + cover = Gtk.Box(valign=Gtk.Align.CENTER) + Astal.widget_set_class_names(cover, ["Cover"]) + + self.add(cover) + self.add(label) + + player.bind_property( + "title", + label, + "label", + SYNC, + lambda *_: f"{player.get_artist()} - {player.get_title()}", + ) + + def on_cover_art(*_): + Astal.widget_set_css( + cover, f"background-image: url('{player.get_cover_art()}')" + ) + + id = player.connect("notify::cover-art", on_cover_art) + cover.connect("destroy", lambda _: player.disconnect(id)) + on_cover_art() + + +class SysTray(Gtk.Box): + def __init__(self) -> None: + super().__init__() + self.items = {} + tray = Tray.get_default() + tray.connect("item_added", self.add_item) + tray.connect("item_removed", self.remove_item) + + def add_item(self, _: Tray.Tray, id: str): + if id in self.items: + return + + item = Tray.get_default().get_item(id) + theme = item.get_icon_theme_path() + + if theme is not None: + from app import app + + app.add_icons(theme) + + menu = item.create_menu() + btn = Astal.Button() + icon = Astal.Icon() + + def on_clicked(btn): + if menu: + menu.popup_at_widget(btn, Gdk.Gravity.SOUTH, Gdk.Gravity.NORTH, None) + + def on_destroy(btn): + if menu: + menu.destroy() + + btn.connect("clicked", on_clicked) + btn.connect("destroy", on_destroy) + + item.bind_property("tooltip-markup", btn, "tooltip-markup", SYNC) + item.bind_property("gicon", icon, "gicon", SYNC) + self.add(btn) + self.items[id] = btn + + def remove_item(self, _: Tray.Tray, id: str): + if id in self.items: + del self.items[id] + + +class Wifi(Astal.Icon): + def __init__(self) -> None: + super().__init__() + Astal.widget_set_class_names(self, ["Wifi"]) + wifi = Network.get_default().get_wifi() + wifi.bind_property("ssid", self, "tooltip-text", SYNC) + wifi.bind_property("icon-name", self, "icon", SYNC) + + +class AudioSlider(Gtk.Box): + def __init__(self) -> None: + super().__init__() + Astal.widget_set_class_names(self, ["AudioSlider"]) + Astal.widget_set_css(self, "min-width: 140px") + + icon = Astal.Icon() + slider = Astal.Slider(hexpand=True) + + self.add(icon) + self.add(slider) + + speaker = Wp.get_default().get_audio().get_default_speaker() + speaker.bind_property("volume-icon", icon, "icon", SYNC) + speaker.bind_property("volume", slider, "value", SYNC) + slider.connect("dragged", lambda *_: speaker.set_volume(slider.get_value())) + + +class BatteryLevel(Gtk.Box): + def __init__(self) -> None: + super().__init__() + Astal.widget_set_class_names(self, ["Battery"]) + + icon = Astal.Icon() + label = Astal.Label() + + self.add(icon) + self.add(label) + + bat = Battery.get_default() + bat.bind_property("is-present", self, "visible", SYNC) + bat.bind_property("battery-icon-name", icon, "icon", SYNC) + bat.bind_property( + "percentage", + label, + "label", + SYNC, + lambda _, value: f"{math.floor(value * 100)}%", + ) + + +class Time(Astal.Label): + def __init__(self, format="%H:%M - %A %e."): + super().__init__() + self.format = format + self.interval = Astal.Time.interval(1000, self.sync) + self.connect("destroy", self.interval.cancel) + Astal.widget_set_class_names(self, ["Time"]) + + def sync(self): + self.set_label(GLib.DateTime.new_now_local().format(self.format)) + + +class Left(Gtk.Box): + def __init__(self) -> None: + super().__init__(hexpand=True, halign=Gtk.Align.START) + self.add(Workspaces()) + self.add(FocusedClient()) + + +class Center(Gtk.Box): + def __init__(self) -> None: + super().__init__() + self.add(Media()) + + +class Right(Gtk.Box): + def __init__(self) -> None: + super().__init__(hexpand=True, halign=Gtk.Align.END) + self.add(SysTray()) + self.add(Wifi()) + self.add(AudioSlider()) + self.add(BatteryLevel()) + self.add(Time()) + + +class Bar(Astal.Window): + def __init__(self, monitor: Gdk.Monitor): + super().__init__( + anchor=Astal.WindowAnchor.LEFT + | Astal.WindowAnchor.RIGHT + | Astal.WindowAnchor.TOP, + gdkmonitor=monitor, + exclusivity=Astal.Exclusivity.EXCLUSIVE, + ) + + Astal.widget_set_class_names(self, ["Bar"]) + + self.add( + Astal.CenterBox( + start_widget=Left(), + center_widget=Center(), + end_widget=Right(), + ) + ) + + self.show_all() diff --git a/examples/py/starter-bar/widget/__init__.py b/examples/py/simple-bar/widget/__init__.py index e69de29..e69de29 100644 --- a/examples/py/starter-bar/widget/__init__.py +++ b/examples/py/simple-bar/widget/__init__.py diff --git a/examples/py/starter-bar/README.md b/examples/py/starter-bar/README.md deleted file mode 100644 index b3a14ce..0000000 --- a/examples/py/starter-bar/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Starter Bar Example - -A starter bar that shows a label and the date. - -> [!NOTE] -> This code is not ideal to work with as it requires too much boilerplate compared to AGS. -> If you want to use python try picking up the [python library](https://github.com/Aylur/astal/tree/feat/python/core/python). diff --git a/examples/py/starter-bar/style.css b/examples/py/starter-bar/style.css deleted file mode 100644 index cb8c712..0000000 --- a/examples/py/starter-bar/style.css +++ /dev/null @@ -1,4 +0,0 @@ -window.Bar { - background-color: #212121; - color: white; -} diff --git a/examples/py/starter-bar/versions.py b/examples/py/starter-bar/versions.py deleted file mode 100644 index a5993be..0000000 --- a/examples/py/starter-bar/versions.py +++ /dev/null @@ -1,6 +0,0 @@ -import gi - -gi.require_version("Astal", "0.1") -gi.require_version("Gtk", "3.0") -gi.require_version("Gdk", "3.0") -gi.require_version("Gio", "2.0") diff --git a/examples/py/starter-bar/widget/Bar.py b/examples/py/starter-bar/widget/Bar.py deleted file mode 100644 index 9071169..0000000 --- a/examples/py/starter-bar/widget/Bar.py +++ /dev/null @@ -1,48 +0,0 @@ -from gi.repository import Astal, Gtk, Gdk, GLib - - -class Time(Astal.Label): - def __init__(self, format="%H:%M:%S"): - super().__init__(visible=True) - self.connect("destroy", self.on_destroy) - self.format = format - self.time = Astal.Time.interval(1000, self.on_tick) - - def on_tick(self): - datetime = GLib.DateTime.new_now_local() - assert datetime - time = datetime.format(self.format) - assert time - self.set_label(time) - - def on_destroy(self, *args, **kwargs): - self.time.cancel() - - -class Bar(Astal.Window): - def __init__(self, monitor: Gdk.Monitor): - super().__init__( - visible=True, - gdkmonitor=monitor, - name="Bar" + str(monitor.get_model()), - anchor=Astal.WindowAnchor.LEFT - | Astal.WindowAnchor.RIGHT - | Astal.WindowAnchor.TOP, - exclusivity=Astal.Exclusivity.EXCLUSIVE, - ) - - Astal.widget_set_class_names(self, ["Bar"]) - start_widget = Astal.Box(visible=True, hexpand=True, halign=Gtk.Align.CENTER) - end_widget = Astal.Box(visible=True, hexpand=True, halign=Gtk.Align.CENTER) - - start_widget.set_children([Astal.Label(visible=True, label="Astal in python")]) - - end_widget.set_children([Time()]) - - self.add( - Astal.CenterBox( - visible=True, - start_widget=start_widget, - end_widget=end_widget, - ) - ) diff --git a/examples/vala/simple-bar/README.md b/examples/vala/simple-bar/README.md index 997e4bf..d33e57d 100644 --- a/examples/vala/simple-bar/README.md +++ b/examples/vala/simple-bar/README.md @@ -11,3 +11,4 @@ A simple bar for Hyprland using - [Network library](https://aylur.github.io/astal/libraries/network). - [Tray library](https://aylur.github.io/astal/libraries/tray). - [WirePlumber library](https://aylur.github.io/astal/libraries/wireplumber). +- [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler diff --git a/examples/vala/simple-bar/app.in.vala b/examples/vala/simple-bar/app.in.vala index 1d61b3a..aece979 100644 --- a/examples/vala/simple-bar/app.in.vala +++ b/examples/vala/simple-bar/app.in.vala @@ -1,6 +1,11 @@ class App : Astal.Application { public static App instance; + public override void request (string msg, SocketConnection conn) { + print(@"$msg\n"); + Astal.write_sock.begin(conn, "hello"); + } + public override void activate () { foreach (var mon in this.monitors) add_window(new Bar(mon)); @@ -8,8 +13,17 @@ class App : Astal.Application { apply_css("@STYLE@"); } - public static int main() { - App.instance = new App(); - return App.instance.run(null); + public static void main(string[] args) { + var instance_name = "simple-bar"; + + App.instance = new App() { + instance_name = instance_name + }; + + if (App.instance.acquire_socket()) { + App.instance.run(null); + } else { + print(Astal.Application.send_message(instance_name, string.joinv(" ", args))); + } } } diff --git a/examples/vala/simple-bar/flake.lock b/examples/vala/simple-bar/flake.lock new file mode 100644 index 0000000..06f572f --- /dev/null +++ b/examples/vala/simple-bar/flake.lock @@ -0,0 +1,62 @@ +{ + "nodes": { + "astal": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1727022015, + "narHash": "sha256-ka7aRbReUE6ImjQV8KabMHoojUgb3gtn1/9drMFTtBk=", + "owner": "aylur", + "repo": "astal", + "rev": "8cab7d039e2cf783033a5f1f26cf8be42b0d158e", + "type": "github" + }, + "original": { + "owner": "aylur", + "repo": "astal", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1716293225, + "narHash": "sha256-pU9ViBVE3XYb70xZx+jK6SEVphvt7xMTbm6yDIF4xPs=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3eaeaeb6b1e08a016380c279f8846e0bd8808916", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1726937504, + "narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "9357f4f23713673f310988025d9dc261c20e70c6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "astal": "astal", + "nixpkgs": "nixpkgs_2" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/examples/vala/simple-bar/widget/Bar.vala b/examples/vala/simple-bar/widget/Bar.vala index 438e8b7..6e99327 100644 --- a/examples/vala/simple-bar/widget/Bar.vala +++ b/examples/vala/simple-bar/widget/Bar.vala @@ -137,10 +137,9 @@ class SysTray : Gtk.Box { } void remove_item(string id) { - if (!items.contains(id)) - return; - - items.remove(id); + if (items.contains(id)) { + items.remove(id); + } } } diff --git a/lib/hyprland/hyprland.vala b/lib/hyprland/hyprland.vala index 5359d2e..3886486 100644 --- a/lib/hyprland/hyprland.vala +++ b/lib/hyprland/hyprland.vala @@ -308,6 +308,7 @@ public class Hyprland : Object { switch (args[0]) { case "workspacev2": + yield sync_workspaces(); focused_workspace = get_workspace(int.parse(args[1])); break; diff --git a/lib/river/include/astal-river.h.in b/lib/river/include/astal-river.h.in index d2b769c..a8f58e6 100644 --- a/lib/river/include/astal-river.h.in +++ b/lib/river/include/astal-river.h.in @@ -26,6 +26,8 @@ gchar *astal_river_output_get_focused_view(AstalRiverOutput *self); guint astal_river_output_get_focused_tags(AstalRiverOutput *self); +void astal_river_output_set_focused_tags(AstalRiverOutput* self, guint tags); + guint astal_river_output_get_urgent_tags(AstalRiverOutput *self); guint astal_river_output_get_occupied_tags(AstalRiverOutput *self); diff --git a/lib/river/include/river-private.h b/lib/river/include/river-private.h index 14cd1c5..8b5292c 100644 --- a/lib/river/include/river-private.h +++ b/lib/river/include/river-private.h @@ -5,11 +5,14 @@ #include "astal-river.h" #include "river-status-unstable-v1-client.h" +#include "river-control-unstable-v1-client.h" G_BEGIN_DECLS AstalRiverOutput *astal_river_output_new(guint id, struct wl_output *wl_output, struct zriver_status_manager_v1 *status_manager, + struct zriver_control_v1 *river_control, + struct wl_seat* seat, struct wl_display *wl_display); struct wl_output *astal_river_output_get_wl_output(AstalRiverOutput *self); diff --git a/lib/river/src/river-output.c b/lib/river/src/river-output.c index dc5f17d..078efb9 100644 --- a/lib/river/src/river-output.c +++ b/lib/river/src/river-output.c @@ -1,5 +1,7 @@ #include <gio/gio.h> +#include "glib-object.h" +#include "glib.h" #include "river-private.h" #include "river-status-unstable-v1-client.h" @@ -17,6 +19,8 @@ struct _AstalRiverOutput { typedef struct { struct zriver_status_manager_v1* river_status_manager; struct zriver_output_status_v1* river_output_status; + struct zriver_control_v1* river_control; + struct wl_seat* seat; struct wl_display* wl_display; struct wl_output* wl_output; } AstalRiverOutputPrivate; @@ -101,6 +105,25 @@ void astal_river_output_set_focused_view(AstalRiverOutput* self, const gchar* fo } /** + * astal_river_output_set_focused_tags + * @self: the AstalRiverOutput object + * @tags: the tagmask to be focused + * + * sets the focused tags of the output + * + */ +void astal_river_output_set_focused_tags(AstalRiverOutput* self, guint tags) { + AstalRiverOutputPrivate* priv = astal_river_output_get_instance_private(self); + gchar *tagstring = g_strdup_printf("%i", tags); + + zriver_control_v1_add_argument(priv->river_control, "set-focused-tags"); + zriver_control_v1_add_argument(priv->river_control, tagstring); + + zriver_control_v1_run_command(priv->river_control, priv->seat); + g_free(tagstring); +} + +/** * astal_river_output_get_focused_tags * @self: the AstalRiverOutput object * @@ -110,6 +133,7 @@ void astal_river_output_set_focused_view(AstalRiverOutput* self, const gchar* fo */ guint astal_river_output_get_focused_tags(AstalRiverOutput* self) { return self->focused_tags; } + /** * astal_river_output_get_urgent_tags * @self: the AstalRiverOutput object @@ -176,6 +200,9 @@ static void astal_river_output_set_property(GObject* object, guint property_id, self->id = g_value_get_uint(value); g_object_notify(G_OBJECT(self), "id"); break; + case ASTAL_RIVER_OUTPUT_PROP_FOCUSED_TAGS: + astal_river_output_set_focused_tags(self, g_value_get_uint(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -276,6 +303,8 @@ static void astal_river_output_init(AstalRiverOutput* self) {} AstalRiverOutput* astal_river_output_new(guint id, struct wl_output* wl_output, struct zriver_status_manager_v1* status_manager, + struct zriver_control_v1* river_control, + struct wl_seat* seat, struct wl_display* wl_display) { AstalRiverOutput* self = g_object_new(ASTAL_RIVER_TYPE_OUTPUT, NULL); AstalRiverOutputPrivate* priv = astal_river_output_get_instance_private(self); @@ -284,6 +313,8 @@ AstalRiverOutput* astal_river_output_new(guint id, struct wl_output* wl_output, priv->wl_display = wl_display; priv->wl_output = wl_output; priv->river_status_manager = status_manager; + priv->river_control = river_control; + priv->seat = seat; priv->river_output_status = zriver_status_manager_v1_get_river_output_status(priv->river_status_manager, wl_output); @@ -308,7 +339,7 @@ static void astal_river_output_class_init(AstalRiverOutputClass* class) { * The currently focused tags */ astal_river_output_properties[ASTAL_RIVER_OUTPUT_PROP_FOCUSED_TAGS] = g_param_spec_uint( - "focused-tags", "focused-tags", "currently focused tags", 0, INT_MAX, 0, G_PARAM_READABLE); + "focused-tags", "focused-tags", "currently focused tags", 0, INT_MAX, 0, G_PARAM_READWRITE); /** * AstalRiverOutput:occupied-tags: * diff --git a/lib/river/src/river.c b/lib/river/src/river.c index edf6af3..7b4f4f5 100644 --- a/lib/river/src/river.c +++ b/lib/river/src/river.c @@ -247,7 +247,7 @@ static void global_registry_handler(void* data, struct wl_registry* registry, ui if (priv->river_status_manager == NULL) return; struct wl_output* wl_out = wl_registry_bind(registry, id, &wl_output_interface, 4); AstalRiverOutput* output = - astal_river_output_new(id, wl_out, priv->river_status_manager, priv->display); + astal_river_output_new(id, wl_out, priv->river_status_manager, priv->river_control, priv->seat, priv->display); self->outputs = g_list_append(self->outputs, output); g_object_notify(G_OBJECT(self), "outputs"); |