Commit da6ae011 authored by Dorota Czaplejewicz's avatar Dorota Czaplejewicz
Browse files

Merge branch 'glib' into 'master'

Switched main loop to glib

See merge request !24
parents 1ccbb925 30baa0f7
Pipeline #1941 passed with stage
in 50 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);