[retro-gtk] Initial GTK4 port
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [retro-gtk] Initial GTK4 port
- Date: Sun, 20 Jun 2021 20:02:05 +0000 (UTC)
commit 7fcd160436ca01de0e2f6503738f1e52dfc696f7
Author: Alexander Mikhaylenko <exalm7659 gmail com>
Date: Sat Oct 27 20:20:08 2018 +0500
Initial GTK4 port
Remove pointer grabbing for now, we'll need to reimplement it via raw
X11/Wayland APIs later.
demos/retro-demo.c | 6 +-
meson.build | 4 +-
retro-gtk/meson.build | 4 +-
retro-gtk/retro-core-view.c | 217 ++++++++++++++++++++++----------------------
retro-gtk/retro-core-view.h | 2 +-
retro-gtk/retro-core.c | 64 ++++++++-----
retro-gtk/retro-keyboard.c | 2 +-
7 files changed, 155 insertions(+), 144 deletions(-)
---
diff --git a/demos/retro-demo.c b/demos/retro-demo.c
index 6e25404..cfb250e 100644
--- a/demos/retro-demo.c
+++ b/demos/retro-demo.c
@@ -96,11 +96,11 @@ retro_demo_activate (GApplication *application)
retro_core_set_keyboard (self->core, GTK_WIDGET (view));
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
- gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (view));
+ gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (view));
+ gtk_window_present (GTK_WINDOW (window));
- gtk_widget_show_all (GTK_WIDGET (window));
gtk_application_add_window (GTK_APPLICATION (application),
GTK_WINDOW (window));
g_signal_connect_swapped (self->core, "shutdown", G_CALLBACK (gtk_window_close), window);
diff --git a/meson.build b/meson.build
index 35f8a39..1fe4294 100644
--- a/meson.build
+++ b/meson.build
@@ -20,7 +20,7 @@ retro_gtk_api_version = '2'
retro_gtk_module = 'retro-gtk-' + retro_gtk_api_version
glib_version = '>= 2.68'
-gtk_version = '>= 3.22'
+gtk_version = '>= 4.0'
epoxy = dependency ('epoxy')
gio = dependency ('gio-2.0', version: glib_version)
@@ -28,7 +28,7 @@ gio_unix = dependency ('gio-unix-2.0', version: glib_version)
glib = dependency ('glib-2.0', version: glib_version)
gmodule = dependency ('gmodule-2.0', version: glib_version)
gobject = dependency ('gobject-2.0', version: glib_version)
-gtk = dependency ('gtk+-3.0', version: gtk_version)
+gtk = dependency ('gtk4', version: gtk_version)
libpulse_simple = dependency ('libpulse-simple', required : get_option('pulseaudio'))
m = cc.find_library('m', required : false)
samplerate = dependency ('samplerate', required : get_option('pulseaudio'))
diff --git a/retro-gtk/meson.build b/retro-gtk/meson.build
index ad59ab3..528c17a 100644
--- a/retro-gtk/meson.build
+++ b/retro-gtk/meson.build
@@ -136,7 +136,7 @@ if get_option('introspection')
'GLib-2.0',
'GModule-2.0',
'GObject-2.0',
- 'Gtk-3.0',
+ 'Gtk-4.0',
]
retro_gtk_gir = gnome.generate_gir(
@@ -159,7 +159,7 @@ if get_option('introspection')
'glib-2.0',
'gmodule-2.0',
'gobject-2.0',
- 'gtk+-3.0',
+ 'gtk4',
]
gnome.generate_vapi(
diff --git a/retro-gtk/retro-core-view.c b/retro-gtk/retro-core-view.c
index dc4e6ae..ae7f32f 100644
--- a/retro-gtk/retro-core-view.c
+++ b/retro-gtk/retro-core-view.c
@@ -19,7 +19,7 @@
struct _RetroCoreView
{
- GtkEventBox parent_instance;
+ GtkWidget parent_instance;
RetroCore *core;
RetroGLDisplay *display;
gboolean can_grab_pointer;
@@ -28,7 +28,6 @@ struct _RetroCoreView
GHashTable *keyval_state;
RetroKeyJoypadMapping *key_joypad_mapping;
GHashTable *mouse_button_state;
- GdkScreen *grabbed_screen;
GdkDevice *grabbed_device;
gdouble mouse_x_delta;
gdouble mouse_y_delta;
@@ -41,7 +40,7 @@ struct _RetroCoreView
gdouble pointer_y;
};
-G_DEFINE_TYPE (RetroCoreView, retro_core_view, GTK_TYPE_EVENT_BOX)
+G_DEFINE_TYPE (RetroCoreView, retro_core_view, GTK_TYPE_WIDGET)
enum {
PROP_CAN_GRAB_POINTER = 1,
@@ -111,8 +110,7 @@ axis_to_retro_axis (gdouble value)
static void
recenter_pointer (RetroCoreView *self)
{
- gdk_device_warp (self->grabbed_device, self->grabbed_screen,
- self->screen_center_x, self->screen_center_y);
+ /* TODO: Reimplement this. See
https://github.com/WebKit/webkit/blob/main/Source/WebKit/UIProcess/gtk/PointerLockManagerX11.cpp */
}
static gboolean
@@ -123,36 +121,11 @@ get_is_pointer_grabbed (RetroCoreView *self)
static void
grab (RetroCoreView *self,
- GdkDevice *device,
- GdkWindow *window,
- GdkEvent *event)
-{
- GdkSeat *seat;
- GdkDisplay *display;
- g_autoptr (GdkCursor) cursor = NULL;
- GdkScreen *screen = NULL;
- GdkMonitor *monitor;
- GdkRectangle monitor_geometry;
-
- g_assert (device != NULL);
- g_assert (window != NULL);
- g_assert (event != NULL);
-
- seat = gdk_device_get_seat (device);
- display = gdk_device_get_display (device);
- cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR);
- gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, event, NULL, NULL);
- monitor = gdk_display_get_monitor_at_window (display, window);
- gdk_monitor_get_geometry (monitor, &monitor_geometry);
-
- gdk_device_get_position (device, &screen, &self->position_on_grab_x, &self->position_on_grab_y);
- self->screen_center_x = monitor_geometry.x + monitor_geometry.width / 2;
- self->screen_center_y = monitor_geometry.y + monitor_geometry.height / 2;
- self->mouse_x_delta = 0;
- self->mouse_y_delta = 0;
+ GdkDevice *device)
+{
+ /* TODO: Reimplement this. See
https://github.com/WebKit/webkit/blob/main/Source/WebKit/UIProcess/gtk/PointerLockManagerX11.cpp */
g_set_object (&self->grabbed_device, device);
- g_set_object (&self->grabbed_screen, screen);
recenter_pointer (self);
@@ -162,17 +135,9 @@ grab (RetroCoreView *self,
static void
ungrab (RetroCoreView *self)
{
- GdkSeat *seat;
-
- g_assert (self->grabbed_device != NULL);
-
- seat = gdk_device_get_seat (self->grabbed_device);
- gdk_seat_ungrab (seat);
- gdk_device_warp (self->grabbed_device, self->grabbed_screen,
- self->position_on_grab_x, self->position_on_grab_y);
+ /* TODO: Reimplement this. See
https://github.com/WebKit/webkit/blob/main/Source/WebKit/UIProcess/gtk/PointerLockManagerX11.cpp */
g_clear_object (&self->grabbed_device);
- g_clear_object (&self->grabbed_screen);
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
}
@@ -181,21 +146,22 @@ static gboolean get_key_state (RetroCoreView *self,
guint16 hardware_keycode);
static gboolean
-key_press_event_cb (GtkWidget *source,
- GdkEventKey *event,
- RetroCoreView *self)
+key_pressed_cb (RetroCoreView *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state)
{
gboolean changed;
- if (event->keyval == GDK_KEY_Escape &&
- (event->state & GDK_CONTROL_MASK) &&
+ if (keyval == GDK_KEY_Escape &&
+ (state & GDK_CONTROL_MASK) &&
get_is_pointer_grabbed (self))
ungrab (self);
- changed = !get_key_state (self, event->hardware_keycode);
+ changed = !get_key_state (self, keycode);
- set_input_pressed (self->key_state, event->hardware_keycode);
- set_input_pressed (self->keyval_state, event->keyval);
+ set_input_pressed (self->key_state, keycode);
+ set_input_pressed (self->keyval_state, keyval);
if (changed)
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
@@ -203,63 +169,72 @@ key_press_event_cb (GtkWidget *source,
return FALSE;
}
-static gboolean
-key_release_event_cb (GtkWidget *source,
- GdkEventKey *event,
- RetroCoreView *self)
+static void
+key_released_cb (RetroCoreView *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state)
{
- set_input_released (self->key_state, event->hardware_keycode);
- set_input_released (self->keyval_state, event->keyval);
+ set_input_released (self->key_state, keycode);
+ set_input_released (self->keyval_state, keyval);
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
-
- return FALSE;
}
-static gboolean
-button_press_event_cb (GtkWidget *source,
- GdkEventButton *event,
- RetroCoreView *self)
+static void
+pressed_cb (RetroCoreView *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkGesture *gesture)
{
- gtk_widget_grab_focus (GTK_WIDGET (source));
+ guint button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
+
+ gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
+
+ gtk_widget_grab_focus (GTK_WIDGET (self));
if (retro_core_view_get_can_grab_pointer (self)) {
if (get_is_pointer_grabbed (self))
- set_input_pressed (self->mouse_button_state, event->button);
- else
- grab (self, event->device, event->window, (GdkEvent *) event);
+ set_input_pressed (self->mouse_button_state, button);
+ else {
+ GdkDevice *device = gtk_event_controller_get_current_event_device (GTK_EVENT_CONTROLLER (gesture));
+
+ grab (self, device);
+ }
}
else {
- set_input_pressed (self->mouse_button_state, event->button);
+ set_input_pressed (self->mouse_button_state, button);
self->pointer_is_on_display =
retro_gl_display_get_coordinates_on_display (self->display,
- event->x,
- event->y,
+ x,
+ y,
&self->pointer_x,
&self->pointer_y);
}
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
-
- return FALSE;
}
-static gboolean
-button_release_event_cb (GtkWidget *source,
- GdkEventButton *event,
- RetroCoreView *self)
+static void
+released_cb (RetroCoreView *self,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GtkGesture *gesture)
+
{
- set_input_released (self->mouse_button_state, event->button);
+ guint button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
- g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
+ gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
- return FALSE;
+ set_input_released (self->mouse_button_state, button);
+
+ g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
}
static gboolean
-focus_out_event_cb (GtkWidget *source,
- GdkEventFocus *event,
- RetroCoreView *self)
+leave_cb (RetroCoreView *self)
{
if (get_is_pointer_grabbed (self))
ungrab (self);
@@ -269,28 +244,30 @@ focus_out_event_cb (GtkWidget *source,
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
- return FALSE;
+ return GDK_EVENT_PROPAGATE;
}
static gboolean
-motion_notify_event_cb (GtkWidget *source,
- GdkEventMotion *event,
- RetroCoreView *self)
+motion_cb (RetroCoreView *self,
+ gdouble x,
+ gdouble y,
+ GtkEventController *controller)
{
if (retro_core_view_get_can_grab_pointer (self)) {
+ GdkDevice *device = gtk_event_controller_get_current_event_device (controller);
+
if (get_is_pointer_grabbed (self) &&
- event->device == self->grabbed_device) {
- self->mouse_x_delta += event->x_root - (double) self->screen_center_x;
- self->mouse_y_delta += event->y_root - (double) self->screen_center_y;
+ device == self->grabbed_device) {
+ self->mouse_x_delta += x - (double) self->screen_center_x;
+ self->mouse_y_delta += y - (double) self->screen_center_y;
recenter_pointer (self);
}
- }
- else {
+ } else {
self->pointer_is_on_display =
retro_gl_display_get_coordinates_on_display (self->display,
- event->x,
- event->y,
+ x,
+ y,
&self->pointer_x,
&self->pointer_y);
@@ -298,7 +275,7 @@ motion_notify_event_cb (GtkWidget *source,
g_signal_emit (self, signals[SIGNAL_CONTROLLER_STATE_CHANGED], 0);
- return FALSE;
+ return GDK_EVENT_PROPAGATE;
}
static gboolean
@@ -343,19 +320,28 @@ get_keyboard_key_state (RetroCoreView *self,
return get_input_state (self->keyval_state, keyval);
}
+static void
+retro_core_view_dispose (GObject *object)
+{
+ RetroCoreView *self = RETRO_CORE_VIEW (object);
+
+ g_clear_pointer ((GtkWidget **) &self->display, gtk_widget_unparent);
+
+ g_clear_object (&self->grabbed_device);
+
+ G_OBJECT_CLASS (retro_core_view_parent_class)->dispose (object);
+}
+
static void
retro_core_view_finalize (GObject *object)
{
RetroCoreView *self = RETRO_CORE_VIEW (object);
g_clear_object (&self->core);
- g_object_unref (self->display);
g_hash_table_unref (self->key_state);
g_hash_table_unref (self->keyval_state);
g_object_unref (self->key_joypad_mapping);
g_hash_table_unref (self->mouse_button_state);
- g_clear_object (&self->grabbed_screen);
- g_clear_object (&self->grabbed_device);
G_OBJECT_CLASS (retro_core_view_parent_class)->finalize (object);
}
@@ -414,6 +400,7 @@ retro_core_view_class_init (RetroCoreViewClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ object_class->dispose = retro_core_view_dispose;
object_class->finalize = retro_core_view_finalize;
object_class->get_property = retro_core_view_get_property;
object_class->set_property = retro_core_view_set_property;
@@ -463,24 +450,21 @@ retro_core_view_class_init (RetroCoreViewClass *klass)
G_TYPE_NONE,
0);
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+
gtk_widget_class_set_css_name (widget_class, "retrocoreview");
}
static void
retro_core_view_init (RetroCoreView *self)
{
- g_object_set ((GtkWidget*) self, "can-default", TRUE, NULL);
- g_object_set ((GtkWidget*) self, "can-focus", TRUE, NULL);
+ GtkEventController *controller;
- gtk_widget_add_events (GTK_WIDGET (self),
- GDK_KEY_PRESS_MASK |
- GDK_KEY_RELEASE_MASK |
- GDK_TOUCH_MASK);
+ gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
self->display = g_object_ref_sink (retro_gl_display_new ());
- gtk_widget_set_visible (GTK_WIDGET (self->display), TRUE);
- g_object_set (GTK_WIDGET (self->display), "can-focus", FALSE, NULL);
- gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->display));
+ gtk_widget_set_can_focus (GTK_WIDGET (self->display), FALSE);
+ gtk_widget_set_parent (GTK_WIDGET (self->display), GTK_WIDGET (self));
g_object_bind_property (G_OBJECT (self), "sensitive",
G_OBJECT (self->display), "sensitive",
@@ -492,12 +476,23 @@ retro_core_view_init (RetroCoreView *self)
self->key_joypad_mapping = retro_key_joypad_mapping_new_default ();
self->mouse_button_state = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
- g_signal_connect_object (self, "key-press-event", (GCallback) key_press_event_cb, self, 0);
- g_signal_connect_object (self, "key-release-event", (GCallback) key_release_event_cb, self, 0);
- g_signal_connect_object (self, "button-press-event", (GCallback) button_press_event_cb, self, 0);
- g_signal_connect_object (self, "button-release-event", (GCallback) button_release_event_cb, self, 0);
- g_signal_connect_object (self, "focus-out-event", (GCallback) focus_out_event_cb, self, 0);
- g_signal_connect_object (self, "motion-notify-event", (GCallback) motion_notify_event_cb, self, 0);
+ controller = gtk_event_controller_key_new ();
+ g_signal_connect_swapped (controller, "key-pressed", G_CALLBACK (key_pressed_cb), self);
+ g_signal_connect_swapped (controller, "key-released", G_CALLBACK (key_released_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ controller = gtk_event_controller_focus_new ();
+ g_signal_connect_swapped (controller, "leave", G_CALLBACK (leave_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ controller = gtk_event_controller_motion_new ();
+ g_signal_connect_swapped (controller, "motion", G_CALLBACK (motion_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+ g_signal_connect_swapped (controller, "pressed", G_CALLBACK (pressed_cb), self);
+ g_signal_connect_swapped (controller, "released", G_CALLBACK (released_cb), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
/* Public */
diff --git a/retro-gtk/retro-core-view.h b/retro-gtk/retro-core-view.h
index 4527b6e..8bdc8f0 100644
--- a/retro-gtk/retro-core-view.h
+++ b/retro-gtk/retro-core-view.h
@@ -17,7 +17,7 @@ G_BEGIN_DECLS
#define RETRO_TYPE_CORE_VIEW (retro_core_view_get_type())
-G_DECLARE_FINAL_TYPE (RetroCoreView, retro_core_view, RETRO, CORE_VIEW, GtkEventBox)
+G_DECLARE_FINAL_TYPE (RetroCoreView, retro_core_view, RETRO, CORE_VIEW, GtkWidget)
RetroCoreView *retro_core_view_new (void) G_GNUC_WARN_UNUSED_RESULT;
diff --git a/retro-gtk/retro-core.c b/retro-gtk/retro-core.c
index ab3a048..c85b345 100644
--- a/retro-gtk/retro-core.c
+++ b/retro-gtk/retro-core.c
@@ -87,8 +87,7 @@ struct _RetroCore
gdouble speed_rate;
GtkWidget *keyboard_widget;
- gulong key_press_event_id;
- gulong key_release_event_id;
+ GtkEventController *key_controller;
RetroFramebuffer *framebuffer;
};
@@ -664,11 +663,11 @@ exit_cb (RetroRunnerProcess *process,
}
static gboolean
-key_event_cb (GtkWidget *widget,
- GdkEventKey *event,
- RetroCore *self)
+key_event (RetroCore *self,
+ guint keyval,
+ GdkModifierType state,
+ gboolean pressed)
{
- gboolean pressed;
RetroKeyboardKey retro_key;
RetroKeyboardModifierKey retro_modifier_key;
guint32 character;
@@ -678,10 +677,9 @@ key_event_cb (GtkWidget *widget,
if (!retro_core_get_is_initiated (self))
return FALSE;
- pressed = event->type == GDK_KEY_PRESS;
- retro_key = retro_keyboard_key_converter (event->keyval);
- retro_modifier_key = retro_keyboard_modifier_key_converter (event->keyval, event->state);
- character = gdk_keyval_to_unicode (event->keyval);
+ retro_key = retro_keyboard_key_converter (keyval);
+ retro_modifier_key = retro_keyboard_modifier_key_converter (keyval, state);
+ character = gdk_keyval_to_unicode (keyval);
proxy = retro_runner_process_get_proxy (self->process);
if (!ipc_runner_call_key_event_sync (proxy, pressed, retro_key,
@@ -692,6 +690,24 @@ key_event_cb (GtkWidget *widget,
return FALSE;
}
+static gboolean
+key_pressed_cb (RetroCore *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state)
+{
+ return key_event (self, keyval, state, TRUE);
+}
+
+static void
+key_released_cb (RetroCore *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state)
+{
+ key_event (self, keyval, state, FALSE);
+}
+
/* Public */
/**
@@ -1754,6 +1770,7 @@ static void
keyboard_widget_notify (RetroCore *self,
GObject *keyboard_widget)
{
+ self->key_controller = NULL;
self->keyboard_widget = NULL;
}
@@ -1771,25 +1788,24 @@ retro_core_set_keyboard (RetroCore *self,
g_return_if_fail (RETRO_IS_CORE (self));
if (self->keyboard_widget != NULL) {
- g_signal_handler_disconnect (G_OBJECT (self->keyboard_widget), self->key_press_event_id);
- g_signal_handler_disconnect (G_OBJECT (self->keyboard_widget), self->key_release_event_id);
+ gtk_widget_remove_controller (self->keyboard_widget, self->key_controller);
+ self->key_controller = NULL;
+
g_object_weak_unref (G_OBJECT (self->keyboard_widget), (GWeakNotify) keyboard_widget_notify, self);
self->keyboard_widget = NULL;
}
if (widget != NULL) {
- self->key_press_event_id =
- g_signal_connect_object (widget,
- "key-press-event",
- G_CALLBACK (key_event_cb),
- self,
- 0);
- self->key_release_event_id =
- g_signal_connect_object (widget,
- "key-release-event",
- G_CALLBACK (key_event_cb),
- self,
- 0);
+ self->key_controller = gtk_event_controller_key_new ();
+ gtk_widget_add_controller (widget, self->key_controller);
+
+ g_signal_connect_object (self->key_controller, "key-pressed",
+ G_CALLBACK (key_pressed_cb), self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (self->key_controller, "key-released",
+ G_CALLBACK (key_released_cb), self,
+ G_CONNECT_SWAPPED);
+
self->keyboard_widget = widget;
g_object_weak_ref (G_OBJECT (widget), (GWeakNotify) keyboard_widget_notify, self);
}
diff --git a/retro-gtk/retro-keyboard.c b/retro-gtk/retro-keyboard.c
index 41b022c..47fc781 100644
--- a/retro-gtk/retro-keyboard.c
+++ b/retro-gtk/retro-keyboard.c
@@ -12,7 +12,7 @@ retro_keyboard_modifier_key_converter (guint keyval,
retro_modifiers |= RETRO_KEYBOARD_MODIFIER_KEY_SHIFT;
if (modifiers & GDK_CONTROL_MASK)
retro_modifiers |= RETRO_KEYBOARD_MODIFIER_KEY_CTRL;
- if (modifiers & GDK_MOD1_MASK)
+ if (modifiers & GDK_ALT_MASK)
retro_modifiers |= RETRO_KEYBOARD_MODIFIER_KEY_ALT;
if (modifiers & GDK_META_MASK)
retro_modifiers |= RETRO_KEYBOARD_MODIFIER_KEY_META;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]