1package wayland
2
3import (
4 "testing"
5 "time"
6
7 "github.com/bnema/go-wayland-bindings/server/wayland"
8 "github.com/bnema/go-wayland-bindings/server/xdgdialog"
9 "github.com/bnema/go-wayland-bindings/server/xdgshell"
10 "github.com/bnema/neferwl/internal/ports"
11 "golang.org/x/sys/unix"
12)
13
14// A modal dialog maps with its state, and later changes reach core. A
15// second dialog object for the same toplevel is a protocol error.
16func TestDialogModal(t *testing.T) {
17 s, events, _, dir := lifecycleServer(t)
18 c := protocolClient(t, s, dir)
19 parent, _, parentXDG := surfaceMapper(t, c, events)()
20 // surfaceMapper allocates the toplevel right after its xdg_surface.
21 parentTop := parentXDG + 1
22
23 comp := bindProtocol(t, c, "wl_compositor")
24 wm := bindProtocol(t, c, "xdg_wm_base")
25 shm := bindProtocol(t, c, "wl_shm")
26 registerProtocol(t, c, shm)
27 dialogs := bindProtocol(t, c, "xdg_wm_dialog_v1")
28 fd, err := unix.MemfdCreate("dialog-buffer", 0)
29 if err != nil {
30 t.Fatal(err)
31 }
32 defer unix.Close(fd)
33 if err := unix.Ftruncate(fd, 4); err != nil {
34 t.Fatal(err)
35 }
36 pool, buffer := c.AllocateID(), c.AllocateID()
37 registerProtocol(t, c, pool)
38 registerProtocol(t, c, buffer)
39 if err := wireRequest(c, shm, uint16(wayland.ShmRequestCreatePool), []int{fd}, pool, int32(4)); err != nil {
40 t.Fatal(err)
41 }
42 requestProtocol(t, c, pool, wayland.ShmPoolRequestCreateBuffer, buffer, int32(0), int32(1), int32(1), int32(4), uint32(0))
43
44 // The dialog toplevel: modal before its first frame.
45 surf, xdg, top, dlg := c.AllocateID(), c.AllocateID(), c.AllocateID(), c.AllocateID()
46 requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, surf)
47 registerProtocol(t, c, surf)
48 requestProtocol(t, c, wm, xdgshell.WmBaseRequestGetXdgSurface, xdg, surf)
49 serials := make(chan uint32, 1)
50 xp := &configureProxy{serial: serials}
51 xp.SetID(xdg)
52 registerWireProxy(c, xp)
53 registerProtocol(t, c, top)
54 requestProtocol(t, c, xdg, xdgshell.SurfaceRequestGetToplevel, top)
55 registerProtocol(t, c, dlg)
56 requestProtocol(t, c, dialogs, xdgdialog.WmDialogV1RequestGetXdgDialog, dlg, top)
57 requestProtocol(t, c, dlg, xdgdialog.DialogV1RequestSetModal)
58 requestProtocol(t, c, top, xdgshell.ToplevelRequestSetParent, parentTop)
59 requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
60 if err := c.Roundtrip(); err != nil {
61 t.Fatal(err)
62 }
63 select {
64 case serial := <-serials:
65 requestProtocol(t, c, xdg, xdgshell.SurfaceRequestAckConfigure, serial)
66 default:
67 t.Fatal("missing configure")
68 }
69 requestProtocol(t, c, surf, wayland.SurfaceRequestAttach, buffer, int32(0), int32(0))
70 requestProtocol(t, c, surf, wayland.SurfaceRequestCommit)
71 if err := c.Roundtrip(); err != nil {
72 t.Fatal(err)
73 }
74 d := mapped(t, events, 2*time.Second)
75 if !d.Modal || d.Parent != parent.ID || !d.Floating {
76 t.Fatalf("dialog %+v, want modal over %d", d, parent.ID)
77 }
78
79 modal := func(want bool) {
80 t.Helper()
81 if err := c.Roundtrip(); err != nil {
82 t.Fatal(err)
83 }
84 for deadline := time.After(2 * time.Second); ; {
85 select {
86 case ev := <-events:
87 if m, ok := ev.(ports.WindowModal); ok {
88 if m.ID != d.ID || m.Modal != want {
89 t.Fatalf("modal change %+v, want %v", m, want)
90 }
91 return
92 }
93 case <-deadline:
94 t.Fatal("no modal change")
95 }
96 }
97 }
98 requestProtocol(t, c, dlg, xdgdialog.DialogV1RequestUnsetModal)
99 modal(false)
100 requestProtocol(t, c, dlg, xdgdialog.DialogV1RequestSetModal)
101 modal(true)
102 // Destroying the object ends the modal state.
103 requestProtocol(t, c, dlg, xdgdialog.DialogV1RequestDestroy)
104 modal(false)
105
106 // A toplevel gets one dialog object at a time.
107 first, second := c.AllocateID(), c.AllocateID()
108 registerProtocol(t, c, first)
109 registerProtocol(t, c, second)
110 requestProtocol(t, c, dialogs, xdgdialog.WmDialogV1RequestGetXdgDialog, first, top)
111 requestProtocol(t, c, dialogs, xdgdialog.WmDialogV1RequestGetXdgDialog, second, top)
112 expectProtocolError(t, c, dialogs, uint32(xdgdialog.WmDialogV1ErrorAlreadyUsed))
113}