1b4dd5b6aSMarc-André Lureau #include "qemu/osdep.h"
2*306d7581SMarc-André Lureau #include "qemu/sockets.h"
3b4dd5b6aSMarc-André Lureau #include "qemu/dbus.h"
40038e9a2SGuoyi Tu #include "qemu/sockets.h"
5b4dd5b6aSMarc-André Lureau #include <gio/gio.h>
6b4dd5b6aSMarc-André Lureau #include <gio/gunixfdlist.h>
7907b5105SMarc-André Lureau #include "libqtest.h"
80e902f59SPaolo Bonzini #include "ui/dbus-display1.h"
9b4dd5b6aSMarc-André Lureau 
10b4dd5b6aSMarc-André Lureau static GDBusConnection*
test_dbus_p2p_from_fd(int fd)11b4dd5b6aSMarc-André Lureau test_dbus_p2p_from_fd(int fd)
12b4dd5b6aSMarc-André Lureau {
13b4dd5b6aSMarc-André Lureau     g_autoptr(GError) err = NULL;
14b4dd5b6aSMarc-André Lureau     g_autoptr(GSocket) socket = NULL;
15b4dd5b6aSMarc-André Lureau     g_autoptr(GSocketConnection) socketc = NULL;
16b4dd5b6aSMarc-André Lureau     GDBusConnection *conn;
17b4dd5b6aSMarc-André Lureau 
18*306d7581SMarc-André Lureau #ifdef WIN32
19*306d7581SMarc-André Lureau     socket = g_socket_new_from_fd(_get_osfhandle(fd), &err);
20*306d7581SMarc-André Lureau #else
21b4dd5b6aSMarc-André Lureau     socket = g_socket_new_from_fd(fd, &err);
22*306d7581SMarc-André Lureau #endif
23b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
24b4dd5b6aSMarc-André Lureau 
25b4dd5b6aSMarc-André Lureau     socketc = g_socket_connection_factory_create_connection(socket);
26b4dd5b6aSMarc-André Lureau     g_assert(socketc != NULL);
27b4dd5b6aSMarc-André Lureau 
28b4dd5b6aSMarc-André Lureau     conn = g_dbus_connection_new_sync(
29b4dd5b6aSMarc-André Lureau         G_IO_STREAM(socketc), NULL,
30b4dd5b6aSMarc-André Lureau         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
31b4dd5b6aSMarc-André Lureau         G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING,
32b4dd5b6aSMarc-André Lureau         NULL, NULL, &err);
33b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
34b4dd5b6aSMarc-André Lureau 
35b4dd5b6aSMarc-André Lureau     return conn;
36b4dd5b6aSMarc-André Lureau }
37b4dd5b6aSMarc-André Lureau 
38b4dd5b6aSMarc-André Lureau static void
test_setup(QTestState ** qts,GDBusConnection ** conn)39b4dd5b6aSMarc-André Lureau test_setup(QTestState **qts, GDBusConnection **conn)
40b4dd5b6aSMarc-André Lureau {
41b4dd5b6aSMarc-André Lureau     int pair[2];
42b4dd5b6aSMarc-André Lureau 
43b4dd5b6aSMarc-André Lureau     *qts = qtest_init("-display dbus,p2p=yes -name dbus-test");
44b4dd5b6aSMarc-André Lureau 
450038e9a2SGuoyi Tu     g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
46b4dd5b6aSMarc-André Lureau 
47b4dd5b6aSMarc-André Lureau     qtest_qmp_add_client(*qts, "@dbus-display", pair[1]);
48b4dd5b6aSMarc-André Lureau 
49b4dd5b6aSMarc-André Lureau     *conn = test_dbus_p2p_from_fd(pair[0]);
50b4dd5b6aSMarc-André Lureau     g_dbus_connection_start_message_processing(*conn);
51b4dd5b6aSMarc-André Lureau }
52b4dd5b6aSMarc-André Lureau 
53b4dd5b6aSMarc-André Lureau static void
test_dbus_display_vm(void)54b4dd5b6aSMarc-André Lureau test_dbus_display_vm(void)
55b4dd5b6aSMarc-André Lureau {
56b4dd5b6aSMarc-André Lureau     g_autoptr(GError) err = NULL;
57b4dd5b6aSMarc-André Lureau     g_autoptr(GDBusConnection) conn = NULL;
58b4dd5b6aSMarc-André Lureau     g_autoptr(QemuDBusDisplay1VMProxy) vm = NULL;
59b4dd5b6aSMarc-André Lureau     QTestState *qts = NULL;
60b4dd5b6aSMarc-André Lureau 
61b4dd5b6aSMarc-André Lureau     test_setup(&qts, &conn);
62b4dd5b6aSMarc-André Lureau 
63b4dd5b6aSMarc-André Lureau     vm = QEMU_DBUS_DISPLAY1_VM_PROXY(
64b4dd5b6aSMarc-André Lureau         qemu_dbus_display1_vm_proxy_new_sync(
65b4dd5b6aSMarc-André Lureau             conn,
66b4dd5b6aSMarc-André Lureau             G_DBUS_PROXY_FLAGS_NONE,
67b4dd5b6aSMarc-André Lureau             NULL,
68b4dd5b6aSMarc-André Lureau             DBUS_DISPLAY1_ROOT "/VM",
69b4dd5b6aSMarc-André Lureau             NULL,
70b4dd5b6aSMarc-André Lureau             &err));
71b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
72b4dd5b6aSMarc-André Lureau 
73b4dd5b6aSMarc-André Lureau     g_assert_cmpstr(
74b4dd5b6aSMarc-André Lureau         qemu_dbus_display1_vm_get_name(QEMU_DBUS_DISPLAY1_VM(vm)),
75b4dd5b6aSMarc-André Lureau         ==,
76b4dd5b6aSMarc-André Lureau         "dbus-test");
77b4dd5b6aSMarc-André Lureau     qtest_quit(qts);
78b4dd5b6aSMarc-André Lureau }
79b4dd5b6aSMarc-André Lureau 
80b4dd5b6aSMarc-André Lureau typedef struct TestDBusConsoleRegister {
81b4dd5b6aSMarc-André Lureau     GMainLoop *loop;
82b4dd5b6aSMarc-André Lureau     GThread *thread;
83b4dd5b6aSMarc-André Lureau     GDBusConnection *listener_conn;
84b4dd5b6aSMarc-André Lureau     GDBusObjectManagerServer *server;
85b4dd5b6aSMarc-André Lureau } TestDBusConsoleRegister;
86b4dd5b6aSMarc-André Lureau 
listener_handle_scanout(QemuDBusDisplay1Listener * object,GDBusMethodInvocation * invocation,guint arg_width,guint arg_height,guint arg_stride,guint arg_pixman_format,GVariant * arg_data,TestDBusConsoleRegister * test)87b4dd5b6aSMarc-André Lureau static gboolean listener_handle_scanout(
88b4dd5b6aSMarc-André Lureau     QemuDBusDisplay1Listener *object,
89b4dd5b6aSMarc-André Lureau     GDBusMethodInvocation *invocation,
90b4dd5b6aSMarc-André Lureau     guint arg_width,
91b4dd5b6aSMarc-André Lureau     guint arg_height,
92b4dd5b6aSMarc-André Lureau     guint arg_stride,
93b4dd5b6aSMarc-André Lureau     guint arg_pixman_format,
94b4dd5b6aSMarc-André Lureau     GVariant *arg_data,
95b4dd5b6aSMarc-André Lureau     TestDBusConsoleRegister *test)
96b4dd5b6aSMarc-André Lureau {
97b4dd5b6aSMarc-André Lureau     g_main_loop_quit(test->loop);
98b4dd5b6aSMarc-André Lureau 
99b4dd5b6aSMarc-André Lureau     return DBUS_METHOD_INVOCATION_HANDLED;
100b4dd5b6aSMarc-André Lureau }
101b4dd5b6aSMarc-André Lureau 
102b4dd5b6aSMarc-André Lureau static void
test_dbus_console_setup_listener(TestDBusConsoleRegister * test)103b4dd5b6aSMarc-André Lureau test_dbus_console_setup_listener(TestDBusConsoleRegister *test)
104b4dd5b6aSMarc-André Lureau {
105b4dd5b6aSMarc-André Lureau     g_autoptr(GDBusObjectSkeleton) listener = NULL;
106b4dd5b6aSMarc-André Lureau     g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL;
107b4dd5b6aSMarc-André Lureau 
108b4dd5b6aSMarc-André Lureau     test->server = g_dbus_object_manager_server_new(DBUS_DISPLAY1_ROOT);
109b4dd5b6aSMarc-André Lureau     listener = g_dbus_object_skeleton_new(DBUS_DISPLAY1_ROOT "/Listener");
110b4dd5b6aSMarc-André Lureau     iface = QEMU_DBUS_DISPLAY1_LISTENER_SKELETON(
111b4dd5b6aSMarc-André Lureau         qemu_dbus_display1_listener_skeleton_new());
112b4dd5b6aSMarc-André Lureau     g_object_connect(iface,
113b4dd5b6aSMarc-André Lureau                      "signal::handle-scanout", listener_handle_scanout, test,
114b4dd5b6aSMarc-André Lureau                      NULL);
115b4dd5b6aSMarc-André Lureau     g_dbus_object_skeleton_add_interface(listener,
116b4dd5b6aSMarc-André Lureau                                          G_DBUS_INTERFACE_SKELETON(iface));
117b4dd5b6aSMarc-André Lureau     g_dbus_object_manager_server_export(test->server, listener);
118b4dd5b6aSMarc-André Lureau     g_dbus_object_manager_server_set_connection(test->server,
119b4dd5b6aSMarc-André Lureau                                                 test->listener_conn);
120b4dd5b6aSMarc-André Lureau 
121b4dd5b6aSMarc-André Lureau     g_dbus_connection_start_message_processing(test->listener_conn);
122b4dd5b6aSMarc-André Lureau }
123b4dd5b6aSMarc-André Lureau 
124b4dd5b6aSMarc-André Lureau static void
test_dbus_console_registered(GObject * source_object,GAsyncResult * res,gpointer user_data)125b4dd5b6aSMarc-André Lureau test_dbus_console_registered(GObject *source_object,
126b4dd5b6aSMarc-André Lureau                              GAsyncResult *res,
127b4dd5b6aSMarc-André Lureau                              gpointer user_data)
128b4dd5b6aSMarc-André Lureau {
129b4dd5b6aSMarc-André Lureau     TestDBusConsoleRegister *test = user_data;
130b4dd5b6aSMarc-André Lureau     g_autoptr(GError) err = NULL;
131b4dd5b6aSMarc-André Lureau 
132b4dd5b6aSMarc-André Lureau     qemu_dbus_display1_console_call_register_listener_finish(
133b4dd5b6aSMarc-André Lureau         QEMU_DBUS_DISPLAY1_CONSOLE(source_object),
134*306d7581SMarc-André Lureau #ifndef WIN32
135*306d7581SMarc-André Lureau         NULL,
136*306d7581SMarc-André Lureau #endif
137*306d7581SMarc-André Lureau         res, &err);
138b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
139b4dd5b6aSMarc-André Lureau 
140b4dd5b6aSMarc-André Lureau     test->listener_conn = g_thread_join(test->thread);
141b4dd5b6aSMarc-André Lureau     test_dbus_console_setup_listener(test);
142b4dd5b6aSMarc-André Lureau }
143b4dd5b6aSMarc-André Lureau 
144b4dd5b6aSMarc-André Lureau static gpointer
test_dbus_p2p_server_setup_thread(gpointer data)145b4dd5b6aSMarc-André Lureau test_dbus_p2p_server_setup_thread(gpointer data)
146b4dd5b6aSMarc-André Lureau {
147b4dd5b6aSMarc-André Lureau     return test_dbus_p2p_from_fd(GPOINTER_TO_INT(data));
148b4dd5b6aSMarc-André Lureau }
149b4dd5b6aSMarc-André Lureau 
150b4dd5b6aSMarc-André Lureau static void
test_dbus_display_console(void)151b4dd5b6aSMarc-André Lureau test_dbus_display_console(void)
152b4dd5b6aSMarc-André Lureau {
153b4dd5b6aSMarc-André Lureau     g_autoptr(GError) err = NULL;
154b4dd5b6aSMarc-André Lureau     g_autoptr(GDBusConnection) conn = NULL;
155b4dd5b6aSMarc-André Lureau     g_autoptr(QemuDBusDisplay1ConsoleProxy) console = NULL;
156b4dd5b6aSMarc-André Lureau     g_autoptr(GMainLoop) loop = NULL;
157b4dd5b6aSMarc-André Lureau     QTestState *qts = NULL;
158*306d7581SMarc-André Lureau     int pair[2];
159b4dd5b6aSMarc-André Lureau     TestDBusConsoleRegister test;
160*306d7581SMarc-André Lureau #ifdef WIN32
161*306d7581SMarc-André Lureau     WSAPROTOCOL_INFOW info;
162*306d7581SMarc-André Lureau     g_autoptr(GVariant) listener = NULL;
163*306d7581SMarc-André Lureau #else
164*306d7581SMarc-André Lureau     g_autoptr(GUnixFDList) fd_list = NULL;
165*306d7581SMarc-André Lureau     int idx;
166*306d7581SMarc-André Lureau #endif
167b4dd5b6aSMarc-André Lureau 
168b4dd5b6aSMarc-André Lureau     test_setup(&qts, &conn);
169b4dd5b6aSMarc-André Lureau 
1700038e9a2SGuoyi Tu     g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0);
171*306d7581SMarc-André Lureau #ifndef WIN32
172b4dd5b6aSMarc-André Lureau     fd_list = g_unix_fd_list_new();
173b4dd5b6aSMarc-André Lureau     idx = g_unix_fd_list_append(fd_list, pair[1], NULL);
174*306d7581SMarc-André Lureau #endif
175b4dd5b6aSMarc-André Lureau 
176b4dd5b6aSMarc-André Lureau     console = QEMU_DBUS_DISPLAY1_CONSOLE_PROXY(
177b4dd5b6aSMarc-André Lureau         qemu_dbus_display1_console_proxy_new_sync(
178b4dd5b6aSMarc-André Lureau             conn,
179b4dd5b6aSMarc-André Lureau             G_DBUS_PROXY_FLAGS_NONE,
180b4dd5b6aSMarc-André Lureau             NULL,
181b4dd5b6aSMarc-André Lureau             "/org/qemu/Display1/Console_0",
182b4dd5b6aSMarc-André Lureau             NULL,
183b4dd5b6aSMarc-André Lureau             &err));
184b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
185b4dd5b6aSMarc-André Lureau 
186b4dd5b6aSMarc-André Lureau     test.loop = loop = g_main_loop_new(NULL, FALSE);
187b4dd5b6aSMarc-André Lureau     test.thread = g_thread_new(NULL, test_dbus_p2p_server_setup_thread,
188b4dd5b6aSMarc-André Lureau                                GINT_TO_POINTER(pair[0]));
189b4dd5b6aSMarc-André Lureau 
190*306d7581SMarc-André Lureau #ifdef WIN32
191*306d7581SMarc-André Lureau     if (WSADuplicateSocketW(_get_osfhandle(pair[1]),
192*306d7581SMarc-André Lureau                             GetProcessId((HANDLE) qtest_pid(qts)),
193*306d7581SMarc-André Lureau                             &info) == SOCKET_ERROR)
194*306d7581SMarc-André Lureau     {
195*306d7581SMarc-André Lureau         g_autofree char *emsg = g_win32_error_message(WSAGetLastError());
196*306d7581SMarc-André Lureau         g_error("WSADuplicateSocket failed: %s", emsg);
197*306d7581SMarc-André Lureau     }
198*306d7581SMarc-André Lureau     close(pair[1]);
199*306d7581SMarc-André Lureau     listener = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
200*306d7581SMarc-André Lureau                                          &info,
201*306d7581SMarc-André Lureau                                          sizeof(info),
202*306d7581SMarc-André Lureau                                          1);
203*306d7581SMarc-André Lureau #endif
204*306d7581SMarc-André Lureau 
205b4dd5b6aSMarc-André Lureau     qemu_dbus_display1_console_call_register_listener(
206b4dd5b6aSMarc-André Lureau         QEMU_DBUS_DISPLAY1_CONSOLE(console),
207*306d7581SMarc-André Lureau #ifdef WIN32
208*306d7581SMarc-André Lureau         listener,
209*306d7581SMarc-André Lureau #else
210b4dd5b6aSMarc-André Lureau         g_variant_new_handle(idx),
211*306d7581SMarc-André Lureau #endif
212b4dd5b6aSMarc-André Lureau         G_DBUS_CALL_FLAGS_NONE,
213b4dd5b6aSMarc-André Lureau         -1,
214*306d7581SMarc-André Lureau #ifndef WIN32
215b4dd5b6aSMarc-André Lureau         fd_list,
216*306d7581SMarc-André Lureau #endif
217b4dd5b6aSMarc-André Lureau         NULL,
218b4dd5b6aSMarc-André Lureau         test_dbus_console_registered,
219b4dd5b6aSMarc-André Lureau         &test);
220b4dd5b6aSMarc-André Lureau 
221b4dd5b6aSMarc-André Lureau     g_main_loop_run(loop);
222b4dd5b6aSMarc-André Lureau 
223b4dd5b6aSMarc-André Lureau     g_clear_object(&test.server);
224b4dd5b6aSMarc-André Lureau     g_clear_object(&test.listener_conn);
225b4dd5b6aSMarc-André Lureau     qtest_quit(qts);
226b4dd5b6aSMarc-André Lureau }
227b4dd5b6aSMarc-André Lureau 
228b4dd5b6aSMarc-André Lureau static void
test_dbus_display_keyboard(void)229b4dd5b6aSMarc-André Lureau test_dbus_display_keyboard(void)
230b4dd5b6aSMarc-André Lureau {
231b4dd5b6aSMarc-André Lureau     g_autoptr(GError) err = NULL;
232b4dd5b6aSMarc-André Lureau     g_autoptr(GDBusConnection) conn = NULL;
233b4dd5b6aSMarc-André Lureau     g_autoptr(QemuDBusDisplay1KeyboardProxy) keyboard = NULL;
234b4dd5b6aSMarc-André Lureau     QTestState *qts = NULL;
235b4dd5b6aSMarc-André Lureau 
236b4dd5b6aSMarc-André Lureau     test_setup(&qts, &conn);
237b4dd5b6aSMarc-André Lureau 
238b4dd5b6aSMarc-André Lureau     keyboard = QEMU_DBUS_DISPLAY1_KEYBOARD_PROXY(
239b4dd5b6aSMarc-André Lureau         qemu_dbus_display1_keyboard_proxy_new_sync(
240b4dd5b6aSMarc-André Lureau             conn,
241b4dd5b6aSMarc-André Lureau             G_DBUS_PROXY_FLAGS_NONE,
242b4dd5b6aSMarc-André Lureau             NULL,
243b4dd5b6aSMarc-André Lureau             "/org/qemu/Display1/Console_0",
244b4dd5b6aSMarc-André Lureau             NULL,
245b4dd5b6aSMarc-André Lureau             &err));
246b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
247b4dd5b6aSMarc-André Lureau 
248b4dd5b6aSMarc-André Lureau 
249b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 0);
250b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0);
251b4dd5b6aSMarc-André Lureau 
252b4dd5b6aSMarc-André Lureau     qemu_dbus_display1_keyboard_call_press_sync(
253b4dd5b6aSMarc-André Lureau         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
254b4dd5b6aSMarc-André Lureau         0x1C, /* qnum enter */
255b4dd5b6aSMarc-André Lureau         G_DBUS_CALL_FLAGS_NONE,
256b4dd5b6aSMarc-André Lureau         -1,
257b4dd5b6aSMarc-André Lureau         NULL,
258b4dd5b6aSMarc-André Lureau         &err);
259b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
260b4dd5b6aSMarc-André Lureau 
261b4dd5b6aSMarc-André Lureau     /* may be should wait for interrupt? */
262b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
263b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
264b4dd5b6aSMarc-André Lureau 
265b4dd5b6aSMarc-André Lureau     qemu_dbus_display1_keyboard_call_release_sync(
266b4dd5b6aSMarc-André Lureau         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard),
267b4dd5b6aSMarc-André Lureau         0x1C, /* qnum enter */
268b4dd5b6aSMarc-André Lureau         G_DBUS_CALL_FLAGS_NONE,
269b4dd5b6aSMarc-André Lureau         -1,
270b4dd5b6aSMarc-André Lureau         NULL,
271b4dd5b6aSMarc-André Lureau         &err);
272b4dd5b6aSMarc-André Lureau     g_assert_no_error(err);
273b4dd5b6aSMarc-André Lureau 
274b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x64) & 0x1, ==, 1);
275b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0xF0); /* scan code 2 release */
276b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qtest_inb(qts, 0x60), ==, 0x5A); /* scan code 2 enter */
277b4dd5b6aSMarc-André Lureau 
278b4dd5b6aSMarc-André Lureau     g_assert_cmpint(qemu_dbus_display1_keyboard_get_modifiers(
279b4dd5b6aSMarc-André Lureau                         QEMU_DBUS_DISPLAY1_KEYBOARD(keyboard)), ==, 0);
280b4dd5b6aSMarc-André Lureau 
281b4dd5b6aSMarc-André Lureau     qtest_quit(qts);
282b4dd5b6aSMarc-André Lureau }
283b4dd5b6aSMarc-André Lureau 
284b4dd5b6aSMarc-André Lureau int
main(int argc,char ** argv)285b4dd5b6aSMarc-André Lureau main(int argc, char **argv)
286b4dd5b6aSMarc-André Lureau {
287b4dd5b6aSMarc-André Lureau     g_test_init(&argc, &argv, NULL);
288b4dd5b6aSMarc-André Lureau 
289b4dd5b6aSMarc-André Lureau     qtest_add_func("/dbus-display/vm", test_dbus_display_vm);
290b4dd5b6aSMarc-André Lureau     qtest_add_func("/dbus-display/console", test_dbus_display_console);
291b4dd5b6aSMarc-André Lureau     qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard);
292b4dd5b6aSMarc-André Lureau 
293b4dd5b6aSMarc-André Lureau     return g_test_run();
294b4dd5b6aSMarc-André Lureau }
295