commit 70de7c449177afdef6e55d685e9777cfe0dec73e Author: efim Date: Sun Jul 7 17:31:51 2024 +0000 let's do ugly versioning in my playground diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dac1f48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/*~ +/.direnv/ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..721892c --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1719597372, + "narHash": "sha256-SpZKwy6sjgH00xiYmfjVAX+JaNkPQ2Q/BrEhNtWJvH4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "dd66e39ec4c7252fab7e6f8fd57b5ab2a3c7d63c", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..7872598 --- /dev/null +++ b/flake.nix @@ -0,0 +1,33 @@ +{ + description = "common lisp dbus playground"; + inputs.nixpkgs.url = "github:nixos/nixpkgs"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + # https://nixos.org/manual/nixpkgs/stable/#lisp-overriding-package-attributes + # getting version with automatic introspection mixin + dbus' = pkgs.sbcl.pkgs.dbus.overrideLispAttrs (oldAttrs: rec { + version = "1.4"; + src = pkgs.fetchFromGitHub { + owner = "death"; + repo = "dbus"; + rev = "master"; + hash = "sha256-xbg3tPYfRNGJo+9F/58w2bDeZqV33Z871+ClSg4ACPk="; + }; + }); + sbcl' = pkgs.sbcl.withPackages (ps: [ ps.alexandria dbus' ]); + in + { + devShells.default = pkgs.mkShell { + buildInputs = [ + sbcl' + ]; + }; + } + ); + # see https://serokell.io/blog/practical-nix-flakes +} diff --git a/listen-to-responses.lisp b/listen-to-responses.lisp new file mode 100644 index 0000000..77aba9c --- /dev/null +++ b/listen-to-responses.lisp @@ -0,0 +1,30 @@ +;;;; +----------------------------------------------------------------+ +;;;; | DBUS | +;;;; +----------------------------------------------------------------+ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'dbus) +(defpackage #:listening-response + (:use #:cl #:dbus) + (:export #:publish-example)) + +(in-package #:listening-response) + +(define-dbus-object root + (:path "/")) + +(define-dbus-signal-handler (root response) () + (:interface "org.freedesktop.portal.Request") + (format t "Got signal with arg ~S~%" "yup") + (force-output)) + +(defun listen-for-responses () + (handler-case + (with-open-bus (bus (session-server-addresses)) + (format t "Bus connection name: ~A~%" (bus-name bus)) + (dbus:add-match bus :type :signal :interface "org.freedesktop.portal.Request") + (publish-objects bus)) + (end-of-file () + :disconnected-by-bus))) ; org.freedesktop.portal.Request +;; yep, works with +;; dbus-send --session --type=signal / org.freedesktop.portal.Request.Response +;; again, will need to register object on the expected response path diff --git a/maybe-screencast.lisp b/maybe-screencast.lisp new file mode 100644 index 0000000..1925638 --- /dev/null +++ b/maybe-screencast.lisp @@ -0,0 +1,119 @@ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'dbus) + +(defpackage #:screencasting (:use #:cl)) +(in-package #:screencasting) + +;; https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html +(defconstant +screen-cast-interface+ "org.freedesktop.portal.ScreenCast") +(defconstant +request-interface+ "org.freedesktop.portal.ScreenCast") + +;; i'm not sure how to do interactive calls, +;; since maybe i need to "publish objects" +;; and that means the callbacks are set in stone? +;; i guess i could try to do a Notification call in the callback on screenshot request? +(with-open-bus (bus (session-server-addresses)) + (with-introspected-object (notification bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications") + (notification "org.freedesktop.Notifications" "Notify" + "Test" 0 "" "Test" "This is a test; I repeat, this is a test." '() '() -1) + )) + +(defun send-notification (bus message) + (dbus:with-introspected-object (notification bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications") + (notification "org.freedesktop.Notifications" "Notify" + "Test" 0 "" "Test" (or message "This is a test; I repeat, this is a test.") '() '() -1))) + +;; i guess i could pass both bus and + +(defun call-screencast () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (let* + ((requester-name (cl-ppcre:regex-replace "\\." (dbus:bus-name bus) "_" :start 1)) + (request-name "yayay") + (resp-path (concatenate 'string "/org/freedesktop/portal/desktop/request/" + requester-name + "/" + request-name))) + (dbus:define-dbus-object request-object + (:path resp-path)) + + (dbus:define-dbus-signal-handler (request-object response) ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface +request-interface+) + (format t "Got response ~S with results ~S~%" id results) + (send-notification bus "yayaya from the first response")) + + (format T "Will try to listen on ~A~%" resp-path) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop +screen-cast-interface+ "CreateSession" + '(("handle_token" ((:string) "yayay")) + ("session_handle_token" ((:string) "hohoyyy"))))) + (dbus:publish-objects bus))) + (end-of-file () + :disconnected-by-bus))) + +;; let's then try to move macroses that define 'request' to outside? +;; they would take bus, create expected request-response listener + +(alexandria:with-gensyms (lala) + (format nil "i-got-gensym-~S" lala)) + +;; well this notification library has interactive "register callback" +;; for multiple callbacks on Notification signals +;; https://github.com/Lautaro-Garcia/cl-notify/blob/main/src/signals.lisp +;; basicly loop starts and one signal-handler that calls all currently registered +;; i guess for my case i'll want separate with-open-bus? +;; how would i then exit the publish-objects loop? +;; am i really expected to exit it? +;; alright, let's try to start publish objects and after that registering stuff +;; to do the call? +(defun do-request (bus) + (let* + ((requester-name (cl-ppcre:regex-replace "\\." (dbus:bus-name bus) "_" :start 1)) + (request-name "yayay") + (resp-path (concatenate 'string "/org/freedesktop/portal/desktop/request/" + requester-name + "/" + request-name))) + (dbus:define-dbus-object request-object + (:path resp-path)) + (dbus:define-dbus-signal-handler (request-object response) ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface "org.freedesktop.portal.Request") + (format t "Got response ~A with results ~A~%" id results) + (send-notification bus "yayaya from the first response")) + (format T "Will try to make a call for ~A~%" resp-path) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + "" + '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:publish-objects bus))) + +(defvar *running-bus*) +(do-request *running-bus*) + +(defun run-bus () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (setq *running-bus* bus) + (dbus:publish-objects bus)) + (end-of-file () + :disconnected-by-bus))) + +;; well, i suppose i would need to open multiple connections? +;; let's do something like this https://github.com/Lautaro-Garcia/cl-notify/blob/main/src/dbus-protocol.lisp#L27 +;; (defmacro with-dbus-method-inovaction ((result-var method &rest args) &body body) +;; (alexandria:with-gensyms (bus notifications-object) +;; `(dbus:with-open-bus (,bus (dbus:session-server-addresses)) +;; (dbus:with-introspected-object (,notifications-object ,bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications") +;; (let ((,result-var (,notifications-object "org.freedesktop.Notifications" ,method ,@args))) +;; ,@body))))) + +;; but. this supposes result-var being a resulting value +;; and i'd want to what? pass in a callback to register? diff --git a/maybe-screenshot.lisp b/maybe-screenshot.lisp new file mode 100644 index 0000000..ed0d65b --- /dev/null +++ b/maybe-screenshot.lisp @@ -0,0 +1,377 @@ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'dbus) + +(defpackage #:screenshotting (:use #:cl)) +(in-package #:screenshotting) + +;; let's try to initiate and get screenshot somehow +;; # + +(iolib/multiplex:with-event-base (event-base) + (dbus:with-open-connection (connection event-base (dbus:session-server-addresses)) + (dbus:authenticate (dbus:supported-authentication-mechanisms connection) connection) + (dbus:hello connection) + (let ((obj (dbus:make-object-from-introspection connection "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop"))) + (dbus:list-interface-methods (dbus:object-interface "org.freedesktop.portal.Screenshot" obj))))) + +(defun start () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (format T "Bus connection name ~A~%" (dbus:bus-name bus))) + (end-of-file () + :disconnected-by-bus))) + +;; https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html +;; Since version 0.9 of xdg-desktop-portal, the handle will be of the form +;;/org/freedesktop/portal/desktop/request/SENDER/TOKEN +;; so for me it would be (dbus:bus-name bus) and "param-i-ve-passed" + +;; a question aboud argument with vararg array +;; https://github.com/death/dbus/issues/20 + +(defun call-screenshot () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + "" + '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request" + :destination (dbus:bus-name bus)) + (DBUS:publish-objects bus)))) + +;; checking params for signal with +;; (dbus:sigexp "ua{sv}") +;; as string from here https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#request + +;; wowy, i get the link with my handle +;; "/org/freedesktop/portal/desktop/request/1_87/yayay" +;; and now the result should be signalled on the Reply object at that path + +;; lot's of stuff in dbus-monitor: +;; method call time=1719909623.911588 sender=:1.89 -> destination=org.freedesktop.portal.Desktop serial=3 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Screenshot; member=Screenshot +;; string "" +;; array [ +;; dict entry( +;; string "handle_token" +;; variant string "yayay" +;; ) +;; ] +;; method call time=1719909623.911612 sender=:1.23 -> destination=org.freedesktop.DBus serial=212 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetConnectionUnixProcessID +;; string ":1.89" +;; method return time=1719909623.911618 sender=org.freedesktop.DBus -> destination=:1.23 serial=152 reply_serial=212 +;; uint32 93638 +;; method call time=1719909623.911624 sender=:1.23 -> destination=org.freedesktop.DBus serial=213 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/portal/desktop/request/1_89/yayay',arg0='org.freedesktop.impl.portal.Request'" +;; method return time=1719909623.911631 sender=org.freedesktop.DBus -> destination=:1.23 serial=153 reply_serial=213 +;; method call time=1719909623.911635 sender=:1.23 -> destination=org.freedesktop.DBus serial=214 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.impl.portal.Request',path='/org/freedesktop/portal/desktop/request/1_89/yayay'" +;; method return time=1719909623.911641 sender=org.freedesktop.DBus -> destination=:1.23 serial=154 reply_serial=214 +;; method call time=1719909623.911644 sender=:1.23 -> destination=org.freedesktop.DBus serial=215 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=StartServiceByName +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; uint32 0 +;; method return time=1719909623.911651 sender=org.freedesktop.DBus -> destination=:1.23 serial=155 reply_serial=215 +;; uint32 2 +;; method call time=1719909623.911657 sender=:1.23 -> destination=org.freedesktop.DBus serial=216 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; method return time=1719909623.911662 sender=org.freedesktop.DBus -> destination=:1.23 serial=156 reply_serial=216 +;; string ":1.28" +;; method call time=1719909623.911668 sender=:1.23 -> destination=:1.28 serial=217 path=/org/freedesktop/portal/desktop/request/1_89/yayay; interface=org.freedesktop.DBus.Properties; member=GetAll +;; string "org.freedesktop.impl.portal.Request" +;; error time=1719909623.911674 sender=:1.28 -> destination=:1.23 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=217 +;; string "Object does not exist at path “/org/freedesktop/portal/desktop/request/1_89/yayay”" +;; method call time=1719909623.911680 sender=:1.23 -> destination=:1.28 serial=218 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Screenshot; member=Screenshot +;; object path "/org/freedesktop/portal/desktop/request/1_89/yayay" +;; string "" +;; string "" +;; array [ +;; ] +;; method return time=1719909623.911693 sender=:1.23 -> destination=:1.89 serial=219 reply_serial=3 +;; object path "/org/freedesktop/portal/desktop/request/1_89/yayay" +;; signal time=1719909623.911699 sender=org.freedesktop.DBus -> destination=:1.89 serial=5 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost +;; string ":1.89" +;; signal time=1719909623.911705 sender=org.freedesktop.DBus -> destination=(null destination) serial=123 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged +;; string ":1.89" +;; string ":1.89" +;; string "" +;; method call time=1719909623.911714 sender=:1.23 -> destination=:1.28 serial=220 path=/org/freedesktop/portal/desktop/request/1_89/yayay; interface=org.freedesktop.impl.portal.Request; member=Close +;; error time=1719909623.911717 sender=:1.28 -> destination=:1.23 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=220 +;; string "Object does not exist at path “/org/freedesktop/portal/desktop/request/1_89/yayay”" + +;; does this mean that I should be the one creating the response object? + +;; well, now maybe i will be able to figure out more in the flameshot repo +;; https://github.com/flameshot-org/flameshot/blob/c1dac52231024174faa68a29577129ebca125dff/src/utils/screengrabber.cpp#L59 + +;; or, well, the Claude writes that maybe connection is closed prematurely because nobody is listening? + +(cl-ppcre:regex-replace "\\." ":1.241" "_" :start 1) + +(defun call-screenshot () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (let* + ((requester-name (cl-ppcre:regex-replace "\\." (dbus:bus-name bus) "_" :start 1)) + (request-name "yayay") + (resp-path (concatenate 'string "/org/freedesktop/portal/desktop/request/" + requester-name + "/" + request-name))) + (dbus:define-dbus-object request-object + (:path resp-path)) + (dbus:define-dbus-signal-handler (request-object response) ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface "org.freedesktop.portal.Request") + (format t "Got response ~A with results ~A~%" id results)) + (format T "Will try to listen on ~A~%" resp-path) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + "" + '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:publish-objects bus))))) +;; with new code inserted about listening on request.response + +;; method call time=1720082195.600449 sender=:1.207 -> destination=org.freedesktop.portal.Desktop serial=3 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Screenshot; member=Screenshot +;; string "" +;; array [ +;; dict entry( +;; string "handle_token" +;; variant string "yayay" +;; ) +;; ] +;; method call time=1720082195.600630 sender=:1.23 -> destination=org.freedesktop.DBus serial=1869 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetConnectionUnixProcessID +;; string ":1.207" +;; method return time=1720082195.600635 sender=org.freedesktop.DBus -> destination=:1.23 serial=212 reply_serial=1869 +;; uint32 93638 +;; method call time=1720082195.600871 sender=:1.23 -> destination=org.freedesktop.DBus serial=1870 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/portal/desktop/request/1_207/yayay',arg0='org.freedesktop.impl.portal.Request'" +;; method return time=1720082195.600875 sender=org.freedesktop.DBus -> destination=:1.23 serial=213 reply_serial=1870 +;; method call time=1720082195.600883 sender=:1.23 -> destination=org.freedesktop.DBus serial=1871 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.impl.portal.Request',path='/org/freedesktop/portal/desktop/request/1_207/yayay'" +;; method return time=1720082195.600887 sender=org.freedesktop.DBus -> destination=:1.23 serial=214 reply_serial=1871 +;; method call time=1720082195.600902 sender=:1.23 -> destination=org.freedesktop.DBus serial=1872 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=StartServiceByName +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; uint32 0 +;; method return time=1720082195.600907 sender=org.freedesktop.DBus -> destination=:1.23 serial=215 reply_serial=1872 +;; uint32 2 +;; method call time=1720082195.600959 sender=:1.23 -> destination=org.freedesktop.DBus serial=1873 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; method return time=1720082195.600963 sender=org.freedesktop.DBus -> destination=:1.23 serial=216 reply_serial=1873 +;; string ":1.28" +;; method call time=1720082195.601013 sender=:1.23 -> destination=:1.28 serial=1874 path=/org/freedesktop/portal/desktop/request/1_207/yayay; interface=org.freedesktop.DBus.Properties; member=GetAll +;; string "org.freedesktop.impl.portal.Request" +;; error time=1720082195.601138 sender=:1.28 -> destination=:1.23 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=1874 +;; string "Object does not exist at path “/org/freedesktop/portal/desktop/request/1_207/yayay”" +;; method call time=1720082195.601215 sender=:1.23 -> destination=:1.28 serial=1875 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Screenshot; member=Screenshot +;; object path "/org/freedesktop/portal/desktop/request/1_207/yayay" +;; string "" +;; string "" +;; array [ +;; ] +;; method return time=1720082195.601235 sender=:1.23 -> destination=:1.207 serial=1876 reply_serial=3 +;; object path "/org/freedesktop/portal/desktop/request/1_207/yayay" +;; method call time=1720082195.601435 sender=:1.207 -> destination=org.freedesktop.DBus serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type=signal,interface=org.freedesktop.portal.Request,destination=:1.207" +;; method return time=1720082195.604833 sender=org.freedesktop.DBus -> destination=:1.207 serial=3 reply_serial=4 +;; signal time=1720082198.479133 sender=:1.192 -> destination=(null destination) serial=221 path=/org/gnome/Terminal/window/2; interface=org.gtk.Actions; member=Changed +;; array [ +;; ] +;; array [ +;; dict entry( +;; string "copy" +;; boolean true +;; ) +;; ] +;; array [ +;; ] +;; array [ +;; ] + +;; trying suggestion on not creating an object ourselves? +(defun call-screenshot-register-request () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (let* + ((bus-name (dbus:bus-name bus)) + (request-name "yayay")) + (dbus:define-dbus-object root-object + (:path "/")) + (dbus:define-dbus-signal-handler + (root-object response) + ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface "org.freedesktop.portal.Request") + (format t "Got response ~A with results ~A~%" id results)) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + "" + '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:publish-objects bus))))) + +;; method call time=1720270194.734644 sender=:1.95 -> destination=org.freedesktop.portal.Desktop serial=3 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Screenshot; member=Screenshot +;; string "" +;; array [ +;; dict entry( +;; string "handle_token" +;; variant string "yayay" +;; ) +;; ] +;; method call time=1720270194.734664 sender=:1.25 -> destination=org.freedesktop.DBus serial=239 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetConnectionUnixProcessID +;; string ":1.95" +;; method return time=1720270194.734669 sender=org.freedesktop.DBus -> destination=:1.25 serial=169 reply_serial=239 +;; uint32 94649 +;; method call time=1720270194.734674 sender=:1.25 -> destination=org.freedesktop.DBus serial=240 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',path='/org/freedesktop/portal/desktop/request/1_95/yayay',arg0='org.freedesktop.impl.portal.Request'" +;; method return time=1720270194.734679 sender=org.freedesktop.DBus -> destination=:1.25 serial=170 reply_serial=240 +;; method call time=1720270194.734682 sender=:1.25 -> destination=org.freedesktop.DBus serial=241 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type='signal',sender='org.freedesktop.impl.portal.desktop.gnome',interface='org.freedesktop.impl.portal.Request',path='/org/freedesktop/portal/desktop/request/1_95/yayay'" +;; method return time=1720270194.734688 sender=org.freedesktop.DBus -> destination=:1.25 serial=171 reply_serial=241 +;; method call time=1720270194.734691 sender=:1.25 -> destination=org.freedesktop.DBus serial=242 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=StartServiceByName +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; uint32 0 +;; method return time=1720270194.734698 sender=org.freedesktop.DBus -> destination=:1.25 serial=172 reply_serial=242 +;; uint32 2 +;; method call time=1720270194.734702 sender=:1.25 -> destination=org.freedesktop.DBus serial=243 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner +;; string "org.freedesktop.impl.portal.desktop.gnome" +;; method return time=1720270194.734707 sender=org.freedesktop.DBus -> destination=:1.25 serial=173 reply_serial=243 +;; string ":1.30" +;; method call time=1720270194.734712 sender=:1.25 -> destination=:1.30 serial=244 path=/org/freedesktop/portal/desktop/request/1_95/yayay; interface=org.freedesktop.DBus.Properties; member=GetAll +;; string "org.freedesktop.impl.portal.Request" +;; error time=1720270194.734718 sender=:1.30 -> destination=:1.25 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=244 +;; string "Object does not exist at path “/org/freedesktop/portal/desktop/request/1_95/yayay”" +;; method call time=1720270194.734723 sender=:1.25 -> destination=:1.30 serial=245 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Screenshot; member=Screenshot +;; object path "/org/freedesktop/portal/desktop/request/1_95/yayay" +;; string "" +;; string "" +;; array [ +;; ] +;; method return time=1720270194.734736 sender=:1.25 -> destination=:1.95 serial=246 reply_serial=3 +;; object path "/org/freedesktop/portal/desktop/request/1_95/yayay" +;; method call time=1720270194.734741 sender=:1.95 -> destination=org.freedesktop.DBus serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch +;; string "type=signal,interface=org.freedesktop.portal.Request" +;; method return time=1720270194.754781 sender=org.freedesktop.DBus -> destination=:1.95 serial=3 reply_serial=4 + +;; well, it seems that still same Object does not exist +;; so. oh. it's 1_95, so what. +;; i'm not subscribing to same thing, right? +;; let's try to chagne in place +;; um. what. what if i manually send to Request sinal? + + +;; and these changes they are messy, would be better to use commits + +(defun call-screenshot () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (let* + ((requester-name (cl-ppcre:regex-replace "\\." (dbus:bus-name bus) "_" :start 1)) + (request-name "yayay") + (resp-path (concatenate 'string "/org/freedesktop/portal/desktop/request/" + requester-name + "/" + request-name))) + (dbus:define-dbus-object request-object + (:path resp-path)) + (dbus:define-dbus-signal-handler (request-object response) ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface "org.freedesktop.portal.Request") + (format t "Got response ~A with results ~A~%" id results)) + (format T "Will try to listen on ~A~%" resp-path) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + ;; (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + ;; (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + ;; "" + ;; '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:publish-objects bus))))) + +;; ok, dbus-send can't add a{sv} because dict:string:variant is not allowed +;; variant is a container and nesting is not supported by the tool +;; let's try again on the real method call? +(defun call-screenshot () + (handler-case + (dbus:with-open-bus (bus (dbus:session-server-addresses)) + (let* + ((requester-name (cl-ppcre:regex-replace "\\." (dbus:bus-name bus) "_" :start 1)) + (request-name "yayay") + (resp-path (concatenate 'string "/org/freedesktop/portal/desktop/request/" + requester-name + "/" + request-name))) + (dbus:define-dbus-object request-object + (:path resp-path)) + (dbus:define-dbus-signal-handler (request-object response) ((id :uint32) (results (:ARRAY (:DICT-ENTRY :STRING :VARIANT)))) + (:interface "org.freedesktop.portal.Request") + (format t "Got response ~A with results ~A~%" id results)) + (format T "Will try to listen on ~A~%" resp-path) + (format T "Bus connection name ~A~%" (dbus:bus-name bus)) + (dbus:with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" + "" + '(("handle_token" ((:string) "yayay"))))) + (dbus:add-match bus :type :signal + :interface "org.freedesktop.portal.Request") + (dbus:publish-objects bus))))) + + +;;; uhm, let's try another method that should more certainly work +;; gdbus call --session --dest org.freedesktop.portal.Desktop --object-path /org/freedesktop/portal/desktop --method org.freedesktop.portal.FileChooser.OpenFile "" "Choose a file" '{}' + +;; and when trying with a screenshot +;; $ gdbus call --session --dest org.freedesktop.portal.Desktop --object-path /org/freedesktop/portal/desktop --method org.freedesktop.portal.Screenshot.Screenshot "" '{}' +;; this returns the path + +;; wow +;; this has info +;; systemctl --user status xdg-desktop-portal-gnome.service +;; Jul 06 15:14:28 LLF33A87M xdg-desktop-portal-gnome[3770]: Failed to get screenshot: Cannot invoke method; proxy is for the well-known name org.gnome.Shell.Screenshot without an owner, and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag + + +;; and. if i want to be able to get screenshots i'll need another backend +;; https://wiki.archlinux.org/title/XDG_Desktop_Portal +;; so. not on xmonad or pure X11 +;; i could try wayland and another tiling manager, you know +;; but i guess first restart into gnome session and try to call gdbus from there +;; List of backends and interfaces + +;; The following table lists all backends available and their support for certain common interfaces. +;; Backend File chooser Screenshot and screen cast +;; xdg-desktop-portal-dde Yes Yes, on Deepin Desktop Environment +;; xdg-desktop-portal-gtk Yes No +;; xdg-desktop-portal-gnome Yes Yes, on GNOME +;; xdg-desktop-portal-kde Yes Yes, on KDE +;; xdg-desktop-portal-hyprland1 No Yes, on wlroots +;; xdg-desktop-portal-lxqt Yes No +;; xdg-desktop-portal-wlr No Yes, on wlroots +;; xdg-desktop-portal-xapp No Yes, on Cinnamon +;; xdg-desktop-portal-liri-gitAUR Yes Yes, on Liri +;; xdg-desktop-portal-shanaAUR Yes2 No +;; xdg-desktop-portal-tdAUR Yes No +;; xdg-desktop-portal-termfilechooser-gitAUR Yes3 No + +;; yep, looks like this does work on gnome, so, yeah. +;; well, let's try another window manager? one on wlroots maybe just hyprland +;; or qtile which should also use wlroots, but do i need this hackability? +;; i don't think i'm using it for much, so hyprland? +;; because i don't even really want to use python for hacking + +;; or, alternatively i guess i could try to install on Ubuntu +;; yeah, seems huh. will probably not launch through just home-manager? +;; or maybe it exactly will, just pull it's own wayland and stuff +;; https://github.com/nix-community/home-manager/issues/1167 maybe things won't work + +;; i tried to install sway just on ubuntu +;; and thing works somewhat, but screenshots are blocked +;; not sure what to do about that. +;; i could continue working on gnome for now + +;; OH, whoah. this faq https://github.com/emersion/xdg-desktop-portal-wlr +;; has link to python3 snippet +;; https://gitlab.gnome.org/-/snippets/19 +;; and that starts the screen cast! diff --git a/maybe-signals.lisp b/maybe-signals.lisp new file mode 100644 index 0000000..3118896 --- /dev/null +++ b/maybe-signals.lisp @@ -0,0 +1,31 @@ +(defpackage #:notification-example + (:use #:cl #:dbus) + (:export #:listen-for-notifications)) +(in-package #:notification-example) +(define-dbus-object root-object + (:path "/")) + +(define-dbus-signal-handler (root-object on-notification-closed) ((id :uint32) (reason :uint32)) + (:interface "org.freedesktop.Notifications") + (:name "NotificationClosed") + (format t "Notification ~A closed for reason ~A~%" id reason)) + +(define-dbus-signal-handler (root-object on-action-invoked) ((id :uint32) (action-key :string)) + (:interface "org.freedesktop.Notifications") + (:name "ActionInvoked") + (format t "Action ~A invoked on notification ~A~%" action-key id)) + +(defun listen-for-notifications () + (with-open-bus (bus (session-server-addresses)) + (format t "Bus connection name: ~A~%" (bus-name bus)) + (add-match bus :type :signal + :interface "org.freedesktop.Notifications" + :member "NotificationClosed") + (add-match bus :type :signal + :interface "org.freedesktop.Notifications" + :member "ActionInvoked") + (publish-objects bus) + (format t "Listening for notification signals...~%") + (loop (sleep 1) + (format t ".") + (force-output)))) diff --git a/notes.org b/notes.org new file mode 100644 index 0000000..09fe6b9 --- /dev/null +++ b/notes.org @@ -0,0 +1,33 @@ +* [2024-06-28 Fri] +** well, i'm setting up sbcl and sly? +because i imagine using Common Lisp for my attempt at building screen +sharing for the XR glasses + +for the cool runtime access, right? +** well, just flake shell and (use-package sly) is enough +for .lisp files to get stuff + +yes, i'm still using C-M-f and stuff to move around sexps but things are fine + +so, what's next? +well, this is nice high level description +dbus +https://www.freedesktop.org/wiki/Software/dbus/ + +and here's something to start usage +https://blog.macrolet.net/posts/DBus-and-PolicyKit-from-Common-Lisp.html +from https://github.com/death/dbus + +and even lots of examples, but i think i will need a big book on Common Lisp to learn new things? +* [2024-06-29 Sat] +** going through the dbus guide, sounds neat +https://develop.kde.org/docs/features/d-bus/introduction_to_dbus/ + +that qdbus and qdbus-viewer are in +#+begin_src bash +$ nix shell nixpkgs#kdePackages.qttools +#+end_src + +** now let's read about calling methods + +* the receiving of the signals diff --git a/notify-with-listen.lisp b/notify-with-listen.lisp new file mode 100644 index 0000000..4dbe39f --- /dev/null +++ b/notify-with-listen.lisp @@ -0,0 +1,50 @@ +;;;; +----------------------------------------------------------------+ +;;;; | DBUS | +;;;; +----------------------------------------------------------------+ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'dbus) +(defpackage #:publish-example + (:use #:cl #:dbus) + (:export #:publish-example)) + +(in-package #:publish-example) + +(define-dbus-object root + (:path "/")) + +(define-dbus-object my-service + (:path "/org/adeht/MyService") + (:parent root)) + +(define-dbus-method (my-service my-method) ((s1 :string) (s2 :string)) (:string) + (:interface "org.adeht.MyService") + (format t "will process call for ~S and ~S~%" s1 s2) + (force-output) + (concatenate 'string "updated" s1 s2)) + +(define-dbus-signal-handler (my-service on-signal) () + (:interface "org.adeht.MyService") + (format t "Got signal with arg ~S~%" "hoh") + (force-output)) + +(define-dbus-signal-handler (root on-signal) ((s :string)) + (:interface "org.adeht.MyService") + (format t "Got signal on root with arg ~S~%" s) + (force-output)) + +(defun publish-example () + (handler-case + (with-open-bus (bus (session-server-addresses)) + (format t "Bus connection name: ~A~%" (bus-name bus)) + (dbus:add-match bus :type :signal :interface "org.adeht.MyService") + (publish-objects bus)) + (end-of-file () + :disconnected-by-bus))) + +;; was missing the 'add-match +;; and now let's try to catch the NotificationClosed tihngy +;; enefedov@LLF33A87M:~$ dbus-send --session --type=signal /org/adeht/MyService org.adeht.MyService.OnSignal string:"Hello yayyaline" + +;; and when i do signal-handler on 'root +;; $ dbus-send --session --type=signal /org/adeht/MyService org.adeht.MyService.OnSignal ;; this is how to send it +;; so! i need to create it on the object path, ok diff --git a/playground.lisp b/playground.lisp new file mode 100644 index 0000000..13d1bfe --- /dev/null +++ b/playground.lisp @@ -0,0 +1,377 @@ +;; for the sly to use dynamibly decided sbcl impl, to pick up wrappers +;; with packages? +;; (setq inferior-lisp-program "/bin/env sbcl") + +;; so. what? load dbus library and try out examples? +;; maybe i want to try some of the simpler function calls +;; and then get to screen sharing dialog, and print some info on frames? + +;; https://nixos.org/manual/nixpkgs/stable/#lisp-building-wrappers +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'alexandria) +(asdf:load-system 'dbus) + +;; https://blog.macrolet.net/posts/DBus-and-PolicyKit-from-Common-Lisp.html +(defpackage #:example + (:use #:cl #:dbus)) + +(in-package #:example) + +(with-open-bus (bus (system-server-addresses)) + (with-introspected-object (authority bus + "/org/freedesktop/PolicyKit1/Authority" + "org.freedesktop.PolicyKit1") + (let* ((subject `("system-bus-name" (("name" ((:string) ,(bus-name bus)))))) + (action-id "org.freedesktop.policykit.exec") + (details ()) + (flags 1) + (cancellation-id "") + (result + (authority "org.freedesktop.PolicyKit1.Authority" "CheckAuthorization" + subject action-id details flags cancellation-id))) + (format T "~A~%" result)))) + +(dbus/server-addresses:system-server-addresses) +(system-server-addresses) + +;; C-c is sly-prefix-map +(with-open-bus (bus (session-server-addresses)) + (with-introspected-object (notification bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications") + (notification "org.freedesktop.Notifications" "Notify" + "Test" 0 "" "Test" "This is a test; I repeat, this is a test." '() '() -1) + )) + +;; ok, but how do i introspect which parameters i need to pass to the call? +;; i could use +;;$ qdbus org.freedesktop.Notifications /org/freedesktop/Notifications +;; from kdePackages.qttools +;; but i'd prefer from cl + +(with-open-bus (bus (session-server-addresses)) + (list-names bus)) + +(with-open-connection (bus (make-instance 'iomux:event-base) (session-server-addresses)) + (let ((obj (make-object-from-introspection bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications"))) + (format T "hello") + (format T "~A" obj))) + +;; sly-macroexpand-1 +;; (let ((bus +;; (open-connection (make-instance 'iolib/multiplex:event-base) +;; (session-server-addresses) :if-failed :error))) +;; (unwind-protect +;; (progn +;; (make-object-from-introspection bus "/org/freedesktop/Notifications" +;; "org.freedesktop.Notifications")) +;; (when bus (close-connection bus)))) + +;; oh, ok. make-object-from-introspection can be subsctituted with convenience +;; ' with-introspected-object' and that's already in examples +;; so convenience does hide the multiplexers and connecitons + +(with-open-bus (bus (session-server-addresses)) + (list-names bus )) + +;; "org.freedesktop.Notifications" +(with-open-bus (bus (session-server-addresses)) + (get-managed-objects bus "org.freedesktop.Notifications" )) + +;; wait, what if i just use 'object in the forms passed to 'with-introspected-object'? +;; and gensyms pick it up? :shrug: +(with-open-bus (bus (session-server-addresses)) + (get-managed-objects bus "org.freedesktop.Notifications" "/org/freedesktop/Notifications" )) +;; nope + +;; ok, found another example maybe +;; https://github.com/lucashpandolfo/udisks +;; well, maybe it should work? +(with-open-bus (bus (session-server-addresses)) + (get-managed-objects bus "org.gtk.vfs.AfcVolumeMonitor" "/org/gtk/Private/RemoteVolumeMonitor" )) + +;; ok, maybe these servcies don't have managed objects? +;; otherwise they would have ObjectManager interface or something +;; so let's try to figure out which interface I need to add for +;; get-all-properties to work +(with-open-bus (bus (session-server-addresses)) + (get-all-properties bus "org.freedesktop.Notifications" + "/org/freedesktop/Notifications" + "org.freedesktop.Notifications")) + +;; nil, but maybe because it has methods, not properties? +(with-open-bus (bus (session-server-addresses)) + (get-all-properties bus "org.freedesktop.portal.Desktop" "/org/freedesktop/portal/desktop" "org.freedesktop.portal.ScreenCast")) + +;; (("AvailableSourceTypes" 0) ("AvailableCursorModes" 0) ("version" 4)) +;; ok, so this is getting properties. cool +;; next - figuring out how to check method signatures? +;; should be possible no? + +;; oh, maybe i should use 'make-object-from-introspection directly +;; but where do i get "connection?" + +(format T "well") + +(iolib/multiplex:with-event-base (event-base) + (format T "yoyo") + (with-open-connection (connection event-base (session-server-addresses)) + (format T "in connection ~A" connection) + (let ((introspection (dbus/introspect:fetch-introspection-document connection "/org/freedesktop/Notifications" "org.freedesktop.Notifications"))) + (format T "Introspection: ~A" introspection)) + + (let ((obj (make-object-from-introspection connection "/org/freedesktop/Notifications" "org.freedesktop.Notifications"))) + (format T "hello") + (format T "~A" obj)))) + +(with-open-bus (my-bus (session-server-addresses)) + (handler-case + (let ((props (dbus:get-all-properties my-bus "org.freedesktop.Notifications" "/org/freedesktop/Notifications" "org.freedesktop.Notifications"))) + (format T "Notification properties: ~A~%" props)) + (error (e) + (format T "Error getting properties: ~pA~%" e)))) + +;; well, strange + +;; https://old.reddit.com/r/lisp/comments/179zl1/has_anyone_else_used_the_dbus_package_much/ +(progn + (setf upower_conn + (open-connection + (make-instance 'iolib.multiplex:event-base) (system-server-addresses))) + (authenticate (supported-authentication-mechanisms upower_conn) upower_conn) + (hello upower_conn) + (invoke-method upower_conn "Introspect" + :path "/org/freedesktop/UPower" + :destination "org.freedesktop.UPower" + :interface "org.freedesktop.DBus.Introspectable")) + + +(progn + (setf upower_conn + (open-connection + (make-instance 'iolib.multiplex:event-base) (system-server-addresses))) + (authenticate (supported-authentication-mechanisms upower_conn) upower_conn) + (hello upower_conn) + (setf bat0_obj + (make-object-from-introspection + upower_conn + "/org/freedesktop/UPower/devices/battery_BAT0" + "org.freedesktop.UPower")) + (object-invoke bat0_obj "org.freedesktop.DBus.Properties" "GetAll" "org.freedesktop.UPower.Device")) + +;; ok, so this works. +;; from 11 years ago, cool +;; creating object from introspection +;; doing invoke, but similar to with-object + +;; let's try get object for Notification +;; and then try to print object and stuff + +(iolib/multiplex:with-event-base (event-base) + (with-open-connection (connection event-base (session-server-addresses)) + (authenticate (supported-authentication-mechanisms connection) connection) + (hello connection) + (let ((obj (make-object-from-introspection connection "/org/freedesktop/Notifications" "org.freedesktop.Notifications"))) + (print-object obj nil) + (list-object-interfaces obj)))) + +;; ok, so this works? +;; i needed both authenticate and hello, cool +;; thank you https://old.reddit.com/r/lisp/comments/179zl1/has_anyone_else_used_the_dbus_package_much/ + +;; (# +;; # +;; # +;; # +;; #) + +;; wowy, yeay! +;; i guess maybe now let's try to list interface on Desktop, which will have ScreenCast + + +(iolib/multiplex:with-event-base (event-base) + (with-open-connection (connection event-base (session-server-addresses)) + (authenticate (supported-authentication-mechanisms connection) connection) + (hello connection) + (let ((obj (make-object-from-introspection connection "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop"))) + (print-object obj nil) + (list-object-interfaces obj)))) + +;; nice! + +;; so, what to try next though? +;; i wanted a way to check the funciton signatures +;; maybe for this one? +;; # +;; how do i get methods of an interface? +;; oh, so 'list-object-interfaces only returns values from hashmap that obj +;; already contains +(iolib/multiplex:with-event-base (event-base) + (with-open-connection (connection event-base (session-server-addresses)) + (authenticate (supported-authentication-mechanisms connection) connection) + (hello connection) + (let ((obj (make-object-from-introspection connection "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop"))) + (list-interface-methods (object-interface "org.freedesktop.portal.Screenshot" obj))))) + +;; (# +;; #) +;; ok, here are sa{sv} types of parameters, maybe this is it +;; i suppose next is figuring out how to call + +;; let's read more general stuff about dbus then? +;; https://develop.kde.org/docs/features/d-bus/accessing_dbus_interfaces/ +;; i think this is in C with qt library, but ok + +;; here's about Variants https://doc.qt.io/qt-5/qvariant.html +;; what's that? + +;; ok, now examples should be more understandable +;; https://github.com/death/dbus/blob/8bba6a0942232e9d7fa915b33bbe32dfedc5abb9/examples/notify.lisp +;; https://github.com/death/dbus/blob/8bba6a0942232e9d7fa915b33bbe32dfedc5abb9/examples/publish.lisp + +;; maybe. let's try to call something with screenshots, let's go + +;; https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html +(iolib/multiplex:with-event-base (event-base) + (with-open-connection (connection event-base (session-server-addresses)) + (authenticate (supported-authentication-mechanisms connection) connection) + (hello connection) + (let ((obj (make-object-from-introspection connection "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop"))) + (list-interface-methods (object-interface "org.freedesktop.portal.Screenshot" obj))))) + +;; for the sa{sv} looking into documentation +;; https://dbus.freedesktop.org/doc/dbus-specification.html#type-system +;; so for screenshot it's string, then array of dict entry string->variant +(with-open-bus (bus (session-server-addresses)) + (with-introspected-object (desktop bus "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Desktop") + (desktop "org.freedesktop.portal.Screenshot" "Screenshot" "" '()))) + +;; dbus-send --session --dest=org.freedesktop.portal.Screenshot --type=method_call --print-reply /org/freedesktop/portal/desktop org.freedesktop.portal.Screenshot + +;; well, calling Screenshot from the qdbusviewer doesn't work +;; maybe it can work from code, +;; not quite found an easy answer, but here's code for flameshot +;; https://github.com/flameshot-org/flameshot/blob/c1dac52231024174faa68a29577129ebca125dff/src/utils/screengrabber.cpp#L59 + +(iolib/multiplex:with-event-base (event-base) + (with-open-connection (connection event-base (session-server-addresses)) + (authenticate (supported-authentication-mechanisms connection) connection) + (hello connection) + (let ((obj (make-object-from-introspection connection "/org/adeht/MyService" "org.adeht.MyService"))) + (list-interface-methods (object-interface "org.adeht.MyService" obj)) + ))) ; missing Introspecable on the service defined through death/dbus +;; that's because 8 months ago introspection publishing was changed to be done by default +;; and my version is older, i could try to set up separate action from removed example? +;; but better to try to update + +(with-open-bus (bus (session-server-addresses)) + (with-introspected-object (desktop bus "/org/adeht/MyService" "org.adeht.MyService") + (desktop "org.adeht.MyService" "my-method" "hello"))) + +;; trying from another discussion +;; still trying to receive a signal +(define-dbus-object root + (:path "/")) + +(dbus:define-dbus-object my-notifications-service + (:path "/org/freedesktop/Notifications") + (:parent root)) + +;; (dbus:define-dbus-signal-handler (my-notifications-service notification-closed) () +;; (:interface "org.freedesktop.Notifications") +;; (format t "Got notification closed without parameters" ) +;; (force-output)) + +(dbus:define-dbus-signal-handler (my-notifications-service notification-closed) ((id :uint32) (reason :uint32)) + (:interface "org.freedesktop.Notifications") + (format t "Got notification closed with parameters ~A ~A" id reason) + (force-output)) + +(define-dbus-signal-handler (my-notifications-service on-non-notification-end-signal) ((s :string)) + (:interface "org.adeht.MyService") + (format t "Got signal with arg ~S~%" s) + (force-output)) + +(defun example-listen-to-notification () + (handler-case + (dbus:with-open-bus (bus (session-server-addresses)) +; (dbus:add-match bus :type :signal :interface "org.freedesktop.Notifications" :member "NotificationClosed") + ; (dbus:add-match bus :type :signal :interface "org.adeht.MyService") + (dbus:add-match bus :type :signal :path "/org/freedesktop/Notifications") + (format t "Bus connection name: ~A~%" (dbus:bus-name bus)) + (dbus:publish-objects bus)) + (end-of-file () + :disconnected-by-bus))) + + + + +;; signal time=1719758221.505386 sender=:1.64 -> destination=:1.162 serial=83 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=NotificationClosed +;; uint32 15 +;; uint32 2 +(with-open-bus (bus (session-server-addresses)) + (with-introspected-object (notification bus "/org/freedesktop/Notifications" "org.freedesktop.Notifications") + (notification "org.freedesktop.Notifications" "Notify" + "Test" 0 "" "Test" "This is a test; I repeat, this is a test." '() '() -1) + )) + +;; yes, i re-declared signal handler without parameters and now it catches +;; the +;; goood! + +;; but it doesn't catch when i actually close the notification +;; maybe because of parameters? +;; oh, maybe uint32 are not the ones i had before in the signature + +;; this works for my extra interface handler: +;; $ dbus-send --session --type=signal /org/freedesktop/Notifications org.adeht.MyService.OnNonNotificationEndSignal string:"Hello yayyaline" + +;; huh. so manual with uint32 works +;; dbus-send --session --type=signal /org/freedesktop/Notifications org.freedesktop.Notifications.NotificationClosed uint32:4 uint32:8 + +;; and if i only define without params, then manual without params works +;; dbus-send --session --type=signal /org/freedesktop/Notifications org.freedesktop.Notifications.NotificationClosed + +;; but the one from actually closed notification doesn't work? +;; in dbus-monitor one from actually closing: +;; signal time=1719765073.249989 sender=:1.45 -> destination=:1.112 serial=51 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=NotificationClosed +;; uint32 11 +;; uint32 2 + +;; and from manual invocation: +;; signal time=1719765156.257421 sender=:1.115 -> destination=(null destination) serial=2 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=NotificationClosed +;; uint32 2222 +;; uint32 11111 + +;; is there any visible difference? +;; aaand, yeah, i get with manual and dont get with actual + +;; aaand, yes. +;; when signal has "destination" set - it's not broadcasted, +;; so, not everyone gets it. when i do actual closing of notification +;; the resulting NotificationClosed is likely sent to caller +;; and my code doesn't receive it, coool +;; $ dbus-send --session --type=signal --dest=:1.108 /org/freedesktop/Notifications org.freedesktop.Notifications.NotificationClosed uint32:777 uint32:333 +;; this works - when i match with a name from "Bus connection name" + +;; yay. i suppose +;; now what would be an example of broadcasted signal that i could listen to +;; in my example + +;; let's do safeeyes icon change: +;; signal time=1719766048.367111 sender=:1.34 -> destination=(null destination) serial=101 path=/org/ayatana/NotificationItem/safeeyes_2; interface=org.kde.StatusNotifierItem; member=NewIcon + +(dbus:define-dbus-object my-safeeyes-listener + (:path "/org/ayatana/NotificationItem/safeeyes_2")) + +(dbus:define-dbus-signal-handler (my-safeeyes-listener new-icon) () + (:interface "org.kde.StatusNotifierItem") + (format t "Got notification on the safeeyes icon change~%" ) + (force-output)) + +(defun example-listen-to-safeeys () + (handler-case + (dbus:with-open-bus (bus (session-server-addresses)) + (dbus:add-match bus :type :signal :interface "org.kde.StatusNotifierItem") + (format t "Bus connection name: ~A~%" (dbus:bus-name bus)) + (dbus:publish-objects bus)) + (end-of-file () + :disconnected-by-bus)))