Commit 30baa0f7 authored by Dorota Czaplejewicz's avatar Dorota Czaplejewicz
Browse files

Switched main loop to glib

Switched the dbus object implementation in one go, as it's easier and more useful than making sd-bus work with glib.
parent 1ccbb925
Pipeline #1873 passed with stage
in 55 seconds
......@@ -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.
......@@ -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;
}
......@@ -37,6 +37,7 @@
#include <assert.h>
#include <time.h>
#include <cairo.h>
#include <glib-object.h>
#include <sys/mman.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
......@@ -71,6 +72,7 @@ typedef void *EGLContext;
#include <linux/input.h>
#include <wayland-client.h>
#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
......
......@@ -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,
)
......
/*
* 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 <errno.h>
#include <glib.h>
#include <wayland-client.h>
#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);