Reputation: 2921
I don't know how to set the position of the wayland
window on the screen.
It seems like it is being set somehow, because on my PC the window seems to be in a repetitive location.
I tried the solution in How to set position of a Wayland client's surface in Weston background?
But it crashes my Ubuntu 18.04 (logs me out).
I wrote an example of displaying an image in a window. The image is displayed but I want to be able to set its position on the screen programmatically. Raw image is here.
compile with gcc wayland_example.cpp -lwayland-client -o wayland_example
run with ./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, ®istry_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;
}
Upvotes: 9
Views: 13989
Reputation: 303
wl_shell
is deprecated. Use xdg_toplevel
instead. It is apart of a separate protocol.
sudo apt install wayland-protocol
will install the protocol./usr/share/wayland-protocol/stable/xdg-shell
for xdg-shell.xml
.wayland-scanner
to generate source and header files from xdg-shell.xml
.wayland-scanner private-code < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-protocol.c
wayland-scanner client-header < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.h
To setup a window using this protocol I recommend reading the wayland-book.
Allowing the window to move is as simple as calling xdg_toplevel_move
inside your pointer button callback.
static void pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
{
struct client_state *client_state = data;
xdg_toplevel_move(client_state->xdg_toplevel, client_state->wl_seat, serial);
}
Upvotes: 2
Reputation: 61
This should work (at least it does on Centos, tested on Centos Stream 8 and 9). You can trivially write the steps in a script, or even bind it to a key in case you use to move it to a given position (top-left, top-right, …, as I do):
Install the gnome-extension "Window Calls" (it can be done via Web browser)
Find the windowId under focus and store it in the $wid variable, for instance:
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 ')
Now move the window to any desired position as follows (example):
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
Upvotes: 6
Reputation: 5992
Acccording to this, toplevel windows cannot be moved in Wayland, only subsurfaces can be moved. So : What you are trying to do is impossible. If you really have to move the window, you'll have to switch to Xlib, or, even better, use a GUI framework as Qt5 or Gtk+ 3. The only option you have with Wayland is to make the toplevel as big as the screen, create a subsurface with the image and move that subsurface in the toplevel, as that is possible in Wayland. You can find an example on how to create subsurfaces in the subsurface-test.c file from the Weston compositor.
Upvotes: 5