diff options
Diffstat (limited to 'lib')
71 files changed, 1017 insertions, 346 deletions
diff --git a/lib/apps/default.nix b/lib/apps/default.nix new file mode 100644 index 0000000..64466a2 --- /dev/null +++ b/lib/apps/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-apps"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "apps"; + gir-suffix = "Apps"; + authors = "Aylur"; + description = "Application query library"; +} diff --git a/lib/apps/fuzzy.vala b/lib/apps/fuzzy.vala index 7745320..590112e 100644 --- a/lib/apps/fuzzy.vala +++ b/lib/apps/fuzzy.vala @@ -2,15 +2,15 @@ namespace AstalApps { private int fuzzy_match_string(string pattern, string str) { const int unmatched_letter_penalty = -1; int score = 100; + int not_found_score = -10; if (pattern.length == 0) return score; - if (str.length < pattern.length) return int.MIN; + if (str.length < pattern.length) return not_found_score; bool found = fuzzy_match_recurse(pattern, str, score, true, out score); score += unmatched_letter_penalty * (str.length - pattern.length); - if(!found) score = -10; - + if(!found) score = not_found_score; return score; } diff --git a/lib/astal/gtk3/default.nix b/lib/astal/gtk3/default.nix new file mode 100644 index 0000000..c86f3de --- /dev/null +++ b/lib/astal/gtk3/default.nix @@ -0,0 +1,21 @@ +{ + mkAstalPkg, + pkgs, + self, +}: +mkAstalPkg { + pname = "astal3"; + src = ./.; + packages = [ + self.packages.${pkgs.system}.io + pkgs.gtk3 + pkgs.gtk-layer-shell + ]; + + libname = "astal3"; + gir-suffix = ""; + authors = "Aylur"; + description = "Astal GTK3 widget library"; + dependencies = ["AstalIO-0.1" "Gtk-3.0"]; + repo-path = "astal/gtk3"; +} diff --git a/lib/astal/gtk3/src/config.vala.in b/lib/astal/gtk3/src/config.vala.in index 88bfe9c..a141571 100644 --- a/lib/astal/gtk3/src/config.vala.in +++ b/lib/astal/gtk3/src/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "Astal", gir_version = "@API_VERSION@")] namespace Astal { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/astal/gtk3/src/meson.build b/lib/astal/gtk3/src/meson.build index bf8f72a..2587b10 100644 --- a/lib/astal/gtk3/src/meson.build +++ b/lib/astal/gtk3/src/meson.build @@ -10,6 +10,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/astal/gtk3/src/widget/circularprogress.vala b/lib/astal/gtk3/src/widget/circularprogress.vala index df1635d..de7a5c7 100644 --- a/lib/astal/gtk3/src/widget/circularprogress.vala +++ b/lib/astal/gtk3/src/widget/circularprogress.vala @@ -41,7 +41,7 @@ public class Astal.CircularProgress : Gtk.Bin { } static construct { - set_css_name("circular-progress"); + set_css_name("circularprogress"); } public override Gtk.SizeRequestMode get_request_mode() { diff --git a/lib/astal/gtk3/src/widget/icon.vala b/lib/astal/gtk3/src/widget/icon.vala index 9a20359..ee6808c 100644 --- a/lib/astal/gtk3/src/widget/icon.vala +++ b/lib/astal/gtk3/src/widget/icon.vala @@ -9,7 +9,21 @@ public class Astal.Icon : Gtk.Image { private double size { get; set; default = 14; } public new Gdk.Pixbuf pixbuf { get; set; } - public GLib.Icon g_icon { get; set; } + + private static bool gicon_warned = false; + [Version (deprecated = true, deprecated_since = "0.1.0", replacement = "gicon")] + public GLib.Icon g_icon { + owned get { + return this.gicon; + } + set { + if( !gicon_warned ) { + GLib.warning("g-icon is deprecated. Use gicon instead."); + gicon_warned = true; + } + this.gicon = value; + } + } /** * Either a named icon or a path to a file. @@ -57,7 +71,6 @@ public class Astal.Icon : Gtk.Image { break; case IconType.GICON: pixel_size = (int)size; - gicon = g_icon; break; } @@ -86,7 +99,7 @@ public class Astal.Icon : Gtk.Image { display_icon.begin(); }); - notify["g-icon"].connect(() => { + notify["gicon"].connect(() => { type = IconType.GICON; display_icon.begin(); }); diff --git a/lib/astal/gtk3/src/widget/slider.vala b/lib/astal/gtk3/src/widget/slider.vala index 97cfb69..b927e32 100644 --- a/lib/astal/gtk3/src/widget/slider.vala +++ b/lib/astal/gtk3/src/widget/slider.vala @@ -30,6 +30,10 @@ public class Astal.Slider : Gtk.Scale { step = 0.05; } + if (page == 0) { + page = 0.01; + } + notify["orientation"].connect(() => { notify_property("vertical"); }); @@ -90,5 +94,13 @@ public class Astal.Slider : Gtk.Scale { set { adjustment.step_increment = value; } } + /** + * Size of page increments. Defaults to `0.01`. + */ + public double page { + get { return adjustment.page_increment; } + set { adjustment.page_increment = value; } + } + // TODO: marks } diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala index 11d542d..a066806 100644 --- a/lib/astal/gtk3/src/widget/window.vala +++ b/lib/astal/gtk3/src/widget/window.vala @@ -50,6 +50,13 @@ public class Astal.Window : Gtk.Window { private InhibitManager? inhibit_manager; private Inhibitor? inhibitor; + /** + * Get the current [[email protected]] this window resides in. + */ + public Gdk.Monitor get_current_monitor() { + return Gdk.Display.get_default().get_monitor_at_window(base.get_window()); + } + private bool check(string action) { if (!is_supported()) { critical(@"can not $action on window: layer shell not supported"); @@ -63,7 +70,7 @@ public class Astal.Window : Gtk.Window { } construct { - // If the window has no size allocatoted when it gets mapped. + // If the window has no size allocatoted when it gets mapped // It won't show up later either when it size changes by adding children. height_request = 1; width_request = 1; diff --git a/lib/astal/gtk4/default.nix b/lib/astal/gtk4/default.nix new file mode 100644 index 0000000..0bca3c0 --- /dev/null +++ b/lib/astal/gtk4/default.nix @@ -0,0 +1,21 @@ +{ + mkAstalPkg, + pkgs, + self, +}: +mkAstalPkg { + pname = "astal4"; + src = ./.; + packages = [ + self.packages.${pkgs.system}.io + pkgs.gtk4 + pkgs.gtk4-layer-shell + ]; + + libname = "astal4"; + gir-suffix = ""; + authors = "Aylur"; + description = "Astal GTK4 widget library"; + dependencies = ["AstalIO-0.1" "Gtk-4.0"]; + repo-path = "astal/gtk4"; +} diff --git a/lib/astal/gtk4/src/application.vala b/lib/astal/gtk4/src/application.vala index fadf705..99f18f8 100644 --- a/lib/astal/gtk4/src/application.vala +++ b/lib/astal/gtk4/src/application.vala @@ -143,16 +143,22 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { public void apply_css(string style, bool reset = false) { var provider = new Gtk.CssProvider(); + provider.parsing_error.connect((section, error) => { + critical("CSS Error %s:%zu:%zu: %s\n", + section.get_file()?.get_basename() ?? "", + section.get_start_location().lines + 1, + section.get_start_location().line_chars + 1, + error.message + ); + }); + if (reset) reset_css(); - try { - if (FileUtils.test(style, FileTest.EXISTS)) - provider.load_from_path(style); - else - provider.load_from_string(style); - } catch (Error err) { - critical(err.message); + if (FileUtils.test(style, FileTest.EXISTS)) { + provider.load_from_path(style); + } else { + provider.load_from_string(style); } Gtk.StyleContext.add_provider_for_display( diff --git a/lib/astal/gtk4/src/config.vala.in b/lib/astal/gtk4/src/config.vala.in index 88bfe9c..a141571 100644 --- a/lib/astal/gtk4/src/config.vala.in +++ b/lib/astal/gtk4/src/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "Astal", gir_version = "@API_VERSION@")] namespace Astal { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/astal/gtk4/src/meson.build b/lib/astal/gtk4/src/meson.build index 8aac969..58408a6 100644 --- a/lib/astal/gtk4/src/meson.build +++ b/lib/astal/gtk4/src/meson.build @@ -10,6 +10,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], @@ -25,6 +26,8 @@ deps = [ ] sources = [config] + files( + 'widget/box.vala', + 'widget/slider.vala', 'widget/window.vala', 'application.vala', ) diff --git a/lib/astal/gtk4/src/widget/box.vala b/lib/astal/gtk4/src/widget/box.vala new file mode 100644 index 0000000..28f2b00 --- /dev/null +++ b/lib/astal/gtk4/src/widget/box.vala @@ -0,0 +1,50 @@ +public class Astal.Box : Gtk.Box { + /** + * Corresponds to [[email protected] :orientation]. + */ + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } + + public List<weak Gtk.Widget> children { + set { + foreach (var child in children) { + remove(child); + } + foreach (var child in value) { + append(child); + } + } + owned get { + var list = new List<weak Gtk.Widget>(); + var child = get_first_child(); + while (child != null) { + list.append(child); + child = child.get_next_sibling(); + } + return list; + } + } + + public Gtk.Widget? child { + owned get { + foreach (var child in children) { + return child; + } + return null; + } + set { + var list = new List<weak Gtk.Widget>(); + list.append(child); + this.children = children; + } + } +} diff --git a/lib/astal/gtk4/src/widget/slider.vala b/lib/astal/gtk4/src/widget/slider.vala new file mode 100644 index 0000000..371f5be --- /dev/null +++ b/lib/astal/gtk4/src/widget/slider.vala @@ -0,0 +1,77 @@ +public class Astal.Slider : Gtk.Scale { + private Gtk.EventControllerLegacy controller; + private bool dragging; + + construct { + if (adjustment == null) + adjustment = new Gtk.Adjustment(0,0,0,0,0,0); + + if (max == 0 && min == 0) { + max = 1; + } + + if (step == 0) { + step = 0.05; + } + + if (page == 0) { + page = 0.01; + } + + controller = new Gtk.EventControllerLegacy(); + add_controller(controller); + controller.event.connect((event) => { + var type = event.get_event_type(); + if (type == Gdk.EventType.BUTTON_PRESS || + type == Gdk.EventType.KEY_PRESS || + type == Gdk.EventType.TOUCH_BEGIN) { + dragging = true; + } + if (type == Gdk.EventType.BUTTON_RELEASE || + type == Gdk.EventType.KEY_RELEASE || + type == Gdk.EventType.TOUCH_END) { + dragging = false; + } + }); + } + + /** + * Value of this slider. Defaults to `0`. + */ + public double value { + get { return adjustment.value; } + set { if (!dragging) adjustment.value = value; } + } + + /** + * Minimum possible value of this slider. Defaults to `0`. + */ + public double min { + get { return adjustment.lower; } + set { adjustment.lower = value; } + } + + /** + * Maximum possible value of this slider. Defaults to `1`. + */ + public double max { + get { return adjustment.upper; } + set { adjustment.upper = value; } + } + + /** + * Size of step increments. Defaults to `0.05`. + */ + public double step { + get { return adjustment.step_increment; } + set { adjustment.step_increment = value; } + } + + /** + * Size of page increments. Defaults to `0.01`. + */ + public double page { + get { return adjustment.page_increment; } + set { adjustment.page_increment = value; } + } +} diff --git a/lib/astal/gtk4/src/widget/window.vala b/lib/astal/gtk4/src/widget/window.vala index 0cf3d11..3b0d113 100644 --- a/lib/astal/gtk4/src/widget/window.vala +++ b/lib/astal/gtk4/src/widget/window.vala @@ -47,6 +47,13 @@ public enum Astal.Keymode { * Subclass of [[email protected]] which integrates GtkLayerShell as class fields. */ public class Astal.Window : Gtk.Window { + /** + * Get the current [[email protected]] this window resides in. + */ + public Gdk.Monitor get_current_monitor() { + return Gdk.Display.get_default().get_monitor_at_surface(base.get_surface()); + } + private bool check(string action) { if (!is_supported()) { critical(@"can not $action on window: layer shell not supported"); diff --git a/lib/astal/io/config.vala.in b/lib/astal/io/config.vala.in index fe1e450..4aee790 100644 --- a/lib/astal/io/config.vala.in +++ b/lib/astal/io/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalIO", gir_version = "@API_VERSION@")] namespace AstalIO { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/astal/io/daemon.vala b/lib/astal/io/daemon.vala new file mode 100644 index 0000000..7f3173e --- /dev/null +++ b/lib/astal/io/daemon.vala @@ -0,0 +1,88 @@ +[DBus (name="io.Astal.Application")] +public class AstalIO.Daemon : GLib.Application, AstalIO.Application { + private SocketService service; + private DBusConnection conn; + private string _instance_name = "astal"; + private string socket_path { get; private set; } + + /** + * A unique instance name. + * + * This is the identifier used by the AstalIO package and the CLI. + */ + [DBus (visible=false)] + public string instance_name { + owned get { return _instance_name; } + construct set { + _instance_name = value != null ? value : "astal"; + application_id = @"io.Astal.$_instance_name"; + } + } + + /** + * Handler for an incoming request. + * + * @param msg Body of the message + * @param conn The connection which expects the response. + */ + [DBus (visible=false)] + public virtual void request(string msg, SocketConnection conn) { + AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); + } + + /** + * Attempt to acquire the astal socket for this app identified by its [[email protected]:instance_name]. + * If the socket is in use by another app with the same name an [[email protected]_OCCUPIED] is thrown. + */ + [DBus (visible=false)] + public void acquire_socket() throws Error { + string path; + service = AstalIO.acquire_socket(this, out path); + socket_path = path; + + Bus.own_name( + BusType.SESSION, + application_id, + BusNameOwnerFlags.NONE, + (conn) => { + try { + this.conn = conn; + conn.register_object("/io/Astal/Application", this); + } catch (Error err) { + critical(err.message); + } + }, + () => {}, + () => {} + ); + } + + public void inspector() throws DBusError, IOError { + throw new DBusError.FAILED("Daemon does not implement inspector"); + } + + public void toggle_window(string window) throws DBusError, IOError { + throw new DBusError.FAILED("Daemon does not implement toggle_window"); + } + + /** + * Quit and stop the socket if it was acquired. + */ + public new void quit() throws DBusError, IOError { + if (service != null) { + service.stop(); + service.close(); + } + + base.quit(); + } + + construct { + hold(); + + shutdown.connect(() => { try { quit(); } catch(Error err) {} }); + Unix.signal_add(1, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(2, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(15, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + } +} diff --git a/lib/astal/io/default.nix b/lib/astal/io/default.nix new file mode 100644 index 0000000..c33132a --- /dev/null +++ b/lib/astal/io/default.nix @@ -0,0 +1,12 @@ +{mkAstalPkg, ...}: +mkAstalPkg { + pname = "astal"; + src = ./.; + + libname = "io"; + gir-suffix = "IO"; + authors = "Aylur"; + description = "Astal Core library"; + repo-path = "astal/io"; + website-path = "io"; +} diff --git a/lib/astal/io/file.vala b/lib/astal/io/file.vala index 57b6dc0..e8e8a8b 100644 --- a/lib/astal/io/file.vala +++ b/lib/astal/io/file.vala @@ -26,6 +26,11 @@ public async string read_file_async(string path) throws Error { */ public void write_file(string path, string content) { try { + var dir = Path.get_dirname(path); + if (!FileUtils.test(dir, FileTest.IS_DIR)) { + File.new_for_path(dir).make_directory_with_parents(null); + } + FileUtils.set_contents(path, content); } catch (Error error) { critical(error.message); @@ -36,6 +41,11 @@ public void write_file(string path, string content) { * Write content to a file asynchronously. */ public async void write_file_async(string path, string content) throws Error { + var dir = Path.get_dirname(path); + if (!FileUtils.test(dir, FileTest.IS_DIR)) { + File.new_for_path(dir).make_directory_with_parents(null); + } + yield File.new_for_path(path).replace_contents_async( content.data, null, diff --git a/lib/astal/io/meson.build b/lib/astal/io/meson.build index 023dece..9a00904 100644 --- a/lib/astal/io/meson.build +++ b/lib/astal/io/meson.build @@ -22,6 +22,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], @@ -38,6 +39,7 @@ deps = [ sources = [config] + files( 'application.vala', + 'daemon.vala', 'file.vala', 'process.vala', 'time.vala', diff --git a/lib/astal/io/process.vala b/lib/astal/io/process.vala index cfd05b9..95c67a3 100644 --- a/lib/astal/io/process.vala +++ b/lib/astal/io/process.vala @@ -2,20 +2,22 @@ * `Process` provides shortcuts for [[email protected]] with sane defaults. */ public class AstalIO.Process : Object { + public string[] argv { construct; get; } + private void read_stream(DataInputStream stream, bool err) { stream.read_line_utf8_async.begin(Priority.DEFAULT, null, (_, res) => { try { var output = stream.read_line_utf8_async.end(res); if (output != null) { - if (err) + if (err) { stdout(output.strip()); - else + } else { stderr(output.strip()); - + } read_stream(stream, err); } } catch (Error err) { - printerr("%s\n", err.message); + critical(err.message); } }); } @@ -24,20 +26,27 @@ public class AstalIO.Process : Object { private DataInputStream err_stream; private DataOutputStream in_stream; private Subprocess process; - public string[] argv { construct; get; } + /** + * When the underlying subprocess writes to its stdout. + * + * @param out Line written to stdout + */ + public signal void stdout(string out); /** - * When the underlying subprocess writes to its stdout - * this signal is emitted with that line. + * When the underlying subprocess writes to its stderr. + * + * @param err Line written to stderr */ - public signal void stdout (string out); + public signal void stderr(string err); /** - * When the underlying subprocess writes to its stderr - * this signal is emitted with that line. + * When the underlying subprocess exits or is terminated. + * + * @param code Exit code or signal number if terminated */ - public signal void stderr (string err); + public signal void exit(int code, bool terminated); /** * Force quit the subprocess. @@ -48,6 +57,8 @@ public class AstalIO.Process : Object { /** * Send a signal to the subprocess. + * + * @param signal_num Signal number to be sent */ public void signal(int signal_num) { process.send_signal(signal_num); @@ -55,6 +66,8 @@ public class AstalIO.Process : Object { /** * Write a line to the subprocess' stdin synchronously. + * + * @param in String to be written to stdin */ public void write(string in) throws Error { in_stream.put_string(in); @@ -62,6 +75,8 @@ public class AstalIO.Process : Object { /** * Write a line to the subprocess' stdin asynchronously. + * + * @param in String to be written to stdin */ public async void write_async(string in) { try { @@ -71,23 +86,47 @@ public class AstalIO.Process : Object { } } - /** - * Start a new subprocess with the given command. - * - * The first element of the vector is executed with the remaining elements as the argument list. - */ - public Process.subprocessv(string[] cmd) throws Error { + /** See [[email protected]] */ + public Process(string[] cmd) throws Error { Object(argv: cmd); - process = new Subprocess.newv(cmd, + process = new Subprocess.newv( + cmd, SubprocessFlags.STDIN_PIPE | SubprocessFlags.STDERR_PIPE | SubprocessFlags.STDOUT_PIPE ); + out_stream = new DataInputStream(process.get_stdout_pipe()); err_stream = new DataInputStream(process.get_stderr_pipe()); in_stream = new DataOutputStream(process.get_stdin_pipe()); + read_stream(out_stream, true); read_stream(err_stream, false); + + process.wait_async.begin(null, (_, res) => { + try { + process.wait_async.end(res); + } catch (Error err) { + // ignore + } + + if (process.get_if_exited()) { + exit(process.get_exit_status(), false); + } + + if (process.get_if_signaled()) { + exit(process.get_term_sig(), true); + } + }); + } + + /** + * Start a new subprocess with the given command. + * + * The first element of the vector is executed with the remaining elements as the argument list. + */ + public static Process subprocessv(string[] cmd) throws Error { + return new Process(cmd); } /** @@ -97,7 +136,7 @@ public class AstalIO.Process : Object { public static Process subprocess(string cmd) throws Error { string[] argv; Shell.parse_argv(cmd, out argv); - return new Process.subprocessv(argv); + return Process.subprocessv(argv); } /** @@ -116,11 +155,11 @@ public class AstalIO.Process : Object { string err_str, out_str; process.communicate_utf8(null, null, out out_str, out err_str); var success = process.get_successful(); - process.dispose(); - if (success) + if (success) { return out_str.strip(); - else + } else { throw new IOError.FAILED(err_str.strip()); + } } /** @@ -151,11 +190,11 @@ public class AstalIO.Process : Object { string err_str, out_str; yield process.communicate_utf8_async(null, null, out out_str, out err_str); var success = process.get_successful(); - process.dispose(); - if (success) + if (success) { return out_str.strip(); - else + } else { throw new IOError.FAILED(err_str.strip()); + } } /** diff --git a/lib/astal/io/variable.vala b/lib/astal/io/variable.vala index 312a27a..e4105f8 100644 --- a/lib/astal/io/variable.vala +++ b/lib/astal/io/variable.vala @@ -172,7 +172,7 @@ public class AstalIO.Variable : VariableBase { return_if_fail(watch_proc == null); return_if_fail(watch_exec != null); - watch_proc = new Process.subprocessv(watch_exec); + watch_proc = Process.subprocessv(watch_exec); watch_proc.stdout.connect((str) => set_closure(str, watch_transform)); watch_proc.stderr.connect((str) => this.error(str)); } diff --git a/lib/auth/default.nix b/lib/auth/default.nix new file mode 100644 index 0000000..3b8b276 --- /dev/null +++ b/lib/auth/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-auth"; + src = ./.; + packages = [pkgs.pam]; + + libname = "auth"; + gir-suffix = "Auth"; + authors = "kotontrion"; + description = "Authentication using pam"; +} diff --git a/lib/battery/config.vala.in b/lib/battery/config.vala.in index 6e7f77e..404e60a 100644 --- a/lib/battery/config.vala.in +++ b/lib/battery/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalBattery", gir_version = "@API_VERSION@")] namespace AstalBattery { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/battery/default.nix b/lib/battery/default.nix new file mode 100644 index 0000000..17bf67a --- /dev/null +++ b/lib/battery/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-battery"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "battery"; + authors = "Aylur"; + gir-suffix = "Battery"; + description = "DBus proxy for upowerd devices"; +} diff --git a/lib/battery/meson.build b/lib/battery/meson.build index 054e9db..ec8e3d0 100644 --- a/lib/battery/meson.build +++ b/lib/battery/meson.build @@ -25,6 +25,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/bluetooth/config.vala.in b/lib/bluetooth/config.vala.in index 9fce720..f43dba3 100644 --- a/lib/bluetooth/config.vala.in +++ b/lib/bluetooth/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalBluetooth", gir_version = "@API_VERSION@")] namespace AstalBluetooth { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/bluetooth/default.nix b/lib/bluetooth/default.nix new file mode 100644 index 0000000..f2d58c8 --- /dev/null +++ b/lib/bluetooth/default.nix @@ -0,0 +1,10 @@ +{mkAstalPkg, ...}: +mkAstalPkg { + pname = "astal-bluetooth"; + src = ./.; + + libname = "bluetooth"; + authors = "Aylur"; + gir-suffix = "Bluetooth"; + description = "DBus proxy for bluez"; +} diff --git a/lib/bluetooth/meson.build b/lib/bluetooth/meson.build index 5288a9e..529e85f 100644 --- a/lib/bluetooth/meson.build +++ b/lib/bluetooth/meson.build @@ -20,6 +20,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/cava/cava.c b/lib/cava/cava.c index 1c5ef66..2934335 100644 --- a/lib/cava/cava.c +++ b/lib/cava/cava.c @@ -440,7 +440,7 @@ static void astal_cava_cava_get_property(GObject* object, guint property_id, GVa g_value_set_int(value, self->bars); break; case ASTAL_CAVA_CAVA_PROP_VALUES: - g_value_set_pointer(value, self->values); + g_value_set_boxed(value, self->values); break; case ASTAL_CAVA_CAVA_PROP_AUTOSENS: g_value_set_boolean(value, self->autosens); @@ -558,7 +558,7 @@ static void astal_cava_cava_class_init(AstalCavaCavaClass* class) { * be 0. */ astal_cava_cava_properties[ASTAL_CAVA_CAVA_PROP_VALUES] = - g_param_spec_pointer("values", "values", "a list of values", G_PARAM_READABLE); + g_param_spec_boxed("values", "values", "a list of values", G_TYPE_ARRAY, G_PARAM_READABLE); /** * AstalCavaCava:active: * diff --git a/lib/cava/default.nix b/lib/cava/default.nix new file mode 100644 index 0000000..5472e8c --- /dev/null +++ b/lib/cava/default.nix @@ -0,0 +1,56 @@ +{ + mkAstalPkg, + pkgs, + ... +}: let + libcava = pkgs.stdenv.mkDerivation rec { + pname = "cava"; + version = "0.10.3"; + + src = pkgs.fetchFromGitHub { + owner = "LukashonakV"; + repo = "cava"; + rev = "0.10.3"; + hash = "sha256-ZDFbI69ECsUTjbhlw2kHRufZbQMu+FQSMmncCJ5pagg="; + }; + + buildInputs = with pkgs; [ + alsa-lib + libpulseaudio + ncurses + iniparser + sndio + SDL2 + libGL + portaudio + jack2 + pipewire + ]; + + propagatedBuildInputs = with pkgs; [ + fftw + ]; + + nativeBuildInputs = with pkgs; [ + autoreconfHook + autoconf-archive + pkgconf + meson + ninja + ]; + + preAutoreconf = '' + echo ${version} > version + ''; + }; +in + mkAstalPkg { + pname = "astal-cava"; + src = ./.; + packages = [libcava]; + + libname = "cava"; + authors = "kotontrion"; + gir-suffix = "Cava"; + description = "Audio visualization library using cava"; + } diff --git a/lib/greet/config.vala.in b/lib/greet/config.vala.in index 333d735..a7990aa 100644 --- a/lib/greet/config.vala.in +++ b/lib/greet/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalGreet", gir_version = "@API_VERSION@")] namespace AstalGreet { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/greet/default.nix b/lib/greet/default.nix new file mode 100644 index 0000000..22b9456 --- /dev/null +++ b/lib/greet/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-greet"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "greet"; + authors = "Aylur"; + gir-suffix = "Greet"; + description = "IPC client for greetd"; +} diff --git a/lib/greet/meson.build b/lib/greet/meson.build index 11321b0..8bd35d7 100644 --- a/lib/greet/meson.build +++ b/lib/greet/meson.build @@ -25,6 +25,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/hyprland/config.vala.in b/lib/hyprland/config.vala.in index 65993b2..0cdba75 100644 --- a/lib/hyprland/config.vala.in +++ b/lib/hyprland/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalHyprland", gir_version = "@API_VERSION@")] namespace AstalHyprland { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/hyprland/default.nix b/lib/hyprland/default.nix new file mode 100644 index 0000000..6b5e15d --- /dev/null +++ b/lib/hyprland/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-hyprland"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "hyprland"; + authors = "Aylur"; + gir-suffix = "Hyprland"; + description = "IPC client for Hyprland"; +} diff --git a/lib/hyprland/hyprland.vala b/lib/hyprland/hyprland.vala index 9825a91..70b5a25 100644 --- a/lib/hyprland/hyprland.vala +++ b/lib/hyprland/hyprland.vala @@ -303,6 +303,20 @@ public class Hyprland : Object { } } + private async bool try_add_client(string addr) throws Error { + if (addr == "" || get_client(addr) != null) { + return true; + } + + var client = new Client(); + _clients.insert(addr, client); + yield sync_clients(); + yield sync_workspaces(); + client_added(client); + notify_property("clients"); + return false; + } + private async void handle_event(string line) throws Error { var args = line.split(">>"); @@ -315,25 +329,11 @@ public class Hyprland : Object { case "focusedmon": var argv = args[1].split(",", 2); + yield sync_monitors(); focused_monitor = get_monitor_by_name(argv[0]); focused_workspace = get_workspace_by_name(argv[1]); break; - // first event that signals a new client - case "activewindowv2": - if (args[1] != "" && get_client(args[1]) == null) { - var client = new Client(); - _clients.insert(args[1], client); - yield sync_clients(); - yield sync_workspaces(); - client_added(client); - notify_property("clients"); - focused_client = client; - } else { - focused_client = get_client(args[1]); - } - break; - // TODO: nag vaxry for fullscreenv2 that passes address case "fullscreen": yield sync_clients(); @@ -392,9 +392,18 @@ public class Hyprland : Object { keyboard_layout(argv[0], argv[1]); break; + // first event that signals a new client when it opens as an active window + case "activewindowv2": + yield try_add_client(args[1]); + focused_client = get_client(args[1]); + break; + case "openwindow": - yield sync_clients(); - yield sync_workspaces(); + var addr = args[1].split(",")[0]; + if (yield try_add_client(addr)) { + yield sync_clients(); + yield sync_workspaces(); + } break; case "closewindow": diff --git a/lib/hyprland/meson.build b/lib/hyprland/meson.build index 7112ee1..9598ba9 100644 --- a/lib/hyprland/meson.build +++ b/lib/hyprland/meson.build @@ -25,6 +25,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/hyprland/structs.vala b/lib/hyprland/structs.vala index 25f70c3..d5180b1 100644 --- a/lib/hyprland/structs.vala +++ b/lib/hyprland/structs.vala @@ -4,12 +4,15 @@ public class Bind : Object { public bool mouse { get; construct set; } public bool release { get; construct set; } public bool repeat { get; construct set; } + public bool long_press { get; construct set; } public bool non_consuming { get; construct set; } + public bool has_description { get; construct set; } public int64 modmask { get; construct set; } public string submap { get; construct set; } public string key { get; construct set; } public int64 keycode { get; construct set; } public bool catch_all { get; construct set; } + public string description { get; construct set; } public string dispatcher { get; construct set; } public string arg { get; construct set; } @@ -18,12 +21,15 @@ public class Bind : Object { mouse = obj.get_boolean_member("mouse"); release = obj.get_boolean_member("release"); repeat = obj.get_boolean_member("repeat"); + long_press = obj.get_member("longPress")?.get_boolean() ?? false; non_consuming = obj.get_boolean_member("non_consuming"); + has_description = obj.get_member("has_description")?.get_boolean() ?? false; modmask = obj.get_int_member("modmask"); submap = obj.get_string_member("submap"); key = obj.get_string_member("key"); keycode = obj.get_int_member("keycode"); catch_all = obj.get_boolean_member("catch_all"); + description = obj.get_member("description")?.get_string() ?? ""; dispatcher = obj.get_string_member("dispatcher"); arg = obj.get_string_member("arg"); } diff --git a/lib/mpris/config.vala.in b/lib/mpris/config.vala.in index 767c4bd..f44c3dc 100644 --- a/lib/mpris/config.vala.in +++ b/lib/mpris/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalMpris", gir_version = "@API_VERSION@")] namespace AstalMpris { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/mpris/default.nix b/lib/mpris/default.nix new file mode 100644 index 0000000..082c647 --- /dev/null +++ b/lib/mpris/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-mpris"; + src = ./.; + packages = with pkgs; [gvfs json-glib]; + + libname = "mpris"; + authors = "Aylur"; + gir-suffix = "Mpris"; + description = "Control mpris players"; +} diff --git a/lib/mpris/meson.build b/lib/mpris/meson.build index bf215c9..91185c8 100644 --- a/lib/mpris/meson.build +++ b/lib/mpris/meson.build @@ -25,6 +25,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/mpris/player.vala b/lib/mpris/player.vala index c69eb21..e6d84bf 100644 --- a/lib/mpris/player.vala +++ b/lib/mpris/player.vala @@ -440,6 +440,9 @@ public class AstalMpris.Player : Object { _loop_status = Loop.from_string(proxy.loop_status); notify_property("loop-status"); } + } else { + _loop_status = Loop.UNSUPPORTED; + notify_property("loop-status"); } if (rate != proxy.rate) { @@ -452,6 +455,9 @@ public class AstalMpris.Player : Object { _shuffle_status = Shuffle.from_bool(proxy.shuffle); notify_property("shuffle-status"); } + } else { + _shuffle_status = Shuffle.UNSUPPORTED; + notify_property("shuffle-status"); } if (volume != proxy.volume) { @@ -498,8 +504,10 @@ public class AstalMpris.Player : Object { } private async void cache_cover() { - if (art_url == null || art_url == "") + if (art_url == null || art_url == "") { + cover_art = null; return; + } var file = File.new_for_uri(art_url); if (file.get_path() != null) { diff --git a/lib/network/config.vala.in b/lib/network/config.vala.in index dbec0f3..3e5beb5 100644 --- a/lib/network/config.vala.in +++ b/lib/network/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalNetwork", gir_version = "@API_VERSION@")] namespace AstalNetwork { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/network/default.nix b/lib/network/default.nix new file mode 100644 index 0000000..cfa2378 --- /dev/null +++ b/lib/network/default.nix @@ -0,0 +1,16 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-network"; + src = ./.; + packages = [pkgs.networkmanager]; + + libname = "network"; + authors = "Aylur"; + gir-suffix = "Network"; + description = "NetworkManager wrapper library"; + dependencies = ["NM-1.0"]; +} diff --git a/lib/network/meson.build b/lib/network/meson.build index 17ea358..a490179 100644 --- a/lib/network/meson.build +++ b/lib/network/meson.build @@ -20,6 +20,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/network/network.vala b/lib/network/network.vala index 96e19c8..b5a6c61 100644 --- a/lib/network/network.vala +++ b/lib/network/network.vala @@ -104,14 +104,14 @@ public enum AstalNetwork.Primary { // alias for NM.State public enum AstalNetwork.State { - UNKNOWN, - ASLEEP, - DISCONNECTED, - DISCONNECTING, - CONNECTING, - CONNECTED_LOCAL, - CONNECTED_SITE, - CONNECTED_GLOBAL; + UNKNOWN = 0, + ASLEEP = 10, + DISCONNECTED = 20, + DISCONNECTING = 30, + CONNECTING = 40, + CONNECTED_LOCAL = 50, + CONNECTED_SITE = 60, + CONNECTED_GLOBAL = 70; public string to_string() { switch (this) { diff --git a/lib/notifd/config.vala.in b/lib/notifd/config.vala.in index 752c754..04b2cd2 100644 --- a/lib/notifd/config.vala.in +++ b/lib/notifd/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalNotifd", gir_version = "@API_VERSION@")] namespace AstalNotifd { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/notifd/default.nix b/lib/notifd/default.nix new file mode 100644 index 0000000..e23528c --- /dev/null +++ b/lib/notifd/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-notifd"; + src = ./.; + packages = with pkgs; [json-glib gdk-pixbuf]; + + libname = "notifd"; + authors = "Aylur"; + gir-suffix = "Notifd"; + description = "Notification daemon library"; +} diff --git a/lib/notifd/meson.build b/lib/notifd/meson.build index 3d4de95..a9e7faf 100644 --- a/lib/notifd/meson.build +++ b/lib/notifd/meson.build @@ -26,6 +26,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/powerprofiles/cli.vala b/lib/powerprofiles/cli.vala index 1e5cc70..87ffe82 100644 --- a/lib/powerprofiles/cli.vala +++ b/lib/powerprofiles/cli.vala @@ -100,7 +100,7 @@ string to_json_string(AstalPowerProfiles.PowerProfiles profiles) { foreach (var prof in profiles.profiles) { profs.add_value(new Json.Builder() .begin_object() - .set_member_name("profie").add_string_value(prof.profile) + .set_member_name("profile").add_string_value(prof.profile) .set_member_name("driver").add_string_value(prof.driver) .set_member_name("cpu_driver").add_string_value(prof.cpu_driver) .set_member_name("platform_driver").add_string_value(prof.platform_driver) diff --git a/lib/powerprofiles/config.vala.in b/lib/powerprofiles/config.vala.in index 79034f1..cc9d309 100644 --- a/lib/powerprofiles/config.vala.in +++ b/lib/powerprofiles/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalPowerProfiles", gir_version = "@API_VERSION@")] namespace AstalPowerProfiles { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/powerprofiles/default.nix b/lib/powerprofiles/default.nix new file mode 100644 index 0000000..f35f9e5 --- /dev/null +++ b/lib/powerprofiles/default.nix @@ -0,0 +1,15 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-powerprofiles"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "powerprofiles"; + authors = "Aylur"; + gir-suffix = "PowerProfiles"; + description = "DBus proxy for upowerd profiles"; +} diff --git a/lib/powerprofiles/meson.build b/lib/powerprofiles/meson.build index cd8cc2b..4a31afa 100644 --- a/lib/powerprofiles/meson.build +++ b/lib/powerprofiles/meson.build @@ -25,6 +25,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], diff --git a/lib/powerprofiles/power-profiles.vala b/lib/powerprofiles/power-profiles.vala index a104d2e..931fc04 100644 --- a/lib/powerprofiles/power-profiles.vala +++ b/lib/powerprofiles/power-profiles.vala @@ -113,6 +113,11 @@ public class PowerProfiles : Object { owned get { return proxy.performance_degraded; } } + private string? get_hashtable_string(HashTable<string, Variant> table, string key) { + var v = table.get(key); + return v == null ? null : v.get_string(); + } + /** * List of each profile. */ @@ -122,10 +127,10 @@ public class PowerProfiles : Object { for (var i = 0; i < proxy.profiles.length; ++i) { var prof = proxy.profiles[i]; profs[i] = Profile() { - profile = prof.get("Profile").get_string(), - cpu_driver = prof.get("CpuDriver").get_string(), - platform_driver = prof.get("PlatformDriver").get_string(), - driver = prof.get("Driver").get_string() + profile = get_hashtable_string(prof, "Profile"), + cpu_driver = get_hashtable_string(prof, "CpuDriver"), + platform_driver = get_hashtable_string(prof, "PlatformDriver"), + driver = get_hashtable_string(prof, "Driver"), }; } return profs; diff --git a/lib/river/default.nix b/lib/river/default.nix new file mode 100644 index 0000000..15c6eb4 --- /dev/null +++ b/lib/river/default.nix @@ -0,0 +1,21 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-river"; + src = ./.; + packages = [pkgs.json-glib]; + + libname = "river"; + authors = "kotontrion"; + gir-suffix = "River"; + description = "IPC client for River"; + + postUnpack = '' + rm -rf $sourceRoot/subprojects + mkdir -p $sourceRoot/subprojects + cp -r --remove-destination ${../wayland-glib} $sourceRoot/subprojects/wayland-glib + ''; +} diff --git a/lib/river/include/wayland-source.h b/lib/river/include/wayland-source.h deleted file mode 100644 index b219589..0000000 --- a/lib/river/include/wayland-source.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __WAYLAND_SOURCE_H__ -#define __WAYLAND_SOURCE_H__ - -#include <glib-object.h> - -G_BEGIN_DECLS - -typedef struct _WLSource WLSource; - -WLSource* wl_source_new(); -void wl_source_free(WLSource* self); -struct wl_display* wl_source_get_display(WLSource* source); - -G_END_DECLS - -#endif /* __WAYLAND_SOURCE_H__ */ diff --git a/lib/river/meson.build b/lib/river/meson.build index aa5e23b..2f98315 100644 --- a/lib/river/meson.build +++ b/lib/river/meson.build @@ -13,6 +13,9 @@ lib_so_version = version_split[0] + '.' + version_split[1] pkg_config = import('pkgconfig') gnome = import('gnome') +wayland_glib = subproject('wayland-glib') +wayland_glib_dep = wayland_glib.get_variable('wayland_glib') + subdir('protocols') subdir('include') subdir('src') diff --git a/lib/river/src/meson.build b/lib/river/src/meson.build index b7ce20d..9c58e41 100644 --- a/lib/river/src/meson.build +++ b/lib/river/src/meson.build @@ -1,7 +1,6 @@ srcs = files( 'river-output.c', - 'river.c', - 'wayland-source.c', + 'river.c' ) deps = [ @@ -9,6 +8,7 @@ deps = [ dependency('gio-2.0'), dependency('wayland-client'), dependency('json-glib-1.0'), + wayland_glib_dep ] astal_river_lib = library( diff --git a/lib/river/src/river.c b/lib/river/src/river.c index 124cb20..6f94c9c 100644 --- a/lib/river/src/river.c +++ b/lib/river/src/river.c @@ -6,7 +6,8 @@ #include "river-control-unstable-v1-client.h" #include "river-private.h" #include "river-status-unstable-v1-client.h" -#include "wayland-source.h" +// #include "wayland-source.h" +#include <wayland-glib.h> struct _AstalRiverRiver { GObject parent_instance; @@ -22,7 +23,7 @@ typedef struct { struct wl_registry* wl_registry; struct wl_seat* seat; struct wl_display* display; - WLSource* wl_source; + WlGlibWlSource* wl_source; struct zriver_status_manager_v1* river_status_manager; struct zriver_control_v1* river_control; struct zriver_seat_status_v1* river_seat_status; @@ -340,7 +341,7 @@ static gboolean astal_river_river_initable_init(GInitable* initable, GCancellabl if (priv->init) return TRUE; - priv->wl_source = wl_source_new(NULL, NULL); + priv->wl_source = wl_glib_wl_source_new(); if (priv->wl_source == NULL) { g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -348,7 +349,7 @@ static gboolean astal_river_river_initable_init(GInitable* initable, GCancellabl return FALSE; } - priv->display = wl_source_get_display(priv->wl_source); + priv->display = priv->wl_source->display; priv->wl_registry = wl_display_get_registry(priv->display); wl_registry_add_listener(priv->wl_registry, ®istry_listener, self); @@ -449,7 +450,8 @@ static void astal_river_river_finalize(GObject* object) { if (priv->seat != NULL) wl_seat_destroy(priv->seat); if (priv->display != NULL) wl_display_flush(priv->display); - if (priv->wl_source != NULL) wl_source_free(priv->wl_source); + // if (priv->wl_source != NULL) wl_source_free(priv->wl_source); + if (priv->wl_source != NULL) g_source_unref((GSource*)(priv->wl_source)); g_free(self->focused_view); g_free(self->focused_output); diff --git a/lib/river/src/wayland-source.c b/lib/river/src/wayland-source.c deleted file mode 100644 index 875c32c..0000000 --- a/lib/river/src/wayland-source.c +++ /dev/null @@ -1,104 +0,0 @@ - -#include "wayland-source.h" - -#include <errno.h> -#include <glib.h> -#include <wayland-client.h> - -struct _WLSource { - GSource source; - struct wl_display *display; - gpointer fd; - int error; -}; - -static gboolean wl_source_prepare(GSource *source, gint *timeout) { - WLSource *self = (WLSource *)source; - - *timeout = 0; - if (wl_display_prepare_read(self->display) != 0) - return TRUE; - else if (wl_display_flush(self->display) < 0) { - self->error = errno; - return TRUE; - } - *timeout = -1; - return FALSE; -} - -static gboolean wl_source_check(GSource *source) { - WLSource *self = (WLSource *)source; - - if (self->error > 0) return TRUE; - - GIOCondition revents; - revents = g_source_query_unix_fd(source, self->fd); - - if (revents & G_IO_IN) { - if (wl_display_read_events(self->display) < 0) self->error = errno; - } else - wl_display_cancel_read(self->display); - - return revents > 0; -} - -static gboolean wl_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { - WLSource *self = (WLSource *)source; - GIOCondition revents; - - revents = g_source_query_unix_fd(source, self->fd); - if ((self->error > 0) || (revents & (G_IO_ERR | G_IO_HUP))) { - errno = self->error; - self->error = 0; - if (callback != NULL) return callback(user_data); - return G_SOURCE_REMOVE; - } - - if (wl_display_dispatch_pending(self->display) < 0) { - if (callback != NULL) return callback(user_data); - return G_SOURCE_REMOVE; - } - - return G_SOURCE_CONTINUE; -} - -static void wl_source_finalize(GSource *source) { - WLSource *self = (WLSource *)source; - wl_display_disconnect(self->display); -} - -static GSourceFuncs wl_source_funcs = { - .prepare = wl_source_prepare, - .check = wl_source_check, - .dispatch = wl_source_dispatch, - .finalize = wl_source_finalize, -}; - -WLSource *wl_source_new() { - struct wl_display *display; - WLSource *self; - GSource *source; - - display = wl_display_connect(NULL); - if (display == NULL) return NULL; - - source = g_source_new(&wl_source_funcs, sizeof(WLSource)); - self = (WLSource *)source; - self->display = display; - - self->fd = g_source_add_unix_fd(source, wl_display_get_fd(self->display), - G_IO_IN | G_IO_ERR | G_IO_HUP); - - g_source_attach(source, NULL); - - return self; -} - -void wl_source_free(WLSource *self) { - GSource *source = (GSource *)self; - g_return_if_fail(source != NULL); - g_source_destroy(source); - g_source_unref(source); -} - -struct wl_display *wl_source_get_display(WLSource *self) { return self->display; } diff --git a/lib/river/subprojects/wayland-glib b/lib/river/subprojects/wayland-glib new file mode 120000 index 0000000..41372cf --- /dev/null +++ b/lib/river/subprojects/wayland-glib @@ -0,0 +1 @@ +../../wayland-glib
\ No newline at end of file diff --git a/lib/tray/config.vala.in b/lib/tray/config.vala.in index 8ef8498..6a46bec 100644 --- a/lib/tray/config.vala.in +++ b/lib/tray/config.vala.in @@ -1,3 +1,4 @@ +[CCode (gir_namespace = "AstalTray", gir_version = "@API_VERSION@")] namespace AstalTray { public const int MAJOR_VERSION = @MAJOR_VERSION@; public const int MINOR_VERSION = @MINOR_VERSION@; diff --git a/lib/tray/default.nix b/lib/tray/default.nix new file mode 100644 index 0000000..6dd0d61 --- /dev/null +++ b/lib/tray/default.nix @@ -0,0 +1,41 @@ +{ + mkAstalPkg, + pkgs, + ... +}: let + vala-panel-appmenu = pkgs.fetchFromGitLab { + owner = "vala-panel-project"; + repo = "vala-panel-appmenu"; + rev = "24.05"; + hash = "sha256-8GWauw7r3zKhvGF2TNOI8GDVctUFDhtG/Vy1cNUpsVo="; + }; + + appmenu-glib-translator = pkgs.stdenv.mkDerivation { + pname = "appmenu-glib-translator"; + version = "24.05"; + + src = "${vala-panel-appmenu}/subprojects/appmenu-glib-translator"; + + buildInputs = with pkgs; [ + glib + ]; + + nativeBuildInputs = with pkgs; [ + gobject-introspection + meson + pkg-config + ninja + vala + ]; + }; +in + mkAstalPkg { + pname = "astal-tray"; + src = ./.; + packages = [pkgs.json-glib appmenu-glib-translator]; + + libname = "tray"; + authors = "kotontrion"; + gir-suffix = "Tray"; + description = "StatusNotifierItem implementation"; + } diff --git a/lib/tray/meson.build b/lib/tray/meson.build index fbf2f98..4fca2e4 100644 --- a/lib/tray/meson.build +++ b/lib/tray/meson.build @@ -26,6 +26,7 @@ config = configure_file( input: 'config.vala.in', output: 'config.vala', configuration: { + 'API_VERSION': api_version, 'VERSION': meson.project_version(), 'MAJOR_VERSION': version_split[0], 'MINOR_VERSION': version_split[1], @@ -39,29 +40,9 @@ deps = [ dependency('gio-2.0'), dependency('json-glib-1.0'), dependency('gdk-pixbuf-2.0'), - dependency('gtk+-3.0'), + dependency('appmenu-glib-translator') ] -dbusmenu_cflags = run_command( - find_program('pkg-config', required: true), - '--cflags', 'dbusmenu-gtk3-0.4', - 'gobject-introspection-1.0', - 'gobject-2.0', - 'glib-2.0', - capture: true, - check: true, -).stdout().strip() - -dbusmenu_libs = run_command( - find_program('pkg-config', required: true), - '--libs', 'dbusmenu-gtk3-0.4', - 'gobject-introspection-1.0', - 'gobject-2.0', - 'glib-2.0', - capture: true, - check: true, -).stdout().strip() - sources = [config] + files('tray.vala', 'watcher.vala', 'trayItem.vala') if get_option('lib') @@ -71,15 +52,13 @@ if get_option('lib') dependencies: deps, vala_header: meson.project_name() + '.h', vala_vapi: meson.project_name() + '-' + api_version + '.vapi', - vala_args: ['--vapi-comments', '--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'], + vala_args: ['--vapi-comments'], version: meson.project_version(), - c_args: dbusmenu_cflags.split(' '), - link_args: dbusmenu_libs.split(' '), install: true, install_dir: [true, true, true] ) - pkgs = ['--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'] + pkgs = [] foreach dep : deps pkgs += ['--pkg=' + dep.name()] endforeach @@ -120,31 +99,13 @@ if get_option('lib') requires: deps, install_dir: get_option('libdir') / 'pkgconfig', ) - # - # custom_target( - # typelib, - # command: [ - # find_program('g-ir-compiler'), - # '--output', '@OUTPUT@', - # '--shared-library', get_option('prefix') / get_option('libdir') / '@PLAINNAME@', - # meson.current_build_dir() / gir, - # ], - # input: lib, - # output: typelib, - # depends: lib, - # install: true, - # install_dir: get_option('libdir') / 'girepository-1.0', - # ) -endif + endif if get_option('cli') executable( meson.project_name(), ['cli.vala', sources], dependencies: deps, - vala_args: ['--pkg', 'DbusmenuGtk3-0.4', '--pkg', 'Dbusmenu-0.4'], - c_args: dbusmenu_cflags.split(' '), - link_args: dbusmenu_libs.split(' '), install: true, ) endif diff --git a/lib/tray/trayItem.vala b/lib/tray/trayItem.vala index db0e6d4..6da51cb 100644 --- a/lib/tray/trayItem.vala +++ b/lib/tray/trayItem.vala @@ -1,5 +1,3 @@ -using DbusmenuGtk; - namespace AstalTray { public struct Pixmap { int width; @@ -88,21 +86,20 @@ public class TrayItem : Object { private IItem proxy; private List<ulong> connection_ids; - /** The Title of the TrayItem */ public string title { owned get { return proxy.Title; } } - + /** The category this item belongs to */ public Category category { get { return proxy.Category; } } - - /** the current status of this item */ + + /** The current status of this item */ public Status status { get { return proxy.Status; } } - - /** the tooltip of this item */ + + /** The tooltip of this item */ public Tooltip? tooltip { owned get { return proxy.ToolTip; } } - - /** - * a markup representation of the tooltip. This is basically equvivalent + + /** + * A markup representation of the tooltip. This is basically equvivalent * to `tooltip.title \n tooltip.description` */ public string tooltip_markup { @@ -120,26 +117,24 @@ public class TrayItem : Object { /** the id of the item. This id is specified by the tray app.*/ public string id { owned get { return proxy.Id ;} } - - /** - * If set, this only supports the menu, so showing the menu should be prefered + + /** + * If set, this only supports the menu, so showing the menu should be prefered * over calling [[email protected]]. */ public bool is_menu { get { return proxy.ItemIsMenu ;} } - - /** - * the icon theme path, where to look for the [[email protected]:icon-name]. - * - * It is recommended to use the [[email protected]:gicon] property, + + /** + * The icon theme path, where to look for the [[email protected]:icon-name]. + * It is recommended to use the [[email protected]:gicon] property, * which does the icon lookups for you. */ public string icon_theme_path { owned get { return proxy.IconThemePath ;} } - - /** - * the name of the icon. This should be looked up in the [[email protected]:icon-theme-path] - * if set or in the currently used icon theme otherwise. - * - * It is recommended to use the [[email protected]:gicon] property, + + /** + * The name of the icon. This should be looked up in the [[email protected]:icon-theme-path] + * if set or in the currently used icon theme otherwise. + * It is recommended to use the [[email protected]:gicon] property, * which does the icon lookups for you. */ public string icon_name { @@ -149,26 +144,41 @@ public class TrayItem : Object { : proxy.IconName; } } - - /** - * a pixbuf containing the icon. - * - * It is recommended to use the [[email protected]:gicon] property, + + /** + * A pixbuf containing the icon. + * It is recommended to use the [[email protected]:gicon] property, * which does the icon lookups for you. */ public Gdk.Pixbuf icon_pixbuf { owned get { return _get_icon_pixbuf(); } } /** - * contains the items icon. This property is intended to be used with the gicon property + * Contains the items icon. This property is intended to be used with the gicon property * of the Icon widget and the recommended way to display the icon. - * This property unifies the [[email protected]:icon-name], + * This property unifies the [[email protected]:icon-name], * [[email protected]:icon-theme-path] and [[email protected]:icon-pixbuf] properties. */ public GLib.Icon gicon { get; private set; } - - /** the id of the item used to uniquely identify the TrayItems by this lib.*/ + + /** The id of the item used to uniquely identify the TrayItems by this lib.*/ public string item_id { get; private set; } + private DBusMenu.Importer menu_importer; + + public MenuModel? menu_model { + owned get { + if (menu_importer == null) return null; + return menu_importer.model; + } + } + + public ActionGroup? action_group { + owned get { + if (menu_importer == null) return null; + return menu_importer.action_group; + } + } + public signal void changed(); public signal void ready(); @@ -183,7 +193,8 @@ public class TrayItem : Object { proxy = yield Bus.get_proxy( BusType.SESSION, service, - path); + path + ); connection_ids.append(proxy.NewStatus.connect(refresh_all_properties)); connection_ids.append(proxy.NewToolTip.connect(refresh_all_properties)); @@ -196,9 +207,16 @@ public class TrayItem : Object { SignalHandler.disconnect(proxy, id); } }); - - update_gicon(); + if (proxy.Menu != null) { + this.menu_importer = new DBusMenu.Importer(proxy.get_name_owner(), proxy.Menu); + this.menu_importer.notify["model"].connect(() => { + notify_property("menu-model"); + notify_property("action-group"); + }); + } + + update_gicon(); ready(); } catch (Error err) { critical(err.message); @@ -206,28 +224,33 @@ public class TrayItem : Object { } private void _notify() { - string[] props = { "category", "id", "title", "status", "is-menu", "tooltip-markup", "icon-name", "icon-pixbuf" }; - - foreach (string prop in props) + string[] props = { + "category", + "id", + "title", + "status", + "is-menu", + "tooltip-markup", + "icon-name", + "icon-pixbuf" + }; + + foreach (string prop in props) { notify_property(prop); + } changed(); } private void update_gicon() { - if(icon_name != null && icon_name != "") { - if(icon_theme_path != null && icon_theme_path != "") { - - Gtk.IconTheme icon_theme = new Gtk.IconTheme(); - string[] paths = {icon_theme_path}; - icon_theme.set_search_path(paths); - - int size = icon_theme.get_icon_sizes(icon_name)[0]; - Gtk.IconInfo icon_info = icon_theme.lookup_icon( - icon_name, size, Gtk.IconLookupFlags.FORCE_SIZE); - - if (icon_info != null) - gicon = new GLib.FileIcon(GLib.File.new_for_path(icon_info.get_filename())); + if (icon_name != null && icon_name != "") { + if (GLib.FileUtils.test(icon_name, GLib.FileTest.EXISTS)) { + gicon = new GLib.FileIcon(GLib.File.new_for_path(icon_name)); + } + else if(icon_theme_path != null && icon_theme_path != "") { + gicon = new GLib.FileIcon(GLib.File.new_for_path( + find_icon_in_theme(icon_name, icon_theme_path) + )); } else { gicon = new GLib.ThemedIcon(icon_name); } @@ -274,33 +297,60 @@ public class TrayItem : Object { } ); } - + + /** + * tells the tray app that its menu is about to be opened, + * so it can update the menu if needed. You should call this method + * before openening the menu. + */ + public void about_to_show() { + if(proxy.Menu == null) return; + try { + Bus.get_sync(BusType.SESSION).call_sync( + this.proxy.g_name_owner, + this.proxy.Menu, + "com.canonical.dbusmenu", + "AboutToShow", + new Variant("(i)", 0), + null, + DBusCallFlags.NONE, + -1, + null + ); + } + catch (Error r) { + //silently ignore + } + } + /** - * send an activate request to the tray app. + * Send an activate request to the tray app. */ public void activate(int x, int y) { try { proxy.Activate(x, y); } catch (Error e) { - if(e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) + if (e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) { warning(e.message); + } } } /** - * send a secondary activate request to the tray app. + * Send a secondary activate request to the tray app. */ public void secondary_activate(int x, int y) { try { proxy.SecondaryActivate(x, y); } catch (Error e) { - if(e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) + if (e.domain != DBusError.quark() || e.code != DBusError.UNKNOWN_METHOD) { warning(e.message); + } } } /** - * send a scroll request to the tray app. + * Send a scroll request to the tray app. * valid values for the orientation are "horizontal" and "vertical". */ public void scroll(int delta, string orientation) { @@ -312,58 +362,44 @@ public class TrayItem : Object { } } - /** - * creates a new Gtk Menu for this item. - */ - public Gtk.Menu? create_menu() { - if (proxy.Menu == null) + private string? find_icon_in_theme(string icon_name, string theme_path){ + if (icon_name == null || theme_path == null || icon_name == "" || theme_path == "") { return null; + } - return new DbusmenuGtk.Menu( - proxy.get_name_owner(), - proxy.Menu); - } - - private Gdk.Pixbuf? _get_icon_pixbuf() { - Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION - ? proxy.AttentionIconPixmap - : proxy.IconPixmap; - - - string icon_name = proxy.Status == Status.NEEDS_ATTENTION - ? proxy.AttentionIconName - : proxy.IconName; - - Gdk.Pixbuf pixbuf = null; - - if (icon_name != null && proxy.IconThemePath != null) - pixbuf = load_from_theme(icon_name, proxy.IconThemePath); + try { + Dir dir = Dir.open(theme_path, 0); + string? name = null; - if (pixbuf == null) - pixbuf = pixmap_to_pixbuf(pixmaps); + while ((name = dir.read_name ()) != null) { + var path = Path.build_filename(theme_path, name); - return pixbuf; - } + if (FileUtils.test(path, FileTest.IS_DIR)) { + string? icon = find_icon_in_theme(icon_name, path); + if (icon != null) return icon; + else continue; + } - private Gdk.Pixbuf? load_from_theme(string icon_name, string theme_path) { - if (theme_path == "" || theme_path == null) - return null; + int dot_index = name.last_index_of("."); + if (dot_index != -1) { + name = name.substring(0, dot_index); + } - if (icon_name == "" || icon_name == null) + if (name == icon_name) return path; + } + } catch (FileError err) { return null; + } + return null; - Gtk.IconTheme icon_theme = new Gtk.IconTheme(); - string[] paths = {theme_path}; - icon_theme.set_search_path(paths); - - int size = icon_theme.get_icon_sizes(icon_name)[0]; - Gtk.IconInfo icon_info = icon_theme.lookup_icon( - icon_name, size, Gtk.IconLookupFlags.FORCE_SIZE); + } - if (icon_info != null) - return icon_info.load_icon(); + private Gdk.Pixbuf? _get_icon_pixbuf() { + Pixmap[] pixmaps = proxy.Status == Status.NEEDS_ATTENTION + ? proxy.AttentionIconPixmap + : proxy.IconPixmap; - return null; + return pixmap_to_pixbuf(pixmaps); } private Gdk.Pixbuf? pixmap_to_pixbuf(Pixmap[] pixmaps) { @@ -371,6 +407,12 @@ public class TrayItem : Object { return null; Pixmap pixmap = pixmaps[0]; + + for (int i = 0; i < pixmaps.length; i++){ + if(pixmap.width < pixmaps[i].width) + pixmap = pixmaps[i]; + }; + uint8[] image_data = pixmap.bytes.copy(); for (int i = 0; i < pixmap.width * pixmap.height * 4; i += 4) { diff --git a/lib/wayland-glib/meson.build b/lib/wayland-glib/meson.build new file mode 100644 index 0000000..3d93ac0 --- /dev/null +++ b/lib/wayland-glib/meson.build @@ -0,0 +1,40 @@ +project( + 'wayland-glib', + 'vala', + 'c', + version: run_command('cat', join_paths(meson.project_source_root(), 'version')).stdout().strip(), + meson_version: '>= 0.62.0', + default_options: [ + 'warning_level=2', + 'werror=false', + 'c_std=gnu11', + ], +) + +version_split = meson.project_version().split('.') + +deps = [ + dependency('glib-2.0'), + dependency('gio-2.0'), + dependency('gobject-2.0'), + dependency('wayland-client'), +] + +sources = [ + 'wl-source.vala', +] + +lib = static_library( + meson.project_name(), + sources, + dependencies: deps, + vala_header: meson.project_name() + '.h', + vala_vapi: meson.project_name() + '.vapi', +) + +wayland_glib = declare_dependency( + link_with: lib, + include_directories: include_directories('.') + ) + + diff --git a/lib/wayland-glib/version b/lib/wayland-glib/version new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/lib/wayland-glib/version @@ -0,0 +1 @@ +0.1.0 diff --git a/lib/wayland-glib/wl-source.vala b/lib/wayland-glib/wl-source.vala new file mode 100644 index 0000000..f8472fb --- /dev/null +++ b/lib/wayland-glib/wl-source.vala @@ -0,0 +1,44 @@ +namespace WlGlib { +public class WlSource : Source { + + public Wl.Display display; + public void* fd; + public int error; + + public override bool dispatch(SourceFunc callback) { + IOCondition revents = this.query_unix_fd(this.fd); + if (this.error > 0 || (revents & (IOCondition.ERR | IOCondition.HUP)) != 0) { + errno = this.error; + if(callback != null) return callback(); + return Source.REMOVE; + } + if (((revents & IOCondition.IN) != 0) && this.display.dispatch() < 0) { + if(callback != null) return callback(); + return Source.REMOVE; + } + return Source.CONTINUE; + } + + public override bool check() { + IOCondition revents = this.query_unix_fd(this.fd); + return revents > 0; + } + + public override bool prepare(out int timeout) { + if(this.display.flush() < 0) + this.error = errno; + timeout = -1; + return false; + } + + public WlSource() { + base(); + this.display = new Wl.Display.connect(null); + if(this.display == null) return; + this.fd = this.add_unix_fd(this.display.get_fd(), + IOCondition.IN | IOCondition.ERR | IOCondition.HUP); + this.attach(null); + } +} +} + diff --git a/lib/wireplumber/default.nix b/lib/wireplumber/default.nix new file mode 100644 index 0000000..f9eb832 --- /dev/null +++ b/lib/wireplumber/default.nix @@ -0,0 +1,16 @@ +{ + mkAstalPkg, + pkgs, + ... +}: +mkAstalPkg { + pname = "astal-wireplumber"; + src = ./.; + packages = [pkgs.wireplumber]; + + libname = "wireplumber"; + authors = "kotontrion"; + gir-suffix = "Wp"; + description = "Wrapper library over the wireplumber API"; + dependencies = ["WP-0.5"]; +} |