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* 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 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 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 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 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 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 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 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 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 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