1package idle
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/bnema/zerobus"
8)
9
10const (
11 portalDest = "org.freedesktop.portal.Desktop"
12 portalPath = "/org/freedesktop/portal/desktop"
13 portalInterface = "org.freedesktop.portal.Inhibit"
14 requestIface = "org.freedesktop.portal.Request"
15 propertiesIface = "org.freedesktop.DBus.Properties"
16
17 // Inhibit flags from portal spec
18 flagLogout = 1
19 flagUserSwitch = 2
20 flagSuspend = 4
21 flagIdle = 8
22)
23
24// portalClient is the XDG Desktop Portal Inhibit API used by PortalInhibitor.
25type portalClient interface {
26 // Version returns the Inhibit portal version; it fails when no portal
27 // answers.
28 Version() (uint32, error)
29 // Inhibit asks the portal to inhibit idle and suspend and returns the
30 // request handle.
31 Inhibit(reason string) (string, error)
32 // CloseRequest ends the inhibit request with the given handle.
33 CloseRequest(handle string) error
34 // Close releases the bus connection.
35 Close() error
36}
37
38// zerobusPortal implements portalClient over a zerobus connection.
39// It is not safe for concurrent use.
40type zerobusPortal struct {
41 conn *zerobus.Conn
42}
43
44func (z *zerobusPortal) Version() (uint32, error) {
45 e := z.conn.NewCall(portalDest, portalPath, propertiesIface, "Get", "ss")
46 e.Str(portalInterface)
47 e.Str("version")
48 m, err := z.conn.Call()
49 if err != nil {
50 return 0, err
51 }
52 body := m.Body()
53 if sig := body.Variant(); sig != "u" {
54 return 0, fmt.Errorf("portal version has type %q, want \"u\"", sig)
55 }
56 version := body.Uint32()
57 return version, body.Err()
58}
59
60// Inhibit calls Inhibit(window: s, flags: u, options: a{sv}) -> handle: o.
61func (z *zerobusPortal) Inhibit(reason string) (string, error) {
62 e := z.conn.NewCall(portalDest, portalPath, portalInterface, "Inhibit", "sua{sv}")
63 e.Str("") // window identifier (empty for non-sandboxed)
64 e.Uint32(flagIdle | flagSuspend) // inhibit idle and suspend
65 options := e.BeginArray('{')
66 e.Struct()
67 e.Str("reason")
68 e.Variant("s")
69 e.Str(reason)
70 e.EndArray(options)
71
72 m, err := z.conn.Call()
73 if err != nil {
74 return "", err
75 }
76 body := m.Body()
77 // The reply points into the receive buffer: copy the handle we keep.
78 handle := strings.Clone(body.ObjectPath())
79 if err := body.Err(); err != nil {
80 return "", err
81 }
82 return handle, nil
83}
84
85func (z *zerobusPortal) CloseRequest(handle string) error {
86 z.conn.NewCall(portalDest, handle, requestIface, "Close", "")
87 _, err := z.conn.Call()
88 return err
89}
90
91func (z *zerobusPortal) Close() error {
92 return z.conn.Close()
93}