0

I realized that Gtk does not have related API, I need to use Pointer constraints protocol according to Locking mouse cursor to specific window But I don't know how to use this protocol in my Gtk code.

I would like to find a way to integrate it into my Gtk project, what should I learn to do this?

1 Answer 1

0

The code below works fine

#include <gtk/gtk.h>
#include <gdk/wayland/gdkwayland.h>
#include <stdio.h>
#include "pointer-constraints-unstable-v1.h"
#include "relative-pointer-unstable-v1.h"

struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
struct zwp_locked_pointer_v1 *locked_pointer = NULL;
struct wl_seat *seat = NULL;
// GdkWaylandWaylandDeviceget_wl_pointer
struct wl_pointer *pointer = NULL;
struct wl_compositor *compositor = NULL;
struct wl_registry *wl_registry = NULL;
// 下面两个从 gtk 拿, 剩下的就不用了
struct wl_display *wl_display = NULL;
struct wl_surface *wl_surface = NULL;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = NULL;
struct zwp_relative_pointer_v1 *relative_pointer = NULL;
static void relative_pointer_handle_relative_motion(void *data,
                                                    struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1,
                                                    uint32_t utime_hi,
                                                    uint32_t utime_lo,
                                                    wl_fixed_t dx,
                                                    wl_fixed_t dy,
                                                    wl_fixed_t dx_unaccel,
                                                    wl_fixed_t dy_unaccel)
{
    double x = wl_fixed_to_double(dx);
    double y = wl_fixed_to_double(dy);
    printf("Relative motion: dx=%.2f, dy=%.2f\n", x, y);
}

static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
    .relative_motion = relative_pointer_handle_relative_motion,
};
static void seat_handle_capabilities(void *data, struct wl_seat *seat,
                                     enum wl_seat_capability caps)
{
    if (caps & WL_SEAT_CAPABILITY_POINTER)
    {
        pointer = wl_seat_get_pointer(seat);
        // 可以在这里为 pointer 添加监听器
        // wl_pointer_add_listener(pointer, &pointer_listener, NULL);
    }
}
static const struct wl_seat_listener seat_listener = {
    .capabilities = seat_handle_capabilities,
};

static void registry_handle_global(void *data, struct wl_registry *registry,
                                   uint32_t name, const char *interface, uint32_t version)
{
    if (strcmp(interface, wl_compositor_interface.name) == 0)
    {
        compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
    }
    else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0)
    {
        pointer_constraints = wl_registry_bind(registry, name,
                                               &zwp_pointer_constraints_v1_interface, 1);
    }
    else if (strcmp(interface, wl_seat_interface.name) == 0)
    {
        seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
        wl_seat_add_listener(seat, &seat_listener, NULL);
    }
    else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0)
    {
        relative_pointer_manager = wl_registry_bind(registry, name,
                                                    &zwp_relative_pointer_manager_v1_interface, 1);
    }
    // 处理其他全局对象...
}
void unlock_pointer()
{
    if (locked_pointer)
    {
        zwp_locked_pointer_v1_destroy(locked_pointer);
        locked_pointer = NULL;
    }
    if (relative_pointer)
    {
        zwp_relative_pointer_v1_destroy(relative_pointer);
        relative_pointer = NULL;
    }
    printf("Pointer unlocked and relative pointer disabled\n");
}
void lock_pointer()
{
    if (!pointer_constraints || !pointer || !wl_surface)
    {
        fprintf(stderr, "Pointer constraints, pointer, or surface not available\n");
        return;
    }

    struct wl_region *region = wl_compositor_create_region(compositor);
    wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);

    locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
        pointer_constraints,
        wl_surface,
        pointer,
        region,
        ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);

    wl_region_destroy(region);

    if (!locked_pointer)
    {
        fprintf(stderr, "Failed to lock pointer\n");
        return;
    }

    if (relative_pointer_manager && pointer)
    {
        relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(
            relative_pointer_manager, pointer);
        if (relative_pointer)
        {
            zwp_relative_pointer_v1_add_listener(relative_pointer, &relative_pointer_listener, NULL);
        }
        else
        {
            fprintf(stderr, "Failed to create relative pointer\n");
        }
    }

    printf("Pointer locked and relative pointer enabled\n");
}

static void toggle_pointer_lock(GtkWidget *widget, gpointer data)
{
    if (locked_pointer)
    {
        unlock_pointer();
    }
    else
    {
        lock_pointer();
    }
}

static void activate(GtkApplication *app, gpointer user_data)
{
    GtkWidget *window;
    GtkWidget *button;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Wayland Info");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
    button = gtk_button_new_with_label("Toggle Pointer Lock");
    g_signal_connect(button, "clicked", G_CALLBACK(toggle_pointer_lock), NULL);
    gtk_window_set_child(GTK_WINDOW(window), button);

    GdkDisplay *display = gtk_widget_get_display(window);
    if (GDK_IS_WAYLAND_DISPLAY(display))
    {
        wl_display = gdk_wayland_display_get_wl_display(display);
        wl_registry = wl_display_get_registry(wl_display);
        printf("wl_display: %p\n", wl_display);
    }
    else
    {
        printf("Not a Wayland display\n");
    }

    // We need to realize the window to get the surface
    gtk_widget_realize(window);

    GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(window));
    if (GDK_IS_WAYLAND_SURFACE(surface))
    {
        wl_surface = gdk_wayland_surface_get_wl_surface(surface);
        printf("wl_surface: %p\n", wl_surface);
    }
    else
    {
        printf("Not a Wayland surface\n");
    }

    static const struct wl_registry_listener registry_listener = {
        .global = registry_handle_global,
    };
    wl_registry_add_listener(wl_registry, &registry_listener, NULL);
    wl_display_roundtrip(wl_display);

    gtk_widget_show(window);
}

int main(int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return status;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.