2024-02-08 16:42:38 +00:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#define DEMO_TYPE_IMAGE (demo_image_get_type ())
|
|
|
|
|
|
|
|
G_DECLARE_FINAL_TYPE (DemoImage, demo_image, DEMO, IMAGE, GtkWidget)
|
|
|
|
|
|
|
|
struct _DemoImage {
|
|
|
|
GtkWidget parent_instance;
|
|
|
|
|
|
|
|
GdkTexture *texture;
|
|
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (DemoImage, demo_image, GTK_TYPE_WIDGET)
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_init (DemoImage *demo)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
DemoImage *demo = DEMO_IMAGE (object);
|
|
|
|
|
|
|
|
g_clear_object (&demo->texture);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (demo_image_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_measure (GtkWidget *widget,
|
|
|
|
GtkOrientation orientation,
|
|
|
|
int for_size,
|
|
|
|
int *minimum,
|
|
|
|
int *natural,
|
|
|
|
int *minimum_baseline,
|
|
|
|
int *natural_baseline)
|
|
|
|
{
|
|
|
|
DemoImage *demo = DEMO_IMAGE (widget);
|
|
|
|
double scale;
|
|
|
|
|
|
|
|
g_print ("measure\n");
|
|
|
|
scale = gdk_surface_get_scale (gtk_native_get_surface (gtk_widget_get_native (widget)));
|
|
|
|
|
|
|
|
if (orientation == GTK_ORIENTATION_VERTICAL)
|
|
|
|
{
|
|
|
|
*minimum = *natural = (int) ceil (gdk_texture_get_height (demo->texture) / scale);
|
|
|
|
g_print ("requesting height: %d\n", *minimum);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*minimum = *natural = (int) ceil (gdk_texture_get_width (demo->texture) / scale);
|
|
|
|
g_print ("requesting width: %d\n", *minimum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_snapshot (GtkWidget *widget,
|
|
|
|
GtkSnapshot *snapshot)
|
|
|
|
{
|
|
|
|
DemoImage *demo = DEMO_IMAGE (widget);
|
|
|
|
GtkNative *native = gtk_widget_get_native (widget);
|
|
|
|
graphene_point_t point;
|
|
|
|
double scale;
|
|
|
|
double ox, oy;
|
|
|
|
double x, y, width, height;
|
|
|
|
|
|
|
|
g_print ("snapshot\n");
|
2024-02-08 20:31:38 +00:00
|
|
|
|
2024-02-08 16:42:38 +00:00
|
|
|
scale = gdk_surface_get_scale (gtk_native_get_surface (native));
|
|
|
|
|
|
|
|
g_print ("scale %f\n", scale);
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
/* width and height that give us 1-1 mapping to device pixels */
|
|
|
|
width = gdk_texture_get_width (demo->texture) / scale;
|
|
|
|
height = gdk_texture_get_height (demo->texture) / scale;
|
|
|
|
|
2024-02-08 16:42:38 +00:00
|
|
|
gtk_native_get_surface_transform (native, &ox, &oy);
|
|
|
|
|
|
|
|
g_print ("surface transform %f %f\n", ox, oy);
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
x = (gtk_widget_get_width (widget) - width) / 2;
|
|
|
|
y = (gtk_widget_get_height (widget) - height) / 2;
|
|
|
|
|
|
|
|
g_print ("texture origin in widget coordinates: %f %f\n", x, y);
|
|
|
|
|
|
|
|
if (!gtk_widget_compute_point (widget, GTK_WIDGET (native), &GRAPHENE_POINT_INIT (x, y), &point))
|
2024-02-08 16:42:38 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
x = point.x;
|
|
|
|
y = point.y;
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
g_print ("in window (app) coordinates: %f %f\n", x, y);
|
2024-02-08 16:42:38 +00:00
|
|
|
|
|
|
|
x += ox;
|
|
|
|
y += oy;
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
g_print ("in surface (app) coordinates: %f %f\n", x, y);
|
2024-02-08 16:42:38 +00:00
|
|
|
|
|
|
|
x *= scale;
|
|
|
|
y *= scale;
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
g_print ("in surface (device) coordinates: %f %f\n", x, y);
|
2024-02-08 16:42:38 +00:00
|
|
|
|
|
|
|
/* Now x, y are the surface (device) coordinates of the widget's origin */
|
|
|
|
|
|
|
|
/* Round up to the next full device pixel */
|
|
|
|
|
|
|
|
x = ceil (x);
|
|
|
|
y = ceil (y);
|
|
|
|
|
|
|
|
g_print ("rounded up: %f %f\n", x, y);
|
|
|
|
|
|
|
|
/* And back to widget coordinates */
|
|
|
|
|
|
|
|
x /= scale;
|
|
|
|
y /= scale;
|
|
|
|
|
|
|
|
x -= ox;
|
|
|
|
y -= oy;
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
if (!gtk_widget_compute_point (widget, GTK_WIDGET (native), &GRAPHENE_POINT_INIT (0, 0), &point))
|
|
|
|
return;
|
|
|
|
|
2024-02-08 16:42:38 +00:00
|
|
|
x -= point.x;
|
|
|
|
y -= point.y;
|
|
|
|
|
|
|
|
g_print ("bounds: %f %f %f %f\n", x, y, width, height);
|
|
|
|
|
|
|
|
gtk_snapshot_append_texture (snapshot, demo->texture,
|
|
|
|
&GRAPHENE_RECT_INIT (x, y, width, height));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_scale (GObject *object,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
GtkWidget *widget)
|
|
|
|
{
|
|
|
|
g_print ("scale change!\n");
|
|
|
|
|
|
|
|
gtk_widget_queue_resize (widget);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_realize (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GtkNative *native;
|
|
|
|
GdkSurface *surface;
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS (demo_image_parent_class)->realize (widget);
|
|
|
|
|
|
|
|
g_print ("realize\n");
|
|
|
|
|
|
|
|
native = gtk_widget_get_native (widget);
|
|
|
|
surface = gtk_native_get_surface (native);
|
|
|
|
g_signal_connect (surface, "notify::scale",
|
|
|
|
G_CALLBACK (notify_scale), widget);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_unrealize (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GtkNative *native;
|
|
|
|
GdkSurface *surface;
|
|
|
|
|
|
|
|
native = gtk_widget_get_native (widget);
|
|
|
|
surface = gtk_native_get_surface (native);
|
|
|
|
g_signal_handlers_disconnect_by_func (surface, notify_scale, widget);
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS (demo_image_parent_class)->unrealize (widget);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
demo_image_class_init (DemoImageClass *class)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
|
|
|
|
|
|
object_class->dispose = demo_image_dispose;
|
|
|
|
|
|
|
|
widget_class->realize = demo_image_realize;
|
|
|
|
widget_class->unrealize = demo_image_unrealize;
|
|
|
|
|
|
|
|
widget_class->measure = demo_image_measure;
|
|
|
|
widget_class->snapshot = demo_image_snapshot;
|
|
|
|
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GtkWidget *
|
|
|
|
demo_image_new (GdkTexture *texture)
|
|
|
|
{
|
|
|
|
DemoImage *demo;
|
|
|
|
|
|
|
|
demo = g_object_new (DEMO_TYPE_IMAGE, NULL);
|
|
|
|
|
|
|
|
demo->texture = g_object_ref (texture);
|
|
|
|
|
|
|
|
g_print ("texture size %dx%d\n",
|
|
|
|
gdk_texture_get_width (texture),
|
|
|
|
gdk_texture_get_height (texture));
|
|
|
|
|
|
|
|
return GTK_WIDGET (demo);
|
|
|
|
}
|
|
|
|
|
|
|
|
static GdkTexture *
|
|
|
|
make_checkerboard_texture (int width, int height)
|
|
|
|
{
|
|
|
|
guint32 *data, *row;
|
|
|
|
GBytes *bytes;
|
|
|
|
GdkTexture *texture;
|
|
|
|
|
|
|
|
data = (guint32 *) g_new (guint32, width * height);
|
|
|
|
|
|
|
|
for (int y = 0; y < height; y++)
|
|
|
|
{
|
|
|
|
row = data + y * width;
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
{
|
|
|
|
if ((x + y) % 2)
|
|
|
|
row[x] = 0xffffffff;
|
|
|
|
else
|
|
|
|
row[x] = 0xff000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes = g_bytes_new_take (data, height * width * 4);
|
|
|
|
|
|
|
|
texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, width * 4);
|
|
|
|
|
|
|
|
g_bytes_unref (bytes);
|
|
|
|
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
2024-02-08 20:31:38 +00:00
|
|
|
static gboolean
|
|
|
|
toggle_fullscreen (GtkWidget *widget,
|
|
|
|
GVariant *args,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkWindow *window = GTK_WINDOW (widget);
|
|
|
|
|
|
|
|
if (gtk_window_is_fullscreen (window))
|
|
|
|
gtk_window_unfullscreen (window);
|
|
|
|
else
|
|
|
|
gtk_window_fullscreen (window);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2024-02-08 16:42:38 +00:00
|
|
|
int
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
GtkWidget *window;
|
|
|
|
GdkTexture *texture;
|
|
|
|
GError *error = NULL;
|
2024-02-08 20:31:38 +00:00
|
|
|
GtkEventController *controller;
|
|
|
|
GtkShortcutTrigger *trigger;
|
|
|
|
GtkShortcutAction *action;
|
|
|
|
GtkShortcut *shortcut;
|
2024-02-08 16:42:38 +00:00
|
|
|
|
|
|
|
gtk_init ();
|
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
{
|
|
|
|
texture = gdk_texture_new_from_filename (argv[1], &error);
|
|
|
|
if (!texture)
|
|
|
|
g_error ("%s", error->message);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
texture = make_checkerboard_texture (100, 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
window = gtk_window_new ();
|
2024-02-08 20:31:38 +00:00
|
|
|
|
|
|
|
controller = gtk_shortcut_controller_new ();
|
|
|
|
trigger = gtk_keyval_trigger_new (GDK_KEY_F11, GDK_NO_MODIFIER_MASK);
|
|
|
|
action = gtk_callback_action_new (toggle_fullscreen, NULL, NULL);
|
|
|
|
shortcut = gtk_shortcut_new (trigger, action);
|
|
|
|
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
|
|
|
|
gtk_widget_add_controller (window, controller);
|
2024-02-08 16:42:38 +00:00
|
|
|
|
|
|
|
gtk_window_set_child (GTK_WINDOW (window), demo_image_new (texture));
|
|
|
|
|
|
|
|
g_object_unref (texture);
|
|
|
|
|
|
|
|
gtk_window_present (GTK_WINDOW (window));
|
|
|
|
|
|
|
|
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
|
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|