On Wed, Nov 16, 2022 at 06:52:04PM +0100, Rafael Sadowski wrote:
> On Sun Nov 13, 2022 at 08:37:55PM +0100, Rafael Sadowski wrote:
> > I don't know if this has worked in the past, but when debugging some Qt
> > applications I saw the following debug messages:
> > 
> > $ gwenview
> > qt.qpa.xcb: Has MIT-SHM     : true
> > qt.qpa.xcb: Has MIT-SHM FD  : true
> > qt.qpa.xcb: xcb_shm_attach() failed
> > qt.qpa.xcb: failed to create System V shared memory segment (remote X11 
> > connection?), disabling SHM
> > qt.qpa.xcb: Using XInput version 2.2
> > qt.qpa.screen: Output DP-1 is not connected
> > qt.qpa.screen: Output HDMI-1 is not connected
> > 
> > The code that fails is here. xcb_shm_attach_checked and/or
> > xcb_request_check failed with all my Qt applications.
> > 
> > pobj/qtbase-5.15.6/qtbase-everywhere-src-5.15.6/src/plugins/platforms/xcb/qxcbbackingstore.cpp
> > 
> > bool QXcbBackingStoreImage::createSystemVShmSegment(xcb_connection_t *c, 
> > size_t segmentSize,
> >                                                     xcb_shm_segment_info_t 
> > *shmInfo)
> > {
> >     const int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0x1C0);
> >     if (id == -1) {
> >         qCWarning(lcQpaXcb, "shmget() failed (%d: %s) for size %zu", errno, 
> > strerror(errno), segmentSize);
> >         return false;
> >     }
> > 
> >     void *addr = shmat(id, nullptr, 0);
> >     if (addr == (void *)-1) {
> >         qCWarning(lcQpaXcb, "shmat() failed (%d: %s) for id %d", errno, 
> > strerror(errno), id);
> >         return false;
> >     }
> > 
> >     if (shmctl(id, IPC_RMID, nullptr) == -1)
> >         qCWarning(lcQpaXcb, "Error while marking the shared memory segment 
> > to be destroyed");
> > 
> >     const auto seg = xcb_generate_id(c);
> >     auto cookie = xcb_shm_attach_checked(c, seg, id, false);
> >     auto *error = xcb_request_check(c, cookie);
> >     if (error) {
> >         qCWarning(lcQpaXcb(), "xcb_shm_attach() failed");
> >         free(error);
> >         if (shmdt(addr) == -1)
> >             qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, 
> > strerror(errno), addr);
> >         return false;
> > 
> > 
> > If you want to test it run any Qt application with
> > QT_LOGGING_RULES="qt.qpa.xcb.debug=true" exported.
> > 
> > Should SHM with XCB generally work on OpenBSD?
> > 

Since on OpenBSD the X server and client will generally not run under
the same uid (except if you use startx(1)), you will need to create
the shared memory segment with mode 666 or similar for the X server to
be able to access it (via 'other' rights).

An alternative was introduced in the MIT-SHM extension revision 1.2 a
few years ago. You can use shm_open() and mmap() to create the shared
memory segement and pass the resulting file descriptor to the X
server.

Below is a sample program to demonstrate how to use this with xcb.

/*
 * Copyright (c) 2019 Matthieu Herrb <matth...@herrb.eu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <X11/Xlib-xcb.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <xcb/shm.h>

Display *d;
XShmSegmentInfo shminfo;
XImage *image, *image2;
Pixmap pixmap;
XEvent e;

#define WIDTH 100
#define HEIGHT 100

#define HAVE_SHM_MKSTEMP

/* shminfo->shmid contains the file descriptor */
Bool
XShmAttachFd(Display *dpy, XShmSegmentInfo *shminfo)
{
        xcb_connection_t *xcb_conn = XGetXCBConnection(dpy);
        
        shminfo->shmseg = xcb_generate_id(xcb_conn);
        xcb_shm_attach_fd(xcb_conn, shminfo->shmseg,
            shminfo->shmid, shminfo->readOnly);
        return 1;
}

int
main(int argc, char *argv[])
{
        int major, minor, i;
        Bool pixmaps;
        XGCValues gcval;
        GC gc;
        int fd;
        unsigned char *addr;
        struct stat statb;
        char name[128];
        size_t len;

        d = XOpenDisplay(NULL);
        if (d == NULL)
                errx(2, "Can't open display\n");
                
        if (!XShmQueryVersion(d, &major, &minor, &pixmaps)) {
                fprintf(stderr, "XShm extension not present\n");
                XCloseDisplay(d);
                exit(2);
        } else {
                printf("XShm extension version %d.%d found\n", major, minor);
        }


        image = XShmCreateImage(d, DefaultVisual(d, DefaultScreen(d)),
            DefaultDepth(d, DefaultScreen(d)),
            ZPixmap, 
            NULL,
            &shminfo,
            100, 100);
        if (image == NULL) {
                fprintf(stderr, "XShmCreateImage() failed\n");
                XCloseDisplay(d);
                exit(2);
        }
        len = image->bytes_per_line * image->height;

        strlcpy(name, "/tmp/tshm-XXXXXXXXXX", sizeof(name));
        fd = shm_mkstemp(name);
        if (fd < 0)
                err(2, "shm_open");
        
        addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
            MAP_SHARED|__MAP_NOFAULT, fd, 0);
        if (addr == MAP_FAILED)
                err(2, "mmap");
        if (ftruncate(fd, len) == -1)
                err(2, "ftruncate");
        if (fstat(fd, &statb) == -1)
                err(2, "fstat");
        printf("size %lld\n", statb.st_size);


        printf("Attaching to segment\n");
        shminfo.shmaddr = addr;
        shminfo.shmid = fd;
        shminfo.readOnly = False;
        XShmAttachFd(d, &shminfo);
        XFlush(d);
        
        printf("Shm successfully attached by X server\n");
        
        image->data = addr;
        printf("image size %ld\n", len);
        
        for (i = 0; i < 16; i++)
                shminfo.shmaddr[i] = 0x42+i;

        gcval.function = GXcopy;
        gc = XCreateGC(d, RootWindow(d, DefaultScreen(d)), GCFunction, &gcval);

        pixmap = XCreatePixmap(d, RootWindow(d, DefaultScreen(d)), 100, 100, 
                               DefaultDepth(d, DefaultScreen(d)));

        XShmPutImage(d, pixmap, gc, image, 0, 0, 0, 0, 100, 100, True);
        while (1) {
                XNextEvent(d, &e);
                if (e.type == XShmGetEventBase(d) + ShmCompletion) {
                        break;
                }
        }
        image2 = XGetImage(d, pixmap, 0, 0, 100, 100, AllPlanes, ZPixmap);
        printf("Got image\n");
        for (i = 0; i < 16; i++) {
                printf("%02x ", image2->data[i]);
        }
        printf("\n");
        
        XShmDetach(d, &shminfo);
        XDestroyImage(image);
        close(fd);
                
        exit(0);
}

-- 
Matthieu Herrb

Reply via email to