[xorg-x11-drv-intel/f21] Add UXA MST support as a fallback
Dave Airlie
airlied at fedoraproject.org
Wed Sep 3 03:18:13 UTC 2014
commit 3edcf1ede15f1061b7bac79b38a1e599a1fc0f19
Author: Dave Airlie <airlied at redhat.com>
Date: Tue Aug 26 11:10:44 2014 +1000
Add UXA MST support as a fallback
uxa-mst.patch | 588 +++++++++++++++++++++++++++++++++++++++++++++++
xorg-x11-drv-intel.spec | 8 +-
2 files changed, 595 insertions(+), 1 deletions(-)
---
diff --git a/uxa-mst.patch b/uxa-mst.patch
new file mode 100644
index 0000000..0ad03ff
--- /dev/null
+++ b/uxa-mst.patch
@@ -0,0 +1,588 @@
+From b7e1958699aad03f03447d03cebf14356e298b7f Mon Sep 17 00:00:00 2001
+From: Dave Airlie <airlied at redhat.com>
+Date: Tue, 26 Aug 2014 11:08:24 +1000
+Subject: [PATCH] f21: uxa mst bits
+
+---
+ src/uxa/intel.h | 1 +
+ src/uxa/intel_display.c | 368 ++++++++++++++++++++++++++++++++++++++----------
+ src/uxa/intel_driver.c | 4 +-
+ 3 files changed, 299 insertions(+), 74 deletions(-)
+
+diff --git a/src/uxa/intel.h b/src/uxa/intel.h
+index 409635d..ea4388b 100644
+--- a/src/uxa/intel.h
++++ b/src/uxa/intel.h
+@@ -407,6 +407,7 @@ extern void intel_mode_disable_unused_functions(ScrnInfoPtr scrn);
+ extern void intel_mode_remove_fb(intel_screen_private *intel);
+ extern void intel_mode_close(intel_screen_private *intel);
+ extern void intel_mode_fini(intel_screen_private *intel);
++extern void intel_mode_hotplug(intel_screen_private *intel);
+ extern int intel_mode_read_drm_events(intel_screen_private *intel);
+
+ typedef void (*intel_drm_handler_proc)(ScrnInfoPtr scrn,
+diff --git a/src/uxa/intel_display.c b/src/uxa/intel_display.c
+index 0b83140..4dbda44 100644
+--- a/src/uxa/intel_display.c
++++ b/src/uxa/intel_display.c
+@@ -80,7 +80,6 @@ static struct list intel_drm_queue;
+ struct intel_mode {
+ int fd;
+ uint32_t fb_id;
+- drmModeResPtr mode_res;
+ int cpp;
+
+ drmEventContext event_context;
+@@ -91,6 +90,7 @@ struct intel_mode {
+
+ struct list outputs;
+ struct list crtcs;
++ Bool delete_dp_12_displays;
+
+ void *pageflip_data;
+ intel_pageflip_handler_proc pageflip_handler;
+@@ -131,7 +131,7 @@ struct intel_output {
+ struct intel_mode *mode;
+ int output_id;
+ drmModeConnectorPtr mode_output;
+- drmModeEncoderPtr mode_encoder;
++ drmModeEncoderPtr *mode_encoders;
+ drmModePropertyBlobPtr edid_blob;
+ int num_props;
+ struct intel_property *props;
+@@ -146,6 +146,8 @@ struct intel_output {
+ int backlight_active_level;
+ xf86OutputPtr output;
+ struct list link;
++ int enc_mask;
++ int enc_clone_mask;
+ };
+
+ static void
+@@ -332,6 +334,8 @@ intel_crtc_apply(xf86CrtcPtr crtc)
+ continue;
+
+ intel_output = output->driver_private;
++ if (!intel_output->mode_output)
++ return FALSE;
+ output_ids[output_count] =
+ intel_output->mode_output->connector_id;
+ output_count++;
+@@ -706,7 +710,7 @@ static const xf86CrtcFuncsRec intel_crtc_funcs = {
+ };
+
+ static void
+-intel_crtc_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num)
++intel_crtc_init(ScrnInfoPtr scrn, struct intel_mode *mode, drmModeResPtr mode_res, int num)
+ {
+ intel_screen_private *intel = intel_get_screen_private(scrn);
+ xf86CrtcPtr crtc;
+@@ -723,7 +727,7 @@ intel_crtc_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num)
+ }
+
+ intel_crtc->mode_crtc = drmModeGetCrtc(mode->fd,
+- mode->mode_res->crtcs[num]);
++ mode_res->crtcs[num]);
+ if (intel_crtc->mode_crtc == NULL) {
+ free(intel_crtc);
+ return;
+@@ -809,6 +813,10 @@ intel_output_attach_edid(xf86OutputPtr output)
+ xf86MonPtr mon = NULL;
+ int i;
+
++ if (!koutput) {
++ xf86OutputSetEDID(output, mon);
++ return;
++ }
+ /* look for an EDID property */
+ for (i = 0; i < koutput->count_props; i++) {
+ drmModePropertyPtr props;
+@@ -898,6 +906,9 @@ intel_output_get_modes(xf86OutputPtr output)
+
+ intel_output_attach_edid(output);
+
++ if (!koutput)
++ return Modes;
++
+ /* modes should already be available */
+ for (i = 0; i < koutput->count_modes; i++) {
+ DisplayModePtr Mode;
+@@ -950,7 +961,10 @@ intel_output_destroy(xf86OutputPtr output)
+ free(intel_output->props[i].atoms);
+ }
+ free(intel_output->props);
+-
++ for (i = 0; i < intel_output->mode_output->count_encoders; i++) {
++ drmModeFreeEncoder(intel_output->mode_encoders[i]);
++ }
++ free(intel_output->mode_encoders);
+ drmModeFreeConnector(intel_output->mode_output);
+ intel_output->mode_output = NULL;
+
+@@ -990,6 +1004,9 @@ intel_output_dpms(xf86OutputPtr output, int dpms)
+ struct intel_mode *mode = intel_output->mode;
+ int i;
+
++ if (!koutput)
++ return;
++
+ for (i = 0; i < koutput->count_props; i++) {
+ drmModePropertyPtr props;
+
+@@ -1338,52 +1355,158 @@ static const char *output_names[] = {
+ "TV",
+ "eDP",
+ };
++static xf86OutputPtr find_output(ScrnInfoPtr pScrn, int id)
++{
++ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
++ int i;
++ for (i = 0; i < xf86_config->num_output; i++) {
++ xf86OutputPtr output = xf86_config->output[i];
++ struct intel_output *intel_output;
++
++ intel_output = output->driver_private;
++ if (intel_output->output_id == id)
++ return output;
++ }
++ return NULL;
++}
++
++static int parse_path_blob(drmModePropertyBlobPtr path_blob, int *conn_base_id, char **path)
++{
++ char *conn;
++ char conn_id[5];
++ int id, len;
++ char *blob_data;
++
++ if (!path_blob)
++ return -1;
++
++ blob_data = path_blob->data;
++ /* we only handle MST paths for now */
++ if (strncmp(blob_data, "mst:", 4))
++ return -1;
++
++ conn = strchr(blob_data + 4, '-');
++ if (!conn)
++ return -1;
++ len = conn - (blob_data + 4);
++ if (len + 1 > 5)
++ return -1;
++ memcpy(conn_id, blob_data + 4, len);
++ conn_id[len] = '\0';
++ id = strtoul(conn_id, NULL, 10);
++
++ *conn_base_id = id;
++
++ *path = conn + 1;
++ return 0;
++}
+
+ static void
+-intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num)
++drmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
++ drmModePropertyBlobPtr path_blob)
+ {
++ int ret;
++ char *extra_path;
++ int conn_id;
++ xf86OutputPtr output;
++
++ ret = parse_path_blob(path_blob, &conn_id, &extra_path);
++ if (ret == -1)
++ goto fallback;
++
++ output = find_output(pScrn, conn_id);
++ if (!output)
++ goto fallback;
++
++ snprintf(name, 32, "%s-%s", output->name, extra_path);
++ ErrorF("setting name to %s\n", name);
++ return;
++
++fallback:
++ if (koutput->connector_type >= ARRAY_SIZE(output_names))
++ snprintf(name, 32, "Unknown-%d", koutput->connector_type_id - 1);
++#ifdef MODESETTING_OUTPUT_SLAVE_SUPPORT
++ else if (pScrn->is_gpu)
++ snprintf(name, 32, "%s-%d-%d", output_names[koutput->connector_type], pScrn->scrnIndex - GPU_SCREEN_OFFSET + 1, koutput->connector_type_id - 1);
++#endif
++ else
++ snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id - 1);
++}
++
++static void
++intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, drmModeResPtr mode_res, int num, int dynamic)
++{
++ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+ xf86OutputPtr output;
+ drmModeConnectorPtr koutput;
+- drmModeEncoderPtr kencoder;
++ drmModeEncoderPtr *kencoders = NULL;
+ struct intel_output *intel_output;
+- const char *output_name;
+ char name[32];
++ drmModePropertyPtr props;
++ drmModePropertyBlobPtr path_blob = NULL;
++ int i;
+
+ koutput = drmModeGetConnector(mode->fd,
+- mode->mode_res->connectors[num]);
++ mode_res->connectors[num]);
+ if (!koutput)
+ return;
++ for (i = 0; i < koutput->count_props; i++) {
++ props = drmModeGetProperty(mode->fd, koutput->props[i]);
++ if (props && (props->flags & DRM_MODE_PROP_BLOB)) {
++ if (!strcmp(props->name, "PATH")) {
++ path_blob = drmModeGetPropertyBlob(mode->fd, koutput->prop_values[i]);
+
+- kencoder = drmModeGetEncoder(mode->fd, koutput->encoders[0]);
+- if (!kencoder) {
+- drmModeFreeConnector(koutput);
+- return;
++ drmModeFreeProperty(props);
++ break;
++ }
++ drmModeFreeProperty(props);
++ }
+ }
+
+- if (koutput->connector_type < ARRAY_SIZE(output_names))
+- output_name = output_names[koutput->connector_type];
+- else
+- output_name = "UNKNOWN";
+- snprintf(name, 32, "%s%d", output_name, koutput->connector_type_id);
++ drmmode_create_name(scrn, koutput, name, path_blob);
++ if (path_blob)
++ drmModeFreePropertyBlob(path_blob);
++
++ if (path_blob && dynamic) {
++ /* see if we have an output with this name already
++ and hook stuff up */
++ for (i = 0; i < xf86_config->num_output; i++) {
++ output = xf86_config->output[i];
++
++ if (strncmp(output->name, name, 32))
++ continue;
++
++ intel_output = output->driver_private;
++ intel_output->output_id = mode_res->connectors[num];
++ intel_output->mode_output = koutput;
++ return;
++ }
++ }
++ kencoders = calloc(sizeof(drmModeEncoderPtr), koutput->count_encoders);
++ if (!kencoders) {
++ goto out_free_encoders;
++ }
++
++ for (i = 0; i < koutput->count_encoders; i++) {
++ kencoders[i] = drmModeGetEncoder(mode->fd, koutput->encoders[i]);
++ if (!kencoders[i])
++ goto out_free_encoders;
++ }
+
+ output = xf86OutputCreate (scrn, &intel_output_funcs, name);
+ if (!output) {
+- drmModeFreeEncoder(kencoder);
+- drmModeFreeConnector(koutput);
+- return;
++ goto out_free_encoders;
+ }
+
+ intel_output = calloc(sizeof(struct intel_output), 1);
+ if (!intel_output) {
+ xf86OutputDestroy(output);
+- drmModeFreeConnector(koutput);
+- drmModeFreeEncoder(kencoder);
+- return;
++ goto out_free_encoders;
+ }
+
+- intel_output->output_id = mode->mode_res->connectors[num];
++ intel_output->output_id = mode_res->connectors[num];
+ intel_output->mode_output = koutput;
+- intel_output->mode_encoder = kencoder;
++ intel_output->mode_encoders = kencoders;
+ intel_output->mode = mode;
+
+ output->mm_width = koutput->mmWidth;
+@@ -1395,11 +1518,22 @@ intel_output_init(ScrnInfoPtr scrn, struct intel_mode *mode, int num)
+ if (is_panel(koutput->connector_type))
+ intel_output_backlight_init(output);
+
+- output->possible_crtcs = kencoder->possible_crtcs;
++ output->possible_crtcs = 0x7f;
++ for (i = 0; i < koutput->count_encoders; i++) {
++ output->possible_crtcs &= kencoders[i]->possible_crtcs;
++ }
+ output->interlaceAllowed = TRUE;
+
+ intel_output->output = output;
+ list_add(&intel_output->link, &mode->outputs);
++ return;
++out_free_encoders:
++ if (kencoders) {
++ for (i = 0; i < koutput->count_encoders; i++)
++ drmModeFreeEncoder(kencoders[i]);
++ free(kencoders);
++ }
++ drmModeFreeConnector(koutput);
+ }
+
+ static Bool
+@@ -1963,57 +2097,63 @@ intel_mode_read_drm_events(struct intel_screen_private *intel)
+ return drmHandleEvent(mode->fd, &mode->event_context);
+ }
+
+-static drmModeEncoderPtr
+-intel_get_kencoder(struct intel_mode *mode, int num)
+-{
+- struct intel_output *iterator;
+- int id = mode->mode_res->encoders[num];
+-
+- list_for_each_entry(iterator, &mode->outputs, link)
+- if (iterator->mode_encoder->encoder_id == id)
+- return iterator->mode_encoder;
+-
+- return NULL;
+-}
+-
+ /*
+ * Libdrm's possible_clones is a mask of encoders, Xorg's possible_clones is a
+ * mask of outputs. This function sets Xorg's possible_clones based on the
+ * values read from libdrm.
+ */
+-static void
+-intel_compute_possible_clones(ScrnInfoPtr scrn, struct intel_mode *mode)
++static uint32_t find_clones(ScrnInfoPtr scrn, xf86OutputPtr output)
+ {
+- xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+- struct intel_output *intel_output, *clone;
+- drmModeEncoderPtr cloned_encoder;
+- uint32_t mask;
+- int i, j, k;
+- CARD32 possible_clones;
++ struct intel_output *intel_output = output->driver_private, *clone_drmout;
++ int i;
++ xf86OutputPtr clone_output;
++ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
++ int index_mask = 0;
+
+- for (i = 0; i < config->num_output; i++) {
+- possible_clones = 0;
+- intel_output = config->output[i]->driver_private;
++ if (intel_output->enc_clone_mask == 0)
++ return index_mask;
+
+- mask = intel_output->mode_encoder->possible_clones;
+- for (j = 0; mask != 0; j++, mask >>= 1) {
++ for (i = 0; i < xf86_config->num_output; i++) {
++ clone_output = xf86_config->output[i];
++ clone_drmout = clone_output->driver_private;
++ if (output == clone_output)
++ continue;
+
+- if ((mask & 1) == 0)
+- continue;
++ if (clone_drmout->enc_mask == 0)
++ continue;
++ if (intel_output->enc_clone_mask == clone_drmout->enc_mask)
++ index_mask |= (1 << i);
++ }
++ return index_mask;
++}
++static void
++intel_compute_possible_clones(ScrnInfoPtr scrn, struct intel_mode *mode, drmModeResPtr mode_res)
++{
++ int i, j;
++ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+
+- cloned_encoder = intel_get_kencoder(mode, j);
+- if (!cloned_encoder)
+- continue;
++ for (i = 0; i < xf86_config->num_output; i++) {
++ xf86OutputPtr output = xf86_config->output[i];
++ struct intel_output *intel_output;
+
+- for (k = 0; k < config->num_output; k++) {
+- clone = config->output[k]->driver_private;
+- if (clone->mode_encoder->encoder_id ==
+- cloned_encoder->encoder_id)
+- possible_clones |= (1 << k);
++ intel_output = output->driver_private;
++ intel_output->enc_clone_mask = 0xff;
++ /* and all the possible encoder clones for this output together */
++ for (j = 0; j < intel_output->mode_output->count_encoders; j++)
++ {
++ int k;
++ for (k = 0; k < mode_res->count_encoders; k++) {
++ if (mode_res->encoders[k] == intel_output->mode_encoders[j]->encoder_id)
++ intel_output->enc_mask |= (1 << k);
+ }
++
++ intel_output->enc_clone_mask &= intel_output->mode_encoders[j]->possible_clones;
+ }
++ }
+
+- config->output[i]->possible_clones = possible_clones;
++ for (i = 0; i < xf86_config->num_output; i++) {
++ xf86OutputPtr output = xf86_config->output[i];
++ output->possible_clones = find_clones(scrn, output);
+ }
+ }
+
+@@ -2024,6 +2164,7 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
+ struct intel_mode *mode;
+ unsigned int i;
+ int has_flipping;
++ drmModeResPtr mode_res;
+
+ mode = calloc(1, sizeof *mode);
+ if (!mode)
+@@ -2037,23 +2178,23 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
+ xf86CrtcConfigInit(scrn, &intel_xf86crtc_config_funcs);
+
+ mode->cpp = cpp;
+- mode->mode_res = drmModeGetResources(mode->fd);
+- if (!mode->mode_res) {
++ mode_res = drmModeGetResources(mode->fd);
++ if (!mode_res) {
+ xf86DrvMsg(scrn->scrnIndex, X_ERROR,
+ "failed to get resources: %s\n", strerror(errno));
+ free(mode);
+ return FALSE;
+ }
+
+- xf86CrtcSetSizeRange(scrn, 320, 200, mode->mode_res->max_width,
+- mode->mode_res->max_height);
+- for (i = 0; i < mode->mode_res->count_crtcs; i++)
+- intel_crtc_init(scrn, mode, i);
++ xf86CrtcSetSizeRange(scrn, 320, 200, mode_res->max_width,
++ mode_res->max_height);
++ for (i = 0; i < mode_res->count_crtcs; i++)
++ intel_crtc_init(scrn, mode, mode_res, i);
+
+- for (i = 0; i < mode->mode_res->count_connectors; i++)
+- intel_output_init(scrn, mode, i);
++ for (i = 0; i < mode_res->count_connectors; i++)
++ intel_output_init(scrn, mode, mode_res, i, 0);
+
+- intel_compute_possible_clones(scrn, mode);
++ intel_compute_possible_clones(scrn, mode, mode_res);
+
+ #ifdef INTEL_PIXMAP_SHARING
+ xf86ProviderSetup(scrn, NULL, "Intel");
+@@ -2080,7 +2221,12 @@ Bool intel_mode_pre_init(ScrnInfoPtr scrn, int fd, int cpp)
+ intel->use_pageflipping = TRUE;
+ }
+
++ if (xf86ReturnOptValBool(intel->Options, OPTION_DELETE_DP12, FALSE)) {
++ mode->delete_dp_12_displays = TRUE;
++ }
++
+ intel->modes = mode;
++ drmModeFreeResources(mode_res);
+ return TRUE;
+ }
+
+@@ -2331,3 +2477,79 @@ cleanup_dst:
+ cleanup_src:
+ (*pScreen->DestroyPixmap)(src);
+ }
++
++void
++intel_mode_hotplug(struct intel_screen_private *intel)
++{
++ ScrnInfoPtr scrn = intel->scrn;
++ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
++ drmModeResPtr mode_res;
++ int i, j;
++ Bool found;
++ Bool changed = FALSE;
++ struct intel_mode *mode = intel->modes;
++ mode_res = drmModeGetResources(intel->drmSubFD);
++ if (!mode_res)
++ goto out;
++
++restart_destroy:
++ for (i = 0; i < config->num_output; i++) {
++ xf86OutputPtr output = config->output[i];
++ struct intel_output *intel_output;
++
++ intel_output = output->driver_private;
++ found = FALSE;
++ for (j = 0; j < mode_res->count_connectors; j++) {
++ if (mode_res->connectors[j] == intel_output->output_id) {
++ found = TRUE;
++ break;
++ }
++ }
++ if (found)
++ continue;
++
++ drmModeFreeConnector(intel_output->mode_output);
++ intel_output->mode_output = NULL;
++ intel_output->output_id = -1;
++
++ changed = TRUE;
++ if (mode->delete_dp_12_displays) {
++ ErrorF("destroying id %d\n", intel_output->output_id);
++ RROutputDestroy(output->randr_output);
++ xf86OutputDestroy(output);
++ goto restart_destroy;
++ }
++ }
++
++ /* find new output ids we don't have outputs for */
++ for (i = 0; i < mode_res->count_connectors; i++) {
++ found = FALSE;
++
++ for (j = 0; j < config->num_output; j++) {
++ xf86OutputPtr output = config->output[j];
++ struct intel_output *intel_output;
++
++ intel_output = output->driver_private;
++ if (mode_res->connectors[i] == intel_output->output_id) {
++ found = TRUE;
++ break;
++ }
++ }
++ if (found)
++ continue;
++
++ changed = TRUE;
++ ErrorF("adding id %d\n", mode_res->connectors[i]);
++ intel_output_init(scrn, intel->modes, mode_res, i, 1);
++
++ }
++
++ if (changed) {
++ RRSetChanged(xf86ScrnToScreen(scrn));
++ RRTellChanged(xf86ScrnToScreen(scrn));
++ }
++
++ drmModeFreeResources(mode_res);
++out:
++ RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
++}
+diff --git a/src/uxa/intel_driver.c b/src/uxa/intel_driver.c
+index a7ca906..7877eb5 100644
+--- a/src/uxa/intel_driver.c
++++ b/src/uxa/intel_driver.c
+@@ -779,7 +779,9 @@ I830HandleUEvents(int fd, void *closure)
+
+ if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
+ hotplug && atoi(hotplug) == 1)
+- RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
++ {
++ intel_mode_hotplug(intel);
++ }
+
+ udev_device_unref(dev);
+ }
+--
+1.9.3
+
diff --git a/xorg-x11-drv-intel.spec b/xorg-x11-drv-intel.spec
index b0b2b00..b8bee0b 100644
--- a/xorg-x11-drv-intel.spec
+++ b/xorg-x11-drv-intel.spec
@@ -26,7 +26,7 @@
Summary: Xorg X11 Intel video driver
Name: xorg-x11-drv-intel
Version: 2.99.914
-Release: 3%{?gitrev}%{?dist}
+Release: 4%{?gitrev}%{?dist}
URL: http://www.x.org
License: MIT
Group: User Interface/X Hardware Support
@@ -42,6 +42,8 @@ Source4: make-git-snapshot.sh
Patch1: 0001-sna-dri3-Mesa-relies-upon-implicit-fences.patch
Patch2: 0001-sna-trapezoids-Use-the-corrected-trapezoid-origin-fo.patch
+Patch10: uxa-mst.patch
+
ExclusiveArch: %{ix86} x86_64 ia64
BuildRequires: autoconf automake libtool
@@ -91,6 +93,7 @@ Debugging tools for Intel graphics chips
%patch1 -p1
%patch2 -p1
+%patch10 -p1 -b .mst
%build
%configure %{?kmsonly:--enable-kms-only}
make %{?_smp_mflags}
@@ -149,6 +152,9 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/libI*XvMC.so
%{_mandir}/man1/intel_*.1*
%changelog
+* Wed Sep 03 2014 Dave Airlie <airlied at redhat.com> 2.99.914-4
+- Add UXA MST support as a fallback
+
* Tue Sep 02 2014 Adel Gadllah <adel.gadllah at gmail.com> - 2.99.914-3
- Backport fix for sna to fix broken shadow rendering in gtk
More information about the scm-commits
mailing list