如何在屏幕上设置Wayland窗口的位置?

7

我不知道如何设置 wayland 窗口在屏幕上的位置。

看起来它已经以某种方式被设置了,因为在我的电脑上窗口似乎总是在同一位置。

我尝试了这个解决方案,但它会导致我的 Ubuntu 18.04 崩溃(退出登录)。

我写了一个示例来显示一个窗口中的图像。图像可以被显示,但我想能够通过编程来设置其在屏幕上的位置。原始图像在这里

使用 gcc wayland_example.cpp -lwayland-client -o wayland_example 编译。

使用 ./wayland_example <path_to_image> 运行。

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <wayland-client.h>

static struct wl_display *display = nullptr;
static struct wl_compositor *compositor = nullptr;
static struct wl_surface *surface;
static struct wl_shell *shell;
static struct wl_shell_surface *shell_surface;
static struct wl_shm *shm;
static struct wl_buffer *buffer;
static struct wl_callback *frame_callback;
static struct wl_registry *registry;

void *shm_data;
uint32_t *im_ptr;

static int w = 0;
static int h = 0;

static void handle_ping(__attribute__((unused)) void *data,
                        struct wl_shell_surface *in_shell_surface, uint32_t serial) {
  wl_shell_surface_pong(in_shell_surface, serial);
}

static void handle_configure(__attribute__((unused)) void *data,
                             __attribute__((unused)) struct wl_shell_surface *in_shell_surface,
                             __attribute__((unused)) uint32_t edges,
                             __attribute__((unused)) int width,
                             __attribute__((unused)) int height) {}

static void handle_popup_done(__attribute__((unused)) void *data,
                              __attribute__((unused)) struct wl_shell_surface *in_shell_surface) {}

static const struct wl_shell_surface_listener kShellSurfaceListener = {
    handle_ping, handle_configure, handle_popup_done};

static int set_cloexec_or_close(int fd) {
  int flags;

  if (fd == -1) {
    return -1;
  }

  flags = fcntl(fd, F_GETFD);
  if (flags == -1) {
    goto err;
  }

  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
    goto err;
  }

  return fd;

err:
  close(fd);
  return -1;
}

static int create_tmpfile_cloexec(char *tmpname) {
  int fd;

#ifdef HAVE_MKOSTEMP
  fd = mkostemp(tmpname, O_CLOEXEC);
  if (fd >= 0) {
    unlink(tmpname);
  }
#else  /* HAVE_MKOSTEMP */
  fd = mkstemp(tmpname);
  if (fd >= 0) {
    fd = set_cloexec_or_close(fd);
    unlink(tmpname);
  }
#endif /* WAYLAND */

  return fd;
}

/*
 * Create a new, unique, anonymous file of the given size, and
 * return the file descriptor for it. The file descriptor is set
 * CLOEXEC. The file is immediately suitable for mmap()'ing
 * the given size at offset zero.
 *
 * The file should not have a permanent backing store like a disk,
 * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
 *
 * The file name is deleted from the file system.
 *
 * The file is suitable for buffer sharing between processes by
 * transmitting the file descriptor over Unix sockets using the
 * SCM_RIGHTS methods.
 */
static int os_create_anonymous_file(off_t size) {
  static const char kTemplate1[] = "/weston-shared-XXXXXX";

  const char *path = getenv("XDG_RUNTIME_DIR");
  if (!path) {
    errno = ENOENT;
    return -1;
  }

  size_t total_len = strlen(path) + sizeof(kTemplate1);
  char *name = reinterpret_cast<char *>(malloc(total_len));
  if (!name) {
    return -1;
  }
  snprintf(name, total_len, "%s%s", path, kTemplate1);

  int fd = create_tmpfile_cloexec(name);

  free(name);

  if (fd < 0) {
    return -1;
  }

  if (ftruncate(fd, size) < 0) {
    close(fd);
    return -1;
  }

  return fd;
}

static void paint_pixels() {
  uint32_t *pixel = reinterpret_cast<uint32_t *>(shm_data);

  memcpy(pixel, im_ptr, w * h * 4);
}

static void redraw(__attribute__((unused)) void *data,
                   __attribute__((unused)) struct wl_callback *callback,
                   __attribute__((unused)) uint32_t time);

static const struct wl_callback_listener kFrameListener = {redraw};

static void redraw(__attribute__((unused)) void *data,
                   __attribute__((unused)) struct wl_callback *callback,
                   __attribute__((unused)) uint32_t time) {
  wl_callback_destroy(frame_callback);
  wl_surface_damage(surface, 0, 0, w, h);
  paint_pixels();
  frame_callback = wl_surface_frame(surface);
  wl_surface_attach(surface, buffer, 0, 0);
  wl_callback_add_listener(frame_callback, &kFrameListener, nullptr);
  wl_surface_commit(surface);
}

static struct wl_buffer *create_buffer() {
  struct wl_shm_pool *pool;
  int stride = w * 4;  // 4 bytes per pixel
  int size = stride * h;
  int fd;
  struct wl_buffer *buff;

  fd = os_create_anonymous_file(size);
  if (fd < 0) {
    printf("creating a buffer file for %d B failed\n", size);
    exit(1);
  }

  shm_data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (shm_data == MAP_FAILED) {
    printf("mmap failed\n");
    close(fd);
    exit(1);
  }

  pool = wl_shm_create_pool(shm, fd, size);
  buff = wl_shm_pool_create_buffer(pool, 0, w, h, stride, WL_SHM_FORMAT_XRGB8888);
  wl_shm_pool_destroy(pool);
  return buff;
}

static void create_window() {
  buffer = create_buffer();

  wl_surface_attach(surface, buffer, 0, 0);
  // wl_surface_damage(surface, 0, 0, WIDTH, HEIGHT);
  wl_surface_commit(surface);
}

static void shm_format(__attribute__((unused)) void *data,
                       __attribute__((unused)) struct wl_shm *wl_shm,
                       __attribute__((unused)) uint32_t format) {}

struct wl_shm_listener shm_listener = {shm_format};

static void global_registry_handler(__attribute__((unused)) void *data,
                                    struct wl_registry *in_registry, uint32_t id,
                                    const char *interface,
                                    __attribute__((unused)) uint32_t version) {
  if (strcmp(interface, "wl_compositor") == 0) {
    compositor =
        (struct wl_compositor *)wl_registry_bind(in_registry, id, &wl_compositor_interface, 1);
  } else if (strcmp(interface, "wl_shell") == 0) {
    shell = (struct wl_shell *)wl_registry_bind(in_registry, id, &wl_shell_interface, 1);
  } else if (strcmp(interface, "wl_shm") == 0) {
    shm = (struct wl_shm *)wl_registry_bind(in_registry, id, &wl_shm_interface, 1);
    wl_shm_add_listener(shm, &shm_listener, nullptr);
  }
}

static void global_registry_remover(__attribute__((unused)) void *data,
                                    __attribute__((unused)) struct wl_registry *in_registry,
                                    __attribute__((unused)) uint32_t id) {}

static const struct wl_registry_listener registry_listener = {global_registry_handler,
                                                              global_registry_remover};

int main(int argc, char **argv) {
  w = 640;
  h = 480;

  im_ptr = (uint32_t *)malloc(w * h * 4);

  FILE *f = fopen(argv[1], "rb");
  fread(im_ptr, w * h * 4, 1, f);
  fclose(f);

  display = wl_display_connect(nullptr);
  if (nullptr == display) {
    printf("Can't connect to display\n");
    exit(1);
  }
  printf("connected to display\n");

  registry = wl_display_get_registry(display);
  wl_registry_add_listener(registry, &registry_listener, nullptr);

  wl_display_dispatch(display);
  wl_display_roundtrip(display);

  if (nullptr == compositor) {
    printf("Can't find compositor\n");
    exit(1);
  } else {
    printf("Found compositor\n");
  }

  surface = wl_compositor_create_surface(compositor);
  if (nullptr == surface) {
    printf("Can't create surface\n");
    exit(1);
  } else {
    printf("Created surface\n");
  }

  shell_surface = wl_shell_get_shell_surface(shell, surface);
  if (nullptr == shell_surface) {
    printf("Can't create shell surface\n");
    exit(1);
  } else {
    printf("Created shell surface\n");
  }
  wl_shell_surface_set_toplevel(shell_surface);

  wl_shell_surface_add_listener(shell_surface, &kShellSurfaceListener, nullptr);

  frame_callback = wl_surface_frame(surface);
  wl_callback_add_listener(frame_callback, &kFrameListener, nullptr);

  create_window();
  redraw(nullptr, nullptr, 0);

  if (wl_display_dispatch(display) == -1) {
    return 1;
  }
  getchar();

  wl_display_disconnect(display);
  free(registry);
  registry = nullptr;

  free(im_ptr);

  return 0;
}


我按照你写的编译并运行了,但是它给了我“无法连接到显示器”的错误信息?! - TheEagle
请澄清:标题说您想移动一个窗口,但在您的帖子中,您说您想移动一张图片。 - TheEagle
你需要使用Wayland作为屏幕管理器。如果你正在使用Ubuntu,你需要注销并在登录时在设置中选择Wayland。 - Ophir Carmi
我想移动一个窗口。图像在窗口中。这只是一个示例。 - Ophir Carmi
根据这里的说明,只需更改wl_surface_attach(surface, buffer, 0, 0);中的数字即可:xy参数指定了新挂起缓冲区左上角的位置,相对于当前缓冲区左上角在表面本地坐标系中的位置。请查看是否有帮助(我没有找到在我的机器上测试它的方法),如果有用,我会将其作为正式答案。 - TheEagle
显示剩余7条评论
2个回答

5

这应该可以工作(至少在Centos上是这样的,在Centos Stream 8和9上进行了测试)。您可以轻松地编写脚本中的步骤,甚至将其绑定到一个键上,以便在将其移动到给定位置(左上角、右上角等)时使用,就像我一样:

  1. 安装gnome扩展程序“Window Calls”(可以通过Web浏览器完成)

  2. 查找当前焦点下的窗口ID并将其存储在$wid变量中,例如:

    window_list=$(gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell/Extensions/Windows --method org.gnome.Shell.Extensions.Windows.List)
    wid=$(echo "${window_list:2:-3}" | jq -c '.[] | select (.focus == true) | .id ')
    
  3. 现在按照以下方式将窗口移动到任何所需位置(示例):

    x=500
    y=275
    gdbus call --session --dest org.gnome.Shell --object-path /org/gnome/Shell/Extensions/Windows --method org.gnome.Shell.Extensions.Windows.Move ${wid} $x $y
    

4
根据this,在Wayland中无法移动顶层窗口,只能移动子表面。因此:您正在尝试的操作是不可能的。如果您真的必须移动窗口,则必须切换到Xlib,或者更好地使用GUI框架,如Qt5或Gtk+ 3。在Wayland中,您唯一的选择是将顶层设置为屏幕大小,创建一个带有图像的子表面,并将该子表面移动到顶层,因为这在Wayland中是可行的。您可以在来自Weston合成器的subsurface-test.c文件中找到有关如何创建子表面的示例。

更多信息请参见:https://wayland-book.com/xdg-shell-in-depth/interactive.html - Zingam
1
如果应用程序在Wayland后端上运行,那么转移到像Qt或GTK这样的GUI框架就无关紧要了...此外,例如,GTK 4直接删除了所有窗口位置API,因为它得出结论:处理这些内容不是工具包的责任(而是WM的责任)-因此,现在在GTK 3或其他可以移动窗口的版本中开始编写代码的人,在桌面更改WM或转到GTK 4时将感到失望。来源:个人经验... - underscore_d
有没有关于创建居中窗口的 Wayland 提示? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接