dialog.go RawHistoryFolder
1package wayland
2
3import (
4 "github.com/bnema/go-wayland-bindings/server/xdgdialog"
5 "github.com/bnema/go-wayland-bindings/server/xdgshell"
6 "github.com/bnema/neferwl/internal/ports"
7 "github.com/bnema/purego-libwayland/server"
8)
9
10// xdg-dialog: a client marks a toplevel as modal for its parent (a "Save
11// as…" dialog). The parent itself still comes from xdg_toplevel.set_parent
12// or xdg-foreign; core sends the focus the parent gets to its modal dialog.
13
14func registerDialog(d *server.Display, s *Server) error {
15 return xdgdialog.NewWmDialogV1Global(d, 1, func(c server.Client, v, id uint32) {
16 _, _ = xdgdialog.NewWmDialogV1(c, int32(v), id, wmDialog{s})
17 })
18}
19
20type wmDialog struct{ s *Server }
21
22func (wmDialog) Destroy(*xdgdialog.WmDialogV1) {}
23
24// GetXdgDialog makes the dialog object of a toplevel; a toplevel has at
25// most one.
26func (m wmDialog) GetXdgDialog(r *xdgdialog.WmDialogV1, id uint32, t *xdgshell.Toplevel) {
27 w := m.s.toplevelWindow(t)
28 if w != nil && w.dialog {
29 r.PostError(uint32(xdgdialog.WmDialogV1ErrorAlreadyUsed), "toplevel already has a dialog object")
30 return
31 }
32 res, err := xdgdialog.NewDialogV1(r.Client(), r.Version(), id, dialog{w})
33 if err != nil || w == nil {
34 return
35 }
36 w.dialog = true
37 // Destroying the object ends the modal state. Once the toplevel is
38 // gone the object is inert: its window stays unmapped, nothing is sent.
39 res.OnDestroy = func() {
40 w.dialog = false
41 w.setModal(false)
42 }
43}
44
45type dialog struct{ w *window }
46
47func (dialog) Destroy(*xdgdialog.DialogV1) {}
48func (d dialog) SetModal(*xdgdialog.DialogV1) {
49 if d.w != nil {
50 d.w.setModal(true)
51 }
52}
53func (d dialog) UnsetModal(*xdgdialog.DialogV1) {
54 if d.w != nil {
55 d.w.setModal(false)
56 }
57}
58
59// toplevelWindow is the window of toplevel t, nil when unknown.
60func (s *Server) toplevelWindow(t *xdgshell.Toplevel) *window {
61 if t == nil {
62 return nil
63 }
64 for _, w := range s.windows {
65 if w.toplevel != nil && w.toplevel.Resource == t.Resource {
66 return w
67 }
68 }
69 return nil
70}
71
72// setModal changes the modal state; core follows it once mapped (the map
73// carries the state of the first frame).
74func (w *window) setModal(modal bool) {
75 if w.modal == modal {
76 return
77 }
78 w.modal = modal
79 if w.mapped {
80 w.xdg.server.emit(ports.WindowModal{ID: w.id, Modal: modal})
81 }
82}