diff --git a/README.md b/README.md index 66de9909cd8cd3420e19f97ea3da70d2f2471c0f..152bee4ad64bb92ec031b083d127f87a438c6445 100644 --- a/README.md +++ b/README.md @@ -86,3 +86,8 @@ To listen to property changes: ``` busctl --user monitor --match path=/sm/puri/OSK0 ``` + +Third-party code +---------------- + +The files `shared/libgwater_wayland.c` and `shared/libgwater_wayland.h` come from the [libgwater](https://github.com/sardemff7/libgwater/tree/master/wayland) project. They should be never modified apart from updating. They should be updated together, and the commit message should specify the source libgwater revision. diff --git a/clients/keyboard.c b/clients/keyboard.c index 6a8d609ac03802cf3241e6cc3b96c6a89eb0d329..fed5f0c51a3af7ad5a788a18411a8fa2a3e32aab 100644 --- a/clients/keyboard.c +++ b/clients/keyboard.c @@ -51,6 +51,8 @@ #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "shared/xalloc.h" +#include "sm.puri.OSK0.h" + struct keyboard; @@ -297,12 +299,9 @@ struct size { static const char* dbus_bus_name = "sm.puri.OSK0"; static const char* dbus_path_name = "/sm/puri/OSK0"; -static const char* dbus_interface_name = "sm.puri.OSK0"; static GDBusProxy *_proxy; -static struct sd_bus *bus = NULL; -static bool dbus_visible = false; -struct task *tsk = NULL; +static SmPuriOSK0 *dbus_interface = NULL; static void __attribute__ ((format (printf, 1, 2))) dbg(const char *fmt, ...) @@ -322,14 +321,7 @@ dbg(const char *fmt, ...) static void notify_visible(bool visible) { - if (dbus_visible == visible) { - return; - } - dbus_visible = visible; - int ret = sd_bus_emit_properties_changed(bus, dbus_path_name, dbus_interface_name, "Visible", NULL); - if (ret < 0) { - fprintf(stderr, "Failed to notify of changed properties\n"); - } + sm_puri_osk0_set_visible(dbus_interface, visible); } static const char * @@ -1210,7 +1202,7 @@ session_register(void) { g_clear_error(&error); return; } - // No glib main loop, so synchronous call is *much* easier to use. + // Synchronous call was *much* easier to use before glib main loop. No need to change this though. g_dbus_proxy_call_sync(_proxy, "RegisterClient", g_variant_new("(ss)", "sm.puri.Virtboard", autostart_id), G_DBUS_CALL_FLAGS_NONE, 1000, NULL, &error); @@ -1222,89 +1214,74 @@ session_register(void) { } } -static void -bus_handle(struct task *t, uint32_t events) +static gboolean +handle_get_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation, + gpointer user_data) { - int ret; - while (true) { - ret = sd_bus_process(bus, NULL); - if (ret == 0) { - break; - } - if (ret < 0) { - fprintf(stderr, "Couldnt process dbus event\n"); - return; - } - } + sm_puri_osk0_complete_get_visible(object, invocation, + sm_puri_osk0_get_visible(object)); + return TRUE; } -static int -bus_handle_set_visible(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) +static gboolean +handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation, + gboolean arg_visible, gpointer user_data) { - struct virtual_keyboard *keyboard = userdata; - int value; - int ret = sd_bus_message_read(m, "b", &value); - if (ret < 0) { - fprintf(stderr, "Failed to read dbus argument\n"); - return ret; - } - - dbg("Received set message from dbus: %d\n", value); + struct virtual_keyboard *keyboard = user_data; + dbg("Received set message from dbus: %d\n", arg_visible); - if (value) { + if (arg_visible) { make_visible(keyboard); } else { make_hidden(keyboard); } - - return sd_bus_reply_method_return(m, NULL); + sm_puri_osk0_complete_set_visible(object, invocation); + return TRUE; } -static int -bus_handle_get_visible(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - return sd_bus_reply_method_return(m, "b", dbus_visible); +static void +bus_acquired(GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + // dbus docs say here the object should be registered via g_dbus_connection_register_object } -static int -bus_handle_property_visible(struct sd_bus *b, const char *path, const char *interface, const char *property, - struct sd_bus_message *reply, void *userdata, sd_bus_error *error) { - return sd_bus_message_append(reply, "b", dbus_visible); +static void +bus_name_acquired(GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + // but let's follow minminbus for now and register objects here + dbus_interface = sm_puri_osk0_skeleton_new(); + + sm_puri_osk0_set_visible(dbus_interface, false); // TODO: use actual value + + g_signal_connect(dbus_interface, "handle-get-visible", + G_CALLBACK(handle_get_visible), user_data); + g_signal_connect(dbus_interface, "handle-set-visible", + G_CALLBACK(handle_set_visible), user_data); + + GError *error; + if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(dbus_interface), + connection, + dbus_path_name, + &error)) { + fprintf(stderr, "Error registering dbus object: %s\n", error->message); + g_clear_error(&error); + exit(1); + } } -static struct sd_bus_vtable dbus_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("SetVisible", "b", NULL, bus_handle_set_visible, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetVisible", NULL, "b", bus_handle_get_visible, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("Visible", "b", bus_handle_property_visible, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_VTABLE_END, -}; - static int -bus_setup(struct display *display, struct virtual_keyboard *virtual_keyboard) +dbus_osk_setup(struct virtual_keyboard *virtual_keyboard) { - int ret = sd_bus_default_user(&bus); - if (ret < 0) { - fprintf(stderr, "Failed to connect to bus\n"); - return ret; - } - struct sd_bus_slot *slot; - ret = sd_bus_add_object_vtable(bus, &slot, - dbus_path_name, dbus_interface_name, - dbus_vtable, - virtual_keyboard); - if (ret < 0) { - fprintf(stderr, "Failed to create dbus object\n"); - return ret; - } - ret = sd_bus_request_name(bus, dbus_bus_name, 0); - if (ret < 0) { - fprintf(stderr, "Failed to claim bus name\n"); - return ret; - } - - tsk = calloc(1, sizeof(struct task)); - tsk->run = bus_handle; - display_watch_fd(display, sd_bus_get_fd(bus), EPOLLIN, tsk); + g_bus_own_name(G_BUS_TYPE_SESSION, + dbus_bus_name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired, bus_name_acquired, NULL, + virtual_keyboard, + NULL); return 0; } @@ -1351,18 +1328,18 @@ main(int argc, char *argv[]) make_input_method(&virtual_keyboard); } make_virtual_keyboard(&virtual_keyboard); + keyboard_create(&virtual_keyboard); - if (bus_setup(virtual_keyboard.display, &virtual_keyboard) != 0) { + if (dbus_osk_setup(&virtual_keyboard) != 0) { fprintf(stderr, "Could not register dbus service\n"); return -1; } - keyboard_create(&virtual_keyboard); + //keyboard_create(&virtual_keyboard); session_register(); display_run(virtual_keyboard.display); - free(tsk); return 0; } diff --git a/clients/window.c b/clients/window.c index b5a726d08c2556707bc815d63ae37c9a7ed578c8..1a26f3a69adf7aad78da9f4a61810195885ec4dd 100644 --- a/clients/window.c +++ b/clients/window.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,7 @@ typedef void *EGLContext; #include #include #include "shared/cairo-util.h" +#include "shared/libgwater-wayland.h" #include "shared/helpers.h" #include "shared/xalloc.h" #include "shared/zalloc.h" @@ -116,6 +118,10 @@ struct display { cairo_device_t *argb_device; uint32_t serial; + int fd; + GMainLoop *loop; + GWaterWaylandSource *source; + int display_fd; uint32_t display_fd_events; struct task display_task; @@ -4172,10 +4178,10 @@ surface_redraw(struct surface *surface) return 0; } -static void -idle_redraw(struct task *task, uint32_t events) +static gboolean +idle_redraw(void *data) { - struct window *window = container_of(task, struct window, redraw_task); + struct window *window = data; struct surface *surface; int failed = 0; int resized = 0; @@ -4189,7 +4195,7 @@ idle_redraw(struct task *task, uint32_t events) /* throttle resizing to the main surface display */ if (window->main_surface->frame_cb) { DBG_OBJ(window->main_surface->frame_cb, "pending\n"); - return; + return FALSE; } idle_resize(window); @@ -4222,6 +4228,7 @@ idle_redraw(struct task *task, uint32_t events) /* Restore widget tree to correspond to what is on screen. */ undo_resize(window); } + return FALSE; } static void @@ -4231,8 +4238,7 @@ window_schedule_redraw_task(struct window *window) return; if (!window->redraw_task_scheduled) { - window->redraw_task.run = idle_redraw; - display_defer(window->display, &window->redraw_task); + g_idle_add(idle_redraw, window); window->redraw_task_scheduled = 1; } } @@ -5786,43 +5792,6 @@ init_dummy_surface(struct display *display) display->dummy_surface_data = data; } -static void -handle_display_data(struct task *task, uint32_t events) -{ - struct display *display = - container_of(task, struct display, display_task); - struct epoll_event ep; - int ret; - - display->display_fd_events = events; - - if (events & EPOLLERR || events & EPOLLHUP) { - display_exit(display); - return; - } - - if (events & EPOLLIN) { - ret = wl_display_dispatch(display->display); - if (ret == -1) { - display_exit(display); - return; - } - } - - if (events & EPOLLOUT) { - ret = wl_display_flush(display->display); - if (ret == 0) { - ep.events = EPOLLIN | EPOLLERR | EPOLLHUP; - ep.data.ptr = &display->display_task; - epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, - display->display_fd, &ep); - } else if (ret == -1 && errno != EAGAIN) { - display_exit(display); - return; - } - } -} - static void log_handler(const char *format, va_list args) { @@ -5830,7 +5799,7 @@ log_handler(const char *format, va_list args) } struct display * - display_create(int *argc, char *argv[]) +display_create(int *argc, char *argv[]) { struct display *d; @@ -5854,13 +5823,6 @@ struct display * return NULL; } - d->epoll_fd = os_epoll_create_cloexec(); - d->display_fd = wl_display_get_fd(d->display); - d->display_task.run = handle_display_data; - display_watch_fd(d, d->display_fd, EPOLLIN | EPOLLERR | EPOLLHUP, - &d->display_task); - - wl_list_init(&d->deferred_list); wl_list_init(&d->input_list); wl_list_init(&d->output_list); wl_list_init(&d->global_list); @@ -5883,6 +5845,9 @@ struct display * d->theme = theme_create(); + d->loop = g_main_loop_new(NULL, FALSE); + d->source = g_water_wayland_source_new_for_display(NULL, d->display); + wl_list_init(&d->window_list); init_dummy_surface(d); @@ -6102,43 +6067,7 @@ display_unwatch_fd(struct display *display, int fd) void display_run(struct display *display) { - struct task *task; - struct epoll_event ep[16]; - int i, count, ret; - - display->running = 1; - while (1) { - while (!wl_list_empty(&display->deferred_list)) { - task = container_of(display->deferred_list.prev, - struct task, link); - wl_list_remove(&task->link); - task->run(task, 0); - } - - wl_display_dispatch_pending(display->display); - - if (!display->running) - break; - - ret = wl_display_flush(display->display); - if (ret < 0 && errno == EAGAIN) { - ep[0].events = - EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP; - ep[0].data.ptr = &display->display_task; - - epoll_ctl(display->epoll_fd, EPOLL_CTL_MOD, - display->display_fd, &ep[0]); - } else if (ret < 0) { - break; - } - - count = epoll_wait(display->epoll_fd, - ep, ARRAY_LENGTH(ep), -1); - for (i = 0; i < count; i++) { - task = ep[i].data.ptr; - task->run(task, ep[i].events); - } - } + g_main_loop_run(display->loop); } void diff --git a/meson.build b/meson.build index 0e92809ee4527a050c7347814a162e7334f13062..c148055f80b3000dd3e6bf5f1d6fb5be2cc6e425 100644 --- a/meson.build +++ b/meson.build @@ -61,7 +61,7 @@ math = cc.find_library('m', required: false) pixman = dependency('pixman-1') png = dependency('libpng') gio = dependency('gio-2.0') -systemd = dependency('libsystemd') +gio_unix = dependency('gio-unix-2.0') wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') @@ -108,6 +108,12 @@ foreach p : protocols wl_protos_headers += wayland_scanner_client.process(xml) endforeach +gnome = import('gnome') +dbus_src = gnome.gdbus_codegen( + 'sm.puri.OSK0', + 'data/dbus/sm.puri.Virtboard.xml' +) + sources = [ 'clients/keyboard.c', 'clients/window.c', @@ -118,17 +124,19 @@ sources = [ 'shared/frame.c', 'shared/image-loader.c', 'shared/file-util.c', + 'shared/libgwater-wayland.c' ] sources += wl_protos_src sources += wl_protos_headers +sources += dbus_src add_global_arguments('-DLIBEXECDIR=""', language : 'c') add_global_arguments('-DDATADIR=""', language : 'c') executable( 'virtboard', sources, - dependencies: [png, math, pixman, wayland_cursor, wayland_client, wayland_protos, xkbcommon, cairo, gio, systemd], + dependencies: [png, math, pixman, wayland_cursor, wayland_client, wayland_protos, xkbcommon, cairo, gio, gio_unix], install: true, ) diff --git a/shared/libgwater-wayland.c b/shared/libgwater-wayland.c new file mode 100644 index 0000000000000000000000000000000000000000..3786bd4dab7a69e3cf46d62e631584f621633c7e --- /dev/null +++ b/shared/libgwater-wayland.c @@ -0,0 +1,193 @@ +/* + * libgwater-wayland - Wayland GSource + * + * Copyright © 2014-2017 Quentin "Sardem FF7" Glidic + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef G_LOG_DOMAIN +#undef G_LOG_DOMAIN +#endif /* G_LOG_DOMAIN */ +#define G_LOG_DOMAIN "GWaterWayland" + +#include + +#include + +#include + +#include "libgwater-wayland.h" + +struct _GWaterWaylandSource { + GSource source; + gboolean display_owned; + struct wl_display *display; + gpointer fd; + int error; +}; + +static gboolean +_g_water_wayland_source_prepare(GSource *source, gint *timeout) +{ + GWaterWaylandSource *self = (GWaterWaylandSource *)source; + + *timeout = 0; + if ( wl_display_prepare_read(self->display) != 0 ) + return TRUE; + else if ( wl_display_flush(self->display) < 0 ) + { + self->error = errno; + return TRUE; + } + + *timeout = -1; + return FALSE; +} + +static gboolean +_g_water_wayland_source_check(GSource *source) +{ + GWaterWaylandSource *self = (GWaterWaylandSource *)source; + + if ( self->error > 0 ) + return TRUE; + + GIOCondition revents; + revents = g_source_query_unix_fd(source, self->fd); + + if ( revents & G_IO_IN ) + { + if ( wl_display_read_events(self->display) < 0 ) + self->error = errno; + } + else + wl_display_cancel_read(self->display); + + return ( revents > 0 ); +} + +static gboolean +_g_water_wayland_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) +{ + GWaterWaylandSource *self = (GWaterWaylandSource *)source; + GIOCondition revents; + + revents = g_source_query_unix_fd(source, self->fd); + if ( ( self->error > 0 ) || ( revents & (G_IO_ERR | G_IO_HUP) ) ) + { + errno = self->error; + self->error = 0; + if ( callback != NULL ) + return callback(user_data); + return G_SOURCE_REMOVE; + } + + if ( wl_display_dispatch_pending(self->display) < 0 ) + { + if ( callback != NULL ) + return callback(user_data); + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +static void +_g_water_wayland_source_finalize(GSource *source) +{ + GWaterWaylandSource *self = (GWaterWaylandSource *)source; + + if ( self->display_owned ) + wl_display_disconnect(self->display); +} + +static GSourceFuncs _g_water_wayland_source_funcs = { + .prepare = _g_water_wayland_source_prepare, + .check = _g_water_wayland_source_check, + .dispatch = _g_water_wayland_source_dispatch, + .finalize = _g_water_wayland_source_finalize, +}; + +GWaterWaylandSource * +g_water_wayland_source_new(GMainContext *context, const gchar *name) +{ + struct wl_display *display; + GWaterWaylandSource *self; + + display = wl_display_connect(name); + if ( display == NULL ) + return NULL; + + self = g_water_wayland_source_new_for_display(context, display); + self->display_owned = TRUE; + return self; +} + +GWaterWaylandSource * +g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display) +{ + g_return_val_if_fail(display != NULL, NULL); + + GSource *source; + GWaterWaylandSource *self; + + source = g_source_new(&_g_water_wayland_source_funcs, sizeof(GWaterWaylandSource)); + self = (GWaterWaylandSource *)source; + self->display = display; + + self->fd = g_source_add_unix_fd(source, wl_display_get_fd(self->display), G_IO_IN | G_IO_ERR | G_IO_HUP); + + g_source_attach(source, context); + + return self; +} + +void +g_water_wayland_source_free(GWaterWaylandSource *self) +{ + GSource * source = (GSource *)self; + g_return_if_fail(self != NULL); + + g_source_destroy(source); + + g_source_unref(source); +} + +void +g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify) +{ + g_return_if_fail(self != NULL); + + g_source_set_callback((GSource *)self, callback, user_data, destroy_notify); +} + +struct wl_display * +g_water_wayland_source_get_display(GWaterWaylandSource *self) +{ + g_return_val_if_fail(self != NULL, NULL); + + return self->display; +} + diff --git a/shared/libgwater-wayland.h b/shared/libgwater-wayland.h new file mode 100644 index 0000000000000000000000000000000000000000..e2aa7f8d0e2a127b502965055e4624c29485f1b8 --- /dev/null +++ b/shared/libgwater-wayland.h @@ -0,0 +1,43 @@ +/* + * libgwater-wayland - Wayland GSource + * + * Copyright © 2014-2017 Quentin "Sardem FF7" Glidic + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef __G_WATER_WAYLAND_H__ +#define __G_WATER_WAYLAND_H__ + +G_BEGIN_DECLS + +typedef struct _GWaterWaylandSource GWaterWaylandSource; + +GWaterWaylandSource *g_water_wayland_source_new(GMainContext *context, const gchar *name); +GWaterWaylandSource *g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display); +void g_water_wayland_source_free(GWaterWaylandSource *self); + +void g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify); +struct wl_display *g_water_wayland_source_get_display(GWaterWaylandSource *source); + +G_END_DECLS + +#endif /* __G_WATER_WAYLAND_H__ */ +