diff options
Diffstat (limited to 'lib/astal')
-rw-r--r-- | lib/astal/gtk3/default.nix | 21 | ||||
-rw-r--r-- | lib/astal/gtk3/src/config.vala.in | 1 | ||||
-rw-r--r-- | lib/astal/gtk3/src/meson.build | 1 | ||||
-rw-r--r-- | lib/astal/gtk3/src/widget/circularprogress.vala | 2 | ||||
-rw-r--r-- | lib/astal/gtk3/src/widget/icon.vala | 19 | ||||
-rw-r--r-- | lib/astal/gtk3/src/widget/slider.vala | 12 | ||||
-rw-r--r-- | lib/astal/gtk3/src/widget/window.vala | 9 | ||||
-rw-r--r-- | lib/astal/gtk4/default.nix | 21 | ||||
-rw-r--r-- | lib/astal/gtk4/src/application.vala | 20 | ||||
-rw-r--r-- | lib/astal/gtk4/src/config.vala.in | 1 | ||||
-rw-r--r-- | lib/astal/gtk4/src/meson.build | 3 | ||||
-rw-r--r-- | lib/astal/gtk4/src/widget/box.vala | 50 | ||||
-rw-r--r-- | lib/astal/gtk4/src/widget/slider.vala | 77 | ||||
-rw-r--r-- | lib/astal/gtk4/src/widget/window.vala | 7 | ||||
-rw-r--r-- | lib/astal/io/config.vala.in | 1 | ||||
-rw-r--r-- | lib/astal/io/daemon.vala | 88 | ||||
-rw-r--r-- | lib/astal/io/default.nix | 12 | ||||
-rw-r--r-- | lib/astal/io/file.vala | 10 | ||||
-rw-r--r-- | lib/astal/io/meson.build | 2 | ||||
-rw-r--r-- | lib/astal/io/process.vala | 89 | ||||
-rw-r--r-- | lib/astal/io/variable.vala | 2 |
21 files changed, 410 insertions, 38 deletions
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)); } |