sideleft: create popdown window
This commit is contained in:
parent
31bc49a97a
commit
ad22dbdfeb
12 changed files with 598 additions and 21 deletions
16
config.ts
16
config.ts
|
|
@ -84,3 +84,19 @@ export const weather = {
|
|||
location: "", // Location as a string or empty to autodetect
|
||||
imperial: false,
|
||||
};
|
||||
|
||||
export const cpu = {
|
||||
interval: 2000,
|
||||
};
|
||||
|
||||
export const gpu = {
|
||||
interval: 2000,
|
||||
};
|
||||
|
||||
export const memory = {
|
||||
interval: 5000,
|
||||
};
|
||||
|
||||
export const storage = {
|
||||
interval: 5000,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -114,14 +114,5 @@
|
|||
color: scheme.$red;
|
||||
font-weight: bold;
|
||||
font-size: lib.s(16);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: scheme.$surface1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: scheme.$surface2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@
|
|||
@forward "networks";
|
||||
@forward "media";
|
||||
@forward "sideright";
|
||||
@forward "sideleft";
|
||||
|
|
|
|||
131
scss/popdowns/sideleft.scss
Normal file
131
scss/popdowns/sideleft.scss
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
@use "sass:color";
|
||||
@use "../scheme";
|
||||
@use "../lib";
|
||||
@use "../font";
|
||||
|
||||
$-accent: scheme.$yellow;
|
||||
|
||||
.sideleft {
|
||||
@include lib.rounded(8);
|
||||
@include lib.border($-accent, 0.4, 2);
|
||||
@include lib.shadow;
|
||||
@include font.mono;
|
||||
|
||||
background-color: scheme.$crust;
|
||||
color: $-accent;
|
||||
padding: lib.s(12);
|
||||
font-size: lib.s(14);
|
||||
|
||||
@include lib.spacing(10, true);
|
||||
|
||||
& > * {
|
||||
@include lib.rounded(5);
|
||||
|
||||
background-color: scheme.$base;
|
||||
padding: lib.s(16) lib.s(20);
|
||||
}
|
||||
|
||||
.user {
|
||||
@include lib.spacing(15);
|
||||
|
||||
.face {
|
||||
@include lib.rounded(1000);
|
||||
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
min-width: lib.s(48);
|
||||
min-height: lib.s(48);
|
||||
font-size: lib.s(24);
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: lib.s(14);
|
||||
|
||||
@include lib.spacing(3, true);
|
||||
|
||||
.name {
|
||||
font-size: lib.s(18);
|
||||
color: scheme.$text;
|
||||
}
|
||||
}
|
||||
|
||||
.power {
|
||||
@include lib.element-decel;
|
||||
@include font.icon;
|
||||
|
||||
font-size: lib.s(24);
|
||||
color: scheme.$red;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: color.mix(scheme.$red, scheme.$base, 80%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: color.mix(scheme.$red, scheme.$base, 60%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.locations {
|
||||
@include lib.spacing(30);
|
||||
|
||||
& > * {
|
||||
@include lib.spacing(8, true);
|
||||
}
|
||||
|
||||
$-colours: scheme.$pink, scheme.$yellow, scheme.$maroon, scheme.$green, scheme.$mauve, scheme.$lavender;
|
||||
@for $i from 1 through 6 {
|
||||
.loc#{$i} {
|
||||
@include lib.element-decel;
|
||||
|
||||
color: nth($-colours, $i);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: color.mix(nth($-colours, $i), scheme.$base, 80%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: color.mix(nth($-colours, $i), scheme.$base, 60%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hw-resources {
|
||||
@include lib.spacing(10, true);
|
||||
|
||||
.resource {
|
||||
font-size: lib.s(13);
|
||||
|
||||
.inner {
|
||||
font-size: lib.s(16);
|
||||
|
||||
@include lib.spacing(20);
|
||||
}
|
||||
|
||||
.slider {
|
||||
@include lib.rounded(5);
|
||||
@include lib.fluent-decel(1000ms);
|
||||
|
||||
min-height: lib.s(10);
|
||||
background-color: scheme.$surface0;
|
||||
color: scheme.$red;
|
||||
}
|
||||
|
||||
$-names: gpu, cpu, memory, storage;
|
||||
$-colours: scheme.$green, scheme.$maroon, scheme.$yellow, scheme.$pink;
|
||||
@for $i from 1 through length($-names) {
|
||||
&.#{nth($-names, $i)} {
|
||||
color: nth($-colours, $i);
|
||||
|
||||
.slider {
|
||||
color: nth($-colours, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
import type { Monitor } from "@/services/monitors";
|
||||
import Players from "@/services/players";
|
||||
import Updates from "@/services/updates";
|
||||
import { getAppCategoryIcon } from "@/utils/icons";
|
||||
import { ellipsize } from "@/utils/strings";
|
||||
import { bindCurrentTime, osIcon } from "@/utils/system";
|
||||
import { setupCustomTooltip } from "@/utils/widgets";
|
||||
import type PopupWindow from "@/widgets/popupwindow";
|
||||
import { execAsync, register, Variable } from "astal";
|
||||
import { bind, kebabify } from "astal/binding";
|
||||
import { App, Astal, astalify, Gdk, Gtk, type ConstructProps } from "astal/gtk3";
|
||||
import { bar as config } from "config";
|
||||
import AstalBluetooth from "gi://AstalBluetooth";
|
||||
import AstalHyprland from "gi://AstalHyprland";
|
||||
import AstalNetwork from "gi://AstalNetwork";
|
||||
import AstalNotifd from "gi://AstalNotifd";
|
||||
import AstalTray from "gi://AstalTray";
|
||||
import AstalWp01 from "gi://AstalWp";
|
||||
import { bar as config } from "../../config";
|
||||
import type { Monitor } from "../services/monitors";
|
||||
import Players from "../services/players";
|
||||
import Updates from "../services/updates";
|
||||
import { getAppCategoryIcon } from "../utils/icons";
|
||||
import { ellipsize } from "../utils/strings";
|
||||
import { bindCurrentTime, osIcon } from "../utils/system";
|
||||
import { setupCustomTooltip } from "../utils/widgets";
|
||||
import type PopupWindow from "../widgets/popupwindow";
|
||||
|
||||
const hyprland = AstalHyprland.get_default();
|
||||
|
||||
|
|
@ -44,7 +44,13 @@ const togglePopup = (self: JSX.Element, event: Astal.ClickEvent, name: string) =
|
|||
}
|
||||
};
|
||||
|
||||
const OSIcon = () => <label className="module os-icon" label={osIcon} />;
|
||||
const OSIcon = () => (
|
||||
<button
|
||||
className="module os-icon"
|
||||
label={osIcon}
|
||||
onClick={(self, event) => event.button === Astal.MouseButton.PRIMARY && togglePopup(self, event, "sideleft")}
|
||||
/>
|
||||
);
|
||||
|
||||
const ActiveWindow = () => (
|
||||
<box
|
||||
|
|
@ -453,7 +459,11 @@ const DateTime = () => (
|
|||
);
|
||||
|
||||
const Power = () => (
|
||||
<button className="module power" label="power_settings_new" onClicked={() => App.toggle_window("session")} />
|
||||
<button
|
||||
className="module power"
|
||||
label="power_settings_new"
|
||||
onClick={(_, event) => event.button === Astal.MouseButton.PRIMARY && App.toggle_window("session")}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ({ monitor }: { monitor: Monitor }) => (
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import BluetoothDevices from "./bluetoothdevices";
|
|||
import Media from "./media";
|
||||
import Networks from "./networks";
|
||||
import Notifications from "./notifications";
|
||||
import SideLeft from "./sideleft";
|
||||
import SideRight from "./sideright";
|
||||
import Updates from "./updates";
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ export default () => {
|
|||
<Networks />;
|
||||
<Media />;
|
||||
<SideRight />;
|
||||
<SideLeft />;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
|||
203
src/modules/popdowns/sideleft.tsx
Normal file
203
src/modules/popdowns/sideleft.tsx
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
import Cpu from "@/services/cpu";
|
||||
import Gpu from "@/services/gpu";
|
||||
import Memory from "@/services/memory";
|
||||
import Storage from "@/services/storage";
|
||||
import { osId } from "@/utils/system";
|
||||
import PopupWindow from "@/widgets/popupwindow";
|
||||
import { bind, execAsync, GLib, type Binding } from "astal";
|
||||
import { App, Gtk, type Widget } from "astal/gtk3";
|
||||
import type cairo from "cairo";
|
||||
|
||||
const fmt = (bytes: number, pow: number) => +(bytes / 1024 ** pow).toFixed(2);
|
||||
const format = ({ total, used }: { total: number; used: number }) => {
|
||||
if (total >= 1024 ** 4) return `${fmt(used, 4)}/${fmt(total, 4)} TiB`;
|
||||
if (total >= 1024 ** 3) return `${fmt(used, 3)}/${fmt(total, 3)} GiB`;
|
||||
if (total >= 1024 ** 2) return `${fmt(used, 2)}/${fmt(total, 2)} MiB`;
|
||||
if (total >= 1024) return `${fmt(used, 1)}/${fmt(total, 1)} KiB`;
|
||||
return `${used}/${total} B`;
|
||||
};
|
||||
|
||||
const User = () => (
|
||||
<box className="user">
|
||||
<box
|
||||
valign={Gtk.Align.CENTER}
|
||||
className="face"
|
||||
css={`
|
||||
background-image: url("${HOME}/.face");
|
||||
`}
|
||||
>
|
||||
{!GLib.file_test(HOME + "/.face", GLib.FileTest.EXISTS) && (
|
||||
<label
|
||||
setup={self => {
|
||||
const name = GLib.get_real_name();
|
||||
if (name !== "Unknown")
|
||||
self.label = name
|
||||
.split(" ")
|
||||
.map(s => s[0].toUpperCase())
|
||||
.join("");
|
||||
else self.label = "";
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</box>
|
||||
<box vertical hexpand valign={Gtk.Align.CENTER} className="details">
|
||||
<label xalign={0} className="name" label={GLib.get_user_name()} />
|
||||
<label xalign={0} label={(GLib.getenv("XDG_CURRENT_DESKTOP") ?? osId).toUpperCase()} />
|
||||
</box>
|
||||
<button
|
||||
valign={Gtk.Align.CENTER}
|
||||
className="power"
|
||||
cursor="pointer"
|
||||
onClicked={() => App.toggle_window("session")}
|
||||
label="power_settings_new"
|
||||
/>
|
||||
</box>
|
||||
);
|
||||
|
||||
const QuickLaunch = () => (
|
||||
<box className="quick-launch">
|
||||
<box vertical>
|
||||
<box>{/* <button */}</box> // TODO
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
|
||||
const Location = ({ home, label, num }: { home?: boolean; label: string; num: number }) => (
|
||||
<button
|
||||
className={"loc" + num}
|
||||
cursor="pointer"
|
||||
onClicked={self => {
|
||||
self.get_toplevel().hide();
|
||||
execAsync(`xdg-open ${HOME}/${home ? "" : label.split(" ").at(-1) + "/"}`).catch(console.error);
|
||||
}}
|
||||
>
|
||||
<label xalign={0} label={label} />
|
||||
</button>
|
||||
);
|
||||
|
||||
const Locations = () => (
|
||||
<box className="locations">
|
||||
<box vertical>
|
||||
<Location label=" Downloads" num={1} />
|
||||
<Location label=" Documents" num={2} />
|
||||
<Location label=" Music" num={3} />
|
||||
</box>
|
||||
<box vertical>
|
||||
<Location label=" Pictures" num={4} />
|
||||
<Location label=" Videos" num={5} />
|
||||
<Location label=" Home" num={6} home />
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
|
||||
const Slider = ({ value }: { value: Binding<number> }) => (
|
||||
<drawingarea
|
||||
hexpand
|
||||
valign={Gtk.Align.CENTER}
|
||||
className="slider"
|
||||
css={bind(value).as(v => `font-size: ${v}px;`)}
|
||||
setup={self => {
|
||||
const halfPi = Math.PI / 2;
|
||||
|
||||
const styleContext = self.get_style_context();
|
||||
self.set_size_request(-1, styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number);
|
||||
|
||||
self.connect("draw", (_, cr: cairo.Context) => {
|
||||
const styleContext = self.get_style_context();
|
||||
|
||||
const width = self.get_allocated_width();
|
||||
const height = styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number;
|
||||
self.set_size_request(-1, height);
|
||||
|
||||
const progressValue = styleContext.get_property("font-size", Gtk.StateFlags.NORMAL) as number;
|
||||
let radius = styleContext.get_property("border-radius", Gtk.StateFlags.NORMAL) as number;
|
||||
|
||||
const bg = styleContext.get_background_color(Gtk.StateFlags.NORMAL);
|
||||
cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
|
||||
|
||||
// Background
|
||||
cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
|
||||
cr.arc(width - radius, radius, radius, -halfPi, 0); // Top right
|
||||
cr.arc(width - radius, height - radius, radius, 0, halfPi); // Bottom right
|
||||
cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
|
||||
cr.fill();
|
||||
|
||||
// Flatten when near 0
|
||||
radius = Math.min(radius, Math.min(width * progressValue, height) / 2);
|
||||
|
||||
const progressPosition = width * progressValue - radius;
|
||||
const fg = styleContext.get_color(Gtk.StateFlags.NORMAL);
|
||||
cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
|
||||
|
||||
// Foreground
|
||||
cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
|
||||
cr.arc(progressPosition, radius, radius, -halfPi, 0); // Top right
|
||||
cr.arc(progressPosition, height - radius, radius, 0, halfPi); // Bottom right
|
||||
cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
|
||||
cr.fill();
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const Resource = ({
|
||||
icon,
|
||||
name,
|
||||
value,
|
||||
labelSetup,
|
||||
}: {
|
||||
icon: string;
|
||||
name: string;
|
||||
value: Binding<number>;
|
||||
labelSetup?: (self: Widget.Label) => void;
|
||||
}) => (
|
||||
<box vertical className={`resource ${name}`}>
|
||||
<box className="inner">
|
||||
<label label={icon} />
|
||||
<Slider value={value.as(v => v / 100)} />
|
||||
</box>
|
||||
<label halign={Gtk.Align.END} label={labelSetup ? "" : value.as(v => `${+v.toFixed(2)}%`)} setup={labelSetup} />
|
||||
</box>
|
||||
);
|
||||
|
||||
const HwResources = () => (
|
||||
<box vertical className="hw-resources">
|
||||
{Gpu.get_default().available && <Resource icon="" name="gpu" value={bind(Gpu.get_default(), "usage")} />}
|
||||
<Resource icon="" name="cpu" value={bind(Cpu.get_default(), "usage")} />
|
||||
<Resource
|
||||
icon=""
|
||||
name="memory"
|
||||
value={bind(Memory.get_default(), "usage")}
|
||||
labelSetup={self => {
|
||||
const mem = Memory.get_default();
|
||||
const update = () => (self.label = format(mem));
|
||||
self.hook(mem, "notify::used", update);
|
||||
self.hook(mem, "notify::total", update);
|
||||
update();
|
||||
}}
|
||||
/>
|
||||
<Resource
|
||||
icon=""
|
||||
name="storage"
|
||||
value={bind(Storage.get_default(), "usage")}
|
||||
labelSetup={self => {
|
||||
const storage = Storage.get_default();
|
||||
const update = () => (self.label = format(storage));
|
||||
self.hook(storage, "notify::used", update);
|
||||
self.hook(storage, "notify::total", update);
|
||||
update();
|
||||
}}
|
||||
/>
|
||||
</box>
|
||||
);
|
||||
|
||||
export default () => (
|
||||
<PopupWindow name="sideleft">
|
||||
<box vertical className="sideleft">
|
||||
<User />
|
||||
{/* <QuickLaunch /> */}
|
||||
<Locations />
|
||||
<HwResources />
|
||||
</box>
|
||||
</PopupWindow>
|
||||
);
|
||||
45
src/services/cpu.ts
Normal file
45
src/services/cpu.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { GObject, interval, property, register } from "astal";
|
||||
import { cpu as config } from "config";
|
||||
import GTop from "gi://GTop";
|
||||
|
||||
@register({ GTypeName: "Cpu" })
|
||||
export default class Cpu extends GObject.Object {
|
||||
static instance: Cpu;
|
||||
static get_default() {
|
||||
if (!this.instance) this.instance = new Cpu();
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
#previous: GTop.glibtop_cpu = new GTop.glibtop_cpu();
|
||||
#usage: number = 0;
|
||||
|
||||
@property(Number)
|
||||
get usage() {
|
||||
return this.#usage;
|
||||
}
|
||||
|
||||
calculateUsage() {
|
||||
const current = new GTop.glibtop_cpu();
|
||||
GTop.glibtop_get_cpu(current);
|
||||
|
||||
// Calculate the differences from the previous to current data
|
||||
const total = current.total - this.#previous.total;
|
||||
const idle = current.idle - this.#previous.idle;
|
||||
|
||||
this.#previous = current;
|
||||
|
||||
return total > 0 ? ((total - idle) / total) * 100 : 0;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.#usage = this.calculateUsage();
|
||||
this.notify("usage");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
interval(config.interval, () => this.update());
|
||||
}
|
||||
}
|
||||
55
src/services/gpu.ts
Normal file
55
src/services/gpu.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { execAsync, Gio, GObject, interval, property, register } from "astal";
|
||||
import { gpu as config } from "config";
|
||||
|
||||
@register({ GTypeName: "Gpu" })
|
||||
export default class Gpu extends GObject.Object {
|
||||
static instance: Gpu;
|
||||
static get_default() {
|
||||
if (!this.instance) this.instance = new Gpu();
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
readonly available: boolean = false;
|
||||
#usage: number = 0;
|
||||
|
||||
@property(Number)
|
||||
get usage() {
|
||||
return this.#usage;
|
||||
}
|
||||
|
||||
async calculateUsage() {
|
||||
const percs = (await execAsync("fish -c 'cat /sys/class/drm/card*/device/gpu_busy_percent'")).split("\n");
|
||||
return percs.reduce((a, b) => a + parseFloat(b), 0) / percs.length;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.calculateUsage().then(usage => {
|
||||
this.#usage = usage;
|
||||
this.notify("usage");
|
||||
});
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
let enumerator = null;
|
||||
try {
|
||||
enumerator = Gio.File.new_for_path("/sys/class/drm").enumerate_children(
|
||||
Gio.FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
Gio.FileQueryInfoFlags.NONE,
|
||||
null
|
||||
);
|
||||
} catch {}
|
||||
|
||||
let info: Gio.FileInfo | undefined | null;
|
||||
while ((info = enumerator?.next_file(null))) {
|
||||
if (/card[0-9]+/.test(info.get_name())) {
|
||||
this.available = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.available) interval(config.interval, () => this.update());
|
||||
}
|
||||
}
|
||||
60
src/services/memory.ts
Normal file
60
src/services/memory.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { GObject, interval, property, readFileAsync, register } from "astal";
|
||||
import { memory as config } from "config";
|
||||
|
||||
@register({ GTypeName: "Memory" })
|
||||
export default class Memory extends GObject.Object {
|
||||
static instance: Memory;
|
||||
static get_default() {
|
||||
if (!this.instance) this.instance = new Memory();
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
#total: number = 0;
|
||||
#free: number = 0;
|
||||
#used: number = 0;
|
||||
#usage: number = 0;
|
||||
|
||||
@property(Number)
|
||||
get total() {
|
||||
return this.#total;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get free() {
|
||||
return this.#free;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get used() {
|
||||
return this.#used;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get usage() {
|
||||
return this.#usage;
|
||||
}
|
||||
|
||||
async update() {
|
||||
const info = await readFileAsync("/proc/meminfo");
|
||||
this.#total = parseInt(info.match(/MemTotal:\s+(\d+)/)?.[1] ?? "0", 10) * 1024;
|
||||
this.#free = parseInt(info.match(/MemAvailable:\s+(\d+)/)?.[1] ?? "0", 10) * 1024;
|
||||
|
||||
if (isNaN(this.#total)) this.#total = 0;
|
||||
if (isNaN(this.#free)) this.#free = 0;
|
||||
|
||||
this.#used = this.#total - this.#free;
|
||||
this.#usage = this.#total > 0 ? (this.#used / this.#total) * 100 : 0;
|
||||
|
||||
this.notify("total");
|
||||
this.notify("free");
|
||||
this.notify("used");
|
||||
this.notify("usage");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
interval(config.interval, () => this.update().catch(console.error));
|
||||
}
|
||||
}
|
||||
61
src/services/storage.ts
Normal file
61
src/services/storage.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { GObject, interval, property, register } from "astal";
|
||||
import { storage as config } from "config";
|
||||
import GTop from "gi://GTop";
|
||||
|
||||
@register({ GTypeName: "Storage" })
|
||||
export default class Storage extends GObject.Object {
|
||||
static instance: Storage;
|
||||
static get_default() {
|
||||
if (!this.instance) this.instance = new Storage();
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
#total: number = 0;
|
||||
#free: number = 0;
|
||||
#used: number = 0;
|
||||
#usage: number = 0;
|
||||
|
||||
@property(Number)
|
||||
get total() {
|
||||
return this.#total;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get free() {
|
||||
return this.#free;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get used() {
|
||||
return this.#used;
|
||||
}
|
||||
|
||||
@property(Number)
|
||||
get usage() {
|
||||
return this.#usage;
|
||||
}
|
||||
|
||||
update() {
|
||||
const root = new GTop.glibtop_fsusage();
|
||||
GTop.glibtop_get_fsusage(root, "/");
|
||||
const home = new GTop.glibtop_fsusage();
|
||||
GTop.glibtop_get_fsusage(home, "/home");
|
||||
|
||||
this.#total = root.blocks * root.block_size + home.blocks * home.block_size;
|
||||
this.#free = root.bavail * root.block_size + home.bavail * home.block_size;
|
||||
this.#used = this.#total - this.#free;
|
||||
this.#usage = this.#total > 0 ? (this.#used / this.#total) * 100 : 0;
|
||||
|
||||
this.notify("total");
|
||||
this.notify("free");
|
||||
this.notify("used");
|
||||
this.notify("usage");
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
interval(config.interval, () => this.update());
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,9 @@
|
|||
"jsxImportSource": "/usr/share/astal/gjs/gtk3",
|
||||
"paths": {
|
||||
"astal": ["/usr/share/astal/gjs"],
|
||||
"astal/*": ["/usr/share/astal/gjs/*"]
|
||||
"astal/*": ["/usr/share/astal/gjs/*"],
|
||||
"config": ["./config"],
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue