summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2009-10-04 10:47:36 -0400
committerRay Strode <rstrode@redhat.com>2009-10-04 10:47:36 -0400
commita29f25bc6c67e87f38f323e8739f06249205d766 (patch)
tree6d648dbcc6f144e3d3436bc77436f629341cde90
parente73e8cadd6b2b28e448710cffd85e32eb2083c83 (diff)
parent08466deaf8203ddff508d492056d2e9f705724e9 (diff)
[branch-merge] Add x11 renderer plugin
This merges the "x11-renderer" branch to master. In order to make debugging splash plugins easier, and in order to make debugging multi-head renderering possible on single head displays, Charlie wrote an X11 rendering plugin. This plugin displays plymouth in a running X session, instead of on the console. It currently only supports graphical plugins. At some point it may grow support for text plugins, too, using vte. That will take some reworking of the plymouth daemon core. This could some day serve as a basis for providing a graphical theme chooser application (like gnome-screensaver-preferences), although it's not clear yet that's a good idea. Right now, it's a great debugging and development tool, though.
-rw-r--r--configure.ac1
-rw-r--r--src/libplybootsplash/ply-boot-splash.c10
-rw-r--r--src/libplybootsplash/ply-console.c12
-rw-r--r--src/libplybootsplash/ply-console.h4
-rw-r--r--src/libplybootsplash/ply-renderer.c5
-rw-r--r--src/main.c3
-rw-r--r--src/plugins/renderers/Makefile.am2
-rw-r--r--src/plugins/renderers/drm/plugin.c7
-rw-r--r--src/plugins/renderers/frame-buffer/plugin.c7
-rw-r--r--src/plugins/renderers/x11/Makefile.am20
-rw-r--r--src/plugins/renderers/x11/plugin.c523
11 files changed, 576 insertions, 18 deletions
diff --git a/configure.ac b/configure.ac
index 318c80e2..e257b9d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -216,6 +216,7 @@ AC_OUTPUT([Makefile
src/plugins/renderers/Makefile
src/plugins/renderers/frame-buffer/Makefile
src/plugins/renderers/drm/Makefile
+ src/plugins/renderers/x11/Makefile
src/plugins/splash/Makefile
src/plugins/splash/throbgress/Makefile
src/plugins/splash/fade-throbber/Makefile
diff --git a/src/libplybootsplash/ply-boot-splash.c b/src/libplybootsplash/ply-boot-splash.c
index 72ee7f02..5ba1bc1d 100644
--- a/src/libplybootsplash/ply-boot-splash.c
+++ b/src/libplybootsplash/ply-boot-splash.c
@@ -169,8 +169,14 @@ on_keyboard_input (ply_boot_splash_t *splash,
case KEY_CTRL_T:
ply_trace ("toggle text mode!");
splash->should_force_text_mode = !splash->should_force_text_mode;
- ply_console_force_text_mode (splash->console,
- splash->should_force_text_mode);
+
+ if (splash->should_force_text_mode)
+ {
+ ply_console_set_mode (splash->console, PLY_CONSOLE_MODE_TEXT);
+ ply_console_ignore_mode_changes (splash->console, true);
+ }
+ else
+ ply_console_ignore_mode_changes (splash->console, false);
ply_trace ("text mode toggled!");
return;
diff --git a/src/libplybootsplash/ply-console.c b/src/libplybootsplash/ply-console.c
index 7fb92980..fb5a86db 100644
--- a/src/libplybootsplash/ply-console.c
+++ b/src/libplybootsplash/ply-console.c
@@ -68,7 +68,7 @@ struct _ply_console
uint32_t is_open : 1;
uint32_t is_watching_for_vt_changes : 1;
- uint32_t should_force_text_mode : 1;
+ uint32_t should_ignore_mode_changes : 1;
};
static bool ply_console_open_device (ply_console_t *console);
@@ -106,8 +106,8 @@ ply_console_set_mode (ply_console_t *console,
assert (console != NULL);
assert (mode == PLY_CONSOLE_MODE_TEXT || mode == PLY_CONSOLE_MODE_GRAPHICS);
- if (console->should_force_text_mode)
- mode = PLY_CONSOLE_MODE_TEXT;
+ if (console->should_ignore_mode_changes)
+ return;
switch (mode)
{
@@ -124,10 +124,10 @@ ply_console_set_mode (ply_console_t *console,
}
void
-ply_console_force_text_mode (ply_console_t *console,
- bool should_force)
+ply_console_ignore_mode_changes (ply_console_t *console,
+ bool should_ignore)
{
- console->should_force_text_mode = should_force;
+ console->should_ignore_mode_changes = should_ignore;
}
static void
diff --git a/src/libplybootsplash/ply-console.h b/src/libplybootsplash/ply-console.h
index 36263bbc..4b45c86e 100644
--- a/src/libplybootsplash/ply-console.h
+++ b/src/libplybootsplash/ply-console.h
@@ -49,8 +49,8 @@ void ply_console_close (ply_console_t *console);
void ply_console_set_mode (ply_console_t *console,
ply_console_mode_t mode);
-void ply_console_force_text_mode (ply_console_t *console,
- bool should_force);
+void ply_console_ignore_mode_changes (ply_console_t *console,
+ bool should_ignore);
int ply_console_get_fd (ply_console_t *console);
int ply_console_get_active_vt (ply_console_t *console);
diff --git a/src/libplybootsplash/ply-renderer.c b/src/libplybootsplash/ply-renderer.c
index 5410feb8..08a246ba 100644
--- a/src/libplybootsplash/ply-renderer.c
+++ b/src/libplybootsplash/ply-renderer.c
@@ -220,6 +220,7 @@ ply_renderer_open (ply_renderer_t *renderer)
*/
const char *known_plugins[] =
{
+ PLYMOUTH_PLUGIN_PATH "renderers/x11.so",
PLYMOUTH_PLUGIN_PATH "renderers/drm.so",
PLYMOUTH_PLUGIN_PATH "renderers/frame-buffer.so",
NULL
@@ -299,10 +300,6 @@ ply_renderer_flush_head (ply_renderer_t *renderer,
assert (renderer->plugin_interface != NULL);
assert (head != NULL);
- if (ply_console_get_active_vt (renderer->console) !=
- ply_terminal_get_vt_number (renderer->terminal))
- return;
-
renderer->plugin_interface->flush_head (renderer->backend, head);
}
diff --git a/src/main.c b/src/main.c
index d79b816e..08ec1ae4 100644
--- a/src/main.c
+++ b/src/main.c
@@ -989,9 +989,6 @@ add_default_displays_and_keyboard (state_t *state)
return;
}
- ply_console_set_active_vt (state->console,
- ply_terminal_get_vt_number (terminal));
-
renderer = ply_renderer_new (NULL, terminal, state->console);
if (!ply_renderer_open (renderer))
diff --git a/src/plugins/renderers/Makefile.am b/src/plugins/renderers/Makefile.am
index 2fadbf40..5a449a79 100644
--- a/src/plugins/renderers/Makefile.am
+++ b/src/plugins/renderers/Makefile.am
@@ -1,2 +1,2 @@
-SUBDIRS = frame-buffer drm
+SUBDIRS = frame-buffer drm x11
MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c
index 2c5e1aa9..05e36fa2 100644
--- a/src/plugins/renderers/drm/plugin.c
+++ b/src/plugins/renderers/drm/plugin.c
@@ -783,6 +783,9 @@ map_to_device (ply_renderer_backend_t *backend)
node = next_node;
}
+ ply_console_set_active_vt (backend->console,
+ ply_terminal_get_vt_number (backend->terminal));
+
return head_mapped;
}
@@ -922,6 +925,10 @@ flush_head (ply_renderer_backend_t *backend,
assert (backend != NULL);
+ if (ply_console_get_active_vt (backend->console) !=
+ ply_terminal_get_vt_number (backend->terminal))
+ return;
+
ply_console_set_mode (backend->console, PLY_CONSOLE_MODE_GRAPHICS);
ply_terminal_set_unbuffered_input (backend->terminal);
pixel_buffer = head->pixel_buffer;
diff --git a/src/plugins/renderers/frame-buffer/plugin.c b/src/plugins/renderers/frame-buffer/plugin.c
index 6f45b8f4..0163daa0 100644
--- a/src/plugins/renderers/frame-buffer/plugin.c
+++ b/src/plugins/renderers/frame-buffer/plugin.c
@@ -491,6 +491,9 @@ map_to_device (ply_renderer_backend_t *backend)
initialize_head (backend, head);
+ ply_console_set_active_vt (backend->console,
+ ply_terminal_get_vt_number (backend->terminal));
+
return true;
}
@@ -522,6 +525,10 @@ flush_head (ply_renderer_backend_t *backend,
assert (backend != NULL);
assert (&backend->head == head);
+ if (ply_console_get_active_vt (backend->console) !=
+ ply_terminal_get_vt_number (backend->terminal))
+ return;
+
ply_console_set_mode (backend->console, PLY_CONSOLE_MODE_GRAPHICS);
ply_terminal_set_unbuffered_input (backend->terminal);
pixel_buffer = head->pixel_buffer;
diff --git a/src/plugins/renderers/x11/Makefile.am b/src/plugins/renderers/x11/Makefile.am
new file mode 100644
index 00000000..2bf85d27
--- /dev/null
+++ b/src/plugins/renderers/x11/Makefile.am
@@ -0,0 +1,20 @@
+INCLUDES = -I$(top_srcdir) \
+ -I$(srcdir)/../../../libply \
+ -I$(srcdir)/../../../libplybootsplash \
+ -I$(srcdir)/../../.. \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/.. \
+ -I$(srcdir)
+
+plugindir = $(libdir)/plymouth/renderers
+plugin_LTLIBRARIES = x11.la
+
+x11_la_CFLAGS = $(GTK_CFLAGS) $(PLYMOUTH_CFLAGS)
+x11_la_LDFLAGS = -module -avoid-version -export-dynamic
+x11_la_LIBADD = $(PLYMOUTH_LIBS) \
+ $(GTK_LIBS) \
+ ../../../libply/libply.la \
+ ../../../libplybootsplash/libplybootsplash.la
+x11_la_SOURCES = $(srcdir)/plugin.c
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/renderers/x11/plugin.c b/src/plugins/renderers/x11/plugin.c
new file mode 100644
index 00000000..a2b61c0b
--- /dev/null
+++ b/src/plugins/renderers/x11/plugin.c
@@ -0,0 +1,523 @@
+/* plugin.c - frame-backend renderer plugin
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * 2009 Charlie Brej <cbrej@cs.man.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by: Charlie Brej <cbrej@cs.man.ac.uk>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Peter Jones <pjones@redhat.com>
+ * Ray Strode <rstrode@redhat.com>
+ */
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include <values.h>
+#include <unistd.h>
+
+#include <linux/fb.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+#include "ply-list.h"
+#include "ply-logger.h"
+#include "ply-rectangle.h"
+#include "ply-region.h"
+#include "ply-terminal.h"
+
+#include "ply-renderer.h"
+#include "ply-renderer-plugin.h"
+
+struct _ply_renderer_head
+{
+ ply_renderer_backend_t *backend;
+ ply_pixel_buffer_t *pixel_buffer;
+ ply_rectangle_t area;
+ GtkWidget *window;
+ GdkPixmap *pixmap;
+ cairo_surface_t *image;
+};
+
+struct _ply_renderer_input_source
+{
+ ply_buffer_t *key_buffer;
+ ply_renderer_input_source_handler_t handler;
+ void *user_data;
+};
+
+struct _ply_renderer_backend
+{
+ ply_event_loop_t *loop;
+ ply_renderer_input_source_t input_source;
+ ply_list_t *heads;
+ ply_console_t *console;
+
+ ply_fd_watch_t *display_watch;
+};
+
+ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
+static void ply_renderer_head_redraw (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head);
+
+static gboolean on_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer user_data);
+static gboolean on_key_event (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data);
+
+static ply_renderer_backend_t *
+create_backend (const char *device_name,
+ ply_terminal_t *terminal,
+ ply_console_t *console)
+{
+ ply_renderer_backend_t *backend;
+
+ backend = calloc (1, sizeof (ply_renderer_backend_t));
+
+ backend->loop = ply_event_loop_get_default ();
+ backend->heads = ply_list_new ();
+ backend->input_source.key_buffer = ply_buffer_new ();
+ backend->console = console;
+
+ return backend;
+}
+
+static void
+destroy_backend (ply_renderer_backend_t *backend)
+{
+ ply_list_node_t *node;
+ node = ply_list_get_first_node (backend->heads);
+ while (node != NULL)
+ {
+ ply_list_node_t *next_node;
+ ply_renderer_head_t *head;
+
+ head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (backend->heads, node);
+
+ free (head);
+ node = next_node;
+ }
+
+ ply_list_free (backend->heads);
+ ply_buffer_free (backend->input_source.key_buffer);
+ free (backend);
+}
+
+static void
+on_display_event (ply_renderer_backend_t *backend)
+{
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+static bool
+open_device (ply_renderer_backend_t *backend)
+{
+ Display *display;
+ int display_fd;
+
+ if (!gtk_init_check (0, NULL))
+ return false;
+
+ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ display_fd = ConnectionNumber (display);
+ backend->display_watch = ply_event_loop_watch_fd (backend->loop,
+ display_fd,
+ PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+ (ply_event_handler_t) on_display_event,
+ NULL,
+ backend);
+
+ return true;
+}
+
+static void
+close_device (ply_renderer_backend_t *backend)
+{
+ ply_event_loop_stop_watching_fd (backend->loop, backend->display_watch);
+ backend->display_watch = NULL;
+ return;
+}
+
+static bool
+query_device (ply_renderer_backend_t *backend)
+{
+ ply_renderer_head_t *head;
+ assert (backend != NULL);
+
+ if (ply_list_get_first_node (backend->heads) == NULL)
+ {
+ head = calloc (1, sizeof (ply_renderer_head_t));
+
+ head->backend = backend;
+ head->area.x = 0;
+ head->area.y = 0;
+ head->area.width = 800; /* FIXME hardcoded */
+ head->area.height = 600;
+ head->pixmap = gdk_pixmap_new (NULL,
+ head->area.width,
+ head->area.height,
+ 24);
+
+ ply_list_append_data (backend->heads, head);
+
+ head = calloc (1, sizeof (ply_renderer_head_t));
+
+ head->backend = backend;
+ head->area.x = 800;
+ head->area.y = 0;
+ head->area.width = 640; /* FIXME hardcoded */
+ head->area.height = 480;
+ head->pixmap = gdk_pixmap_new (NULL,
+ head->area.width,
+ head->area.height,
+ 24);
+
+ ply_list_append_data (backend->heads, head);
+ }
+
+ return true;
+}
+
+static gboolean
+on_window_destroy (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+static bool
+map_to_device (ply_renderer_backend_t *backend)
+{
+ ply_list_node_t *node;
+ assert (backend != NULL);
+
+ /* Prevent other parts of plymouth from trying to use
+ * the console, since X draws to it.
+ */
+ ply_console_ignore_mode_changes (backend->console, true);
+
+ node = ply_list_get_first_node (backend->heads);
+ while (node != NULL)
+ {
+ ply_list_node_t *next_node;
+ ply_renderer_head_t *head;
+ uint32_t *shadow_buffer;
+
+ head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (backend->heads, node);
+
+ head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
+ head->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_resizable (GTK_WINDOW (head->window), FALSE);
+ gtk_widget_set_size_request (head->window,
+ head->area.width,
+ head->area.height);
+ shadow_buffer = ply_pixel_buffer_get_argb32_data (head->pixel_buffer);
+ head->image = cairo_image_surface_create_for_data ((unsigned char *) shadow_buffer,
+ CAIRO_FORMAT_ARGB32,
+ head->area.width, head->area.height,
+ head->area.width * 4);
+ gtk_widget_set_app_paintable (head->window, TRUE);
+ gtk_widget_show_all (head->window);
+ gdk_window_set_back_pixmap (head->window->window, head->pixmap, FALSE);
+ gdk_window_set_decorations (head->window->window, GDK_DECOR_BORDER);
+ gtk_window_move (GTK_WINDOW (head->window), head->area.x, head->area.y);
+
+ gtk_widget_add_events (head->window, GDK_BUTTON1_MOTION_MASK);
+
+ g_signal_connect (head->window, "motion-notify-event",
+ G_CALLBACK (on_motion_notify_event),
+ head);
+ g_signal_connect (head->window, "key-press-event",
+ G_CALLBACK (on_key_event),
+ &backend->input_source);
+ g_signal_connect (head->window, "delete-event",
+ G_CALLBACK (on_window_destroy),
+ NULL);
+
+ ply_renderer_head_redraw (backend, head);
+ node = next_node;
+ }
+ return true;
+}
+
+static void
+unmap_from_device (ply_renderer_backend_t *backend)
+{
+ ply_list_node_t *node;
+ assert (backend != NULL);
+
+ node = ply_list_get_first_node (backend->heads);
+ while (node != NULL)
+ {
+ ply_list_node_t *next_node;
+ ply_renderer_head_t *head;
+
+ head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (backend->heads, node);
+
+ gtk_widget_destroy (head->window);
+ head->window = NULL;
+ ply_pixel_buffer_free (head->pixel_buffer);
+ head->pixel_buffer = NULL;
+ cairo_surface_destroy (head->image);
+ head->image = NULL;
+
+ node = next_node;
+ }
+
+ ply_console_ignore_mode_changes (backend->console, false);
+}
+
+static void
+flush_area_to_device (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head,
+ ply_rectangle_t *area_to_flush,
+ cairo_t *cr)
+{
+ cairo_save (cr);
+ cairo_rectangle (cr,
+ area_to_flush->x,
+ area_to_flush->y,
+ area_to_flush->width,
+ area_to_flush->height);
+ cairo_clip (cr);
+
+ cairo_set_source_surface (cr, head->image, 0, 0);
+ cairo_paint (cr);
+ cairo_restore (cr);
+}
+
+static void
+flush_head (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head)
+{
+ ply_region_t *updated_region;
+ ply_list_t *areas_to_flush;
+ ply_list_node_t *node;
+ ply_pixel_buffer_t *pixel_buffer;
+ cairo_t *cr;
+
+ assert (backend != NULL);
+
+ pixel_buffer = head->pixel_buffer;
+ updated_region = ply_pixel_buffer_get_updated_areas (pixel_buffer);
+ areas_to_flush = ply_region_get_rectangle_list (updated_region);
+
+ cr = gdk_cairo_create (head->pixmap);
+
+ node = ply_list_get_first_node (areas_to_flush);
+ while (node != NULL)
+ {
+ ply_list_node_t *next_node;
+ ply_rectangle_t *area_to_flush;
+
+ area_to_flush = (ply_rectangle_t *) ply_list_node_get_data (node);
+
+ next_node = ply_list_get_next_node (areas_to_flush, node);
+
+ flush_area_to_device (backend, head, area_to_flush, cr);
+ gdk_window_clear_area (head->window->window,
+ area_to_flush->x,
+ area_to_flush->y,
+ area_to_flush->width,
+ area_to_flush->height);
+ node = next_node;
+ }
+ ply_region_clear (updated_region);
+
+ cairo_destroy (cr);
+
+ /* Force read-back to make sure plymouth isn't saturating the
+ * X server with requests
+ */
+ g_object_unref (gdk_drawable_get_image (GDK_DRAWABLE (head->pixmap),
+ 0, 0, 1, 1));
+}
+
+static void
+ply_renderer_head_redraw (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head)
+{
+ ply_region_t *region;
+
+ region = ply_pixel_buffer_get_updated_areas (head->pixel_buffer);
+
+ ply_region_add_rectangle (region, &head->area);
+
+ flush_head (backend, head);
+}
+
+static ply_list_t *
+get_heads (ply_renderer_backend_t *backend)
+{
+ return backend->heads;
+}
+
+static ply_pixel_buffer_t *
+get_buffer_for_head (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head)
+{
+ if (head->backend != backend)
+ return NULL;
+
+ return head->pixel_buffer;
+}
+
+static bool
+has_input_source (ply_renderer_backend_t *backend,
+ ply_renderer_input_source_t *input_source)
+{
+ return input_source == &backend->input_source;
+}
+
+static ply_renderer_input_source_t *
+get_input_source (ply_renderer_backend_t *backend)
+{
+ return &backend->input_source;
+}
+
+static gboolean
+on_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer user_data)
+{
+ ply_renderer_head_t *head = user_data;
+
+ gtk_window_begin_move_drag (GTK_WINDOW (head->window), 1,
+ event->x_root, event->y_root, event->time);
+ return FALSE;
+}
+
+static gboolean
+on_key_event (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ ply_renderer_input_source_t *input_source = user_data;
+
+ if (event->keyval == GDK_Return) /* Enter */
+ {
+ ply_buffer_append_bytes (input_source->key_buffer, "\r", 1);
+ }
+ else if (event->keyval == GDK_Escape) /* Esc */
+ {
+ ply_buffer_append_bytes (input_source->key_buffer, "\033", 1);
+ }
+ else if (event->keyval == GDK_BackSpace) /* Backspace */
+ {
+ ply_buffer_append_bytes (input_source->key_buffer, "\177", 1);
+ }
+ else
+ {
+ gchar bytes[7];
+ int byte_count;
+ guint32 unichar;
+ unichar = gdk_keyval_to_unicode (event->keyval);
+ byte_count = g_unichar_to_utf8 (unichar, bytes);
+ if (bytes[0] != 0)
+ ply_buffer_append_bytes (input_source->key_buffer, bytes, byte_count);
+ else
+ ply_trace ("unknown GDK key: 0x%X \"%s\"",
+ event->keyval,
+ gdk_keyval_name (event->keyval));
+ }
+
+ if (input_source->handler != NULL)
+ input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
+ return FALSE;
+}
+
+static bool
+open_input_source (ply_renderer_backend_t *backend,
+ ply_renderer_input_source_t *input_source)
+{
+
+ assert (backend != NULL);
+ assert (has_input_source (backend, input_source));
+
+ return true;
+}
+
+static void
+set_handler_for_input_source (ply_renderer_backend_t *backend,
+ ply_renderer_input_source_t *input_source,
+ ply_renderer_input_source_handler_t handler,
+ void *user_data)
+{
+ assert (backend != NULL);
+ assert (has_input_source (backend, input_source));
+
+ input_source->handler = handler;
+ input_source->user_data = user_data;
+}
+
+static void
+close_input_source (ply_renderer_backend_t *backend,
+ ply_renderer_input_source_t *input_source)
+{
+ assert (backend != NULL);
+ assert (has_input_source (backend, input_source));
+}
+
+ply_renderer_plugin_interface_t *
+ply_renderer_backend_get_interface (void)
+{
+ static ply_renderer_plugin_interface_t plugin_interface =
+ {
+ .create_backend = create_backend,
+ .destroy_backend = destroy_backend,
+ .open_device = open_device,
+ .close_device = close_device,
+ .query_device = query_device,
+ .map_to_device = map_to_device,
+ .unmap_from_device = unmap_from_device,
+ .flush_head = flush_head,
+ .get_heads = get_heads,
+ .get_buffer_for_head = get_buffer_for_head,
+ .get_input_source = get_input_source,
+ .open_input_source = open_input_source,
+ .set_handler_for_input_source = set_handler_for_input_source,
+ .close_input_source = close_input_source
+ };
+ return &plugin_interface;
+}
+
+/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */