[renderer] Add start of X11 plugin

This renderer is useful for testing plymouth splash
plugins without leaving X. It simulates a multi-head
display by creating two X windows each representing
one monitor.
calculate-0.9.5
Charlie Brej 15 years ago committed by Ray Strode
parent a8676e2c08
commit 654c14e680

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

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

@ -1,2 +1,2 @@
SUBDIRS = frame-buffer drm
SUBDIRS = frame-buffer drm x11
MAINTAINERCLEANFILES = Makefile.in

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

@ -0,0 +1,485 @@
/* 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 "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;
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_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_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 ();
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 bool
open_device (ply_renderer_backend_t *backend)
{
return gtk_init_check (0, NULL);
}
static void
close_device (ply_renderer_backend_t *backend)
{
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;
ply_list_append_data (backend->heads, head);
head = calloc (1, sizeof (ply_renderer_head_t));
head->backend = backend;
head->area.x = 0;
head->area.y = 0;
head->area.width = 640; /* FIXME hardcoded */
head->area.height = 480;
ply_list_append_data (backend->heads, head);
}
return true;
}
static void
screen_refresh (GtkWidget *widget,
cairo_surface_t *image,
int x,
int y,
int width,
int height)
{
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window (widget));
cairo_rectangle (cr, x, y, width, height);
cairo_clip (cr);
cairo_set_source_surface (cr, image, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
return;
}
static gboolean
on_expose_event (GtkWidget *widget,
GdkEventExpose *event,
gpointer user_data)
{
cairo_surface_t *image = user_data;
screen_refresh (widget,
image,
event->area.x,
event->area.y,
event->area.width,
event->area.height);
return FALSE;
}
static gboolean
on_window_destroy (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
return TRUE;
}
static void
on_timeout (ply_renderer_backend_t *backend)
{
while (gtk_events_pending ())
gtk_main_iteration ();
ply_event_loop_watch_for_timeout (backend->loop,
0.01,
(ply_event_loop_timeout_handler_t)
on_timeout, backend);
}
static bool
map_to_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;
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_default_size (GTK_WINDOW (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);
g_signal_connect (head->window, "expose-event",
G_CALLBACK (on_expose_event),
head->image);
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;
}
on_timeout (backend);
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_event_loop_stop_watching_for_timeout (backend->loop,
(ply_event_loop_timeout_handler_t)
on_timeout, backend);
}
static void
flush_area_to_device (ply_renderer_backend_t *backend,
ply_renderer_head_t *head,
ply_rectangle_t *area_to_flush)
{
screen_refresh (head->window,
head->image,
area_to_flush->x,
area_to_flush->y,
area_to_flush->width,
area_to_flush->height);
}
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;
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);
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);
node = next_node;
}
ply_region_clear (updated_region);
}
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_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 */
Loading…
Cancel
Save