diff options
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | plugingui/nativewindow_x11.cc | 339 | ||||
-rw-r--r-- | plugingui/nativewindow_x11.h | 26 |
3 files changed, 219 insertions, 151 deletions
diff --git a/configure.ac b/configure.ac index ee13559..f93e6ae 100644 --- a/configure.ac +++ b/configure.ac @@ -156,9 +156,10 @@ AS_IF( dnl Check for Xlib dnl ====================== PKG_CHECK_MODULES(X11, x11 >= 1.0) + PKG_CHECK_MODULES(XEXT, xext >= 1.0) - GUI_CPPFLAGS="-DX11 $X11_CFLAGS" - GUI_LIBS="$X11_LIBS"], + GUI_CPPFLAGS="-DX11 $X11_CFLAGS $XEXT_CFLAGS" + GUI_LIBS="$X11_LIBS $XEXT_LIBS"], [test "x$enable_gui" = "xwin32"], [AC_MSG_RESULT([Setting gui backend to Win32]) diff --git a/plugingui/nativewindow_x11.cc b/plugingui/nativewindow_x11.cc index c87d3df..465b03e 100644 --- a/plugingui/nativewindow_x11.cc +++ b/plugingui/nativewindow_x11.cc @@ -26,7 +26,14 @@ */ #include "nativewindow_x11.h" +//http://www.mesa3d.org/brianp/xshm.c + #include <X11/Xutil.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <cerrno> +#include <cstring> + #include <stdlib.h> #include <chrono> @@ -34,11 +41,11 @@ #include "window.h" -namespace GUI { +namespace GUI +{ NativeWindowX11::NativeWindowX11(void* native_window, Window& window) - : buffer(nullptr) - , window(window) + : window(window) { display = XOpenDisplay(nullptr); if(display == nullptr) @@ -48,6 +55,8 @@ NativeWindowX11::NativeWindowX11(void* native_window, Window& window) } screen = DefaultScreen(display); + visual = DefaultVisual(display, screen); + depth = DefaultDepth(display, screen); ::Window parentWindow; if(native_window) @@ -60,18 +69,17 @@ NativeWindowX11::NativeWindowX11(void* native_window, Window& window) } // Create the window - unsigned long border = 0; XSetWindowAttributes swa; swa.backing_store = Always; xwindow = XCreateWindow(display, parentWindow, window.x(), window.y(), window.width(), window.height(), - border, + 0, // border CopyFromParent, // depth CopyFromParent, // class CopyFromParent, // visual - CWBackingStore, + 0,//CWBackingStore, &swa); long mask = (StructureNotifyMask | @@ -103,10 +111,7 @@ NativeWindowX11::~NativeWindowX11() return; } - if(buffer) - { - XDestroyImage(buffer); - } + deallocateShmImage(); XFreeGC(display, gc); @@ -184,139 +189,9 @@ void NativeWindowX11::hide() XUnmapWindow(display, xwindow); } -static int get_byte_order (void) -{ - union { - char c[sizeof(short)]; - short s; - } order; - - order.s = 1; - if((1 == order.c[0])) - { - return LSBFirst; - } - else - { - return MSBFirst; - } -} - -XImage* NativeWindowX11::createImageFromBuffer(unsigned char* buf, - int width, int height) -{ - int depth; - XImage* img = nullptr; - Visual* vis; - double rRatio; - double gRatio; - double bRatio; - int outIndex = 0; - int i; - int numBufBytes = (3 * (width * height)); - - depth = DefaultDepth(display, screen); - vis = DefaultVisual(display, screen); - - rRatio = vis->red_mask / 255.0; - gRatio = vis->green_mask / 255.0; - bRatio = vis->blue_mask / 255.0; - - if(depth >= 24) - { - size_t numNewBufBytes = (4 * (width * height)); - u_int32_t *newBuf = (u_int32_t *)malloc (numNewBufBytes); - - for(i = 0; i < numBufBytes; ++i) - { - unsigned int r, g, b; - r = (buf[i] * rRatio); - ++i; - g = (buf[i] * gRatio); - ++i; - b = (buf[i] * bRatio); - - r &= vis->red_mask; - g &= vis->green_mask; - b &= vis->blue_mask; - - newBuf[outIndex] = r | g | b; - ++outIndex; - } - - img = XCreateImage (display, - CopyFromParent, depth, - ZPixmap, 0, - (char*) newBuf, - width, height, - 32, 0); - } - else - { - if(depth >= 15) - { - size_t numNewBufBytes = (2 * (width * height)); - u_int16_t* newBuf = (u_int16_t*)malloc (numNewBufBytes); - - for(i = 0; i < numBufBytes; ++i) - { - unsigned int r, g, b; - - r = (buf[i] * rRatio); - ++i; - g = (buf[i] * gRatio); - ++i; - b = (buf[i] * bRatio); - - r &= vis->red_mask; - g &= vis->green_mask; - b &= vis->blue_mask; - - newBuf[outIndex] = r | g | b; - ++outIndex; - } - - img = XCreateImage(display, CopyFromParent, depth, ZPixmap, 0, - (char*)newBuf, width, height, 16, 0); - } - else - { - //fprintf (stderr, "This program does not support displays with a depth less than 15."); - return nullptr; - } - } - - XInitImage (img); - - // Set the client's byte order, so that XPutImage knows what - // to do with the data. - // The default in a new X image is the server's format, which - // may not be what we want. - if((LSBFirst == get_byte_order ())) - { - img->byte_order = LSBFirst; - } - else - { - img->byte_order = MSBFirst; - } - - // The bitmap_bit_order doesn't matter with ZPixmap images. - img->bitmap_bit_order = MSBFirst; - - return img; -} - void NativeWindowX11::handleBuffer() { - if(buffer) - { - XDestroyImage(buffer); - } - - buffer = createImageFromBuffer(window.wpixbuf.buf, - window.wpixbuf.width, - window.wpixbuf.height); + updateImageFromBuffer(); } void NativeWindowX11::redraw() @@ -326,13 +201,16 @@ void NativeWindowX11::redraw() return; } - if(buffer == nullptr) + if(!image) { window.updateBuffer(); + handleBuffer(); } - XPutImage(display, xwindow, gc, buffer, 0, 0, 0, 0, - window.width(), window.height()); + XShmPutImage(display, xwindow, gc, image, 0, 0, 0, 0, + std::min(image->width, (int)window.width()), + std::min(image->height, (int)window.height()), false); + XFlush(display); } @@ -369,6 +247,11 @@ std::shared_ptr<Event> NativeWindowX11::getNextEvent() return nullptr; } + if(!XPending(display)) + { + return nullptr; + } + XEvent xEvent; XNextEvent(display, &xEvent); return translateXMessage(xEvent); @@ -381,6 +264,11 @@ std::shared_ptr<Event> NativeWindowX11::peekNextEvent() return nullptr; } + if(!XPending(display)) + { + return nullptr; + } + XEvent peekXEvent; XPeekEvent(display, &peekXEvent); return translateXMessage(peekXEvent, true); @@ -393,6 +281,7 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, switch(xevent.type) { case MotionNotify: + //DEBUG(x11, "MotionNotify"); { auto mouseMoveEvent = std::make_shared<MouseMoveEvent>(); mouseMoveEvent->window_id = xevent.xmotion.window; @@ -403,6 +292,16 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, break; case Expose: + //DEBUG(x11, "Expose"); + //if(!peek) + //{ + // XEvent peekXEvent; + // while(XCheckWindowEvent(display, xwindow, Expose, &peekXEvent)) + // { + // xevent.xexpose = peekXEvent.xexpose; + // } + //} + if(xevent.xexpose.count == 0) { auto repaintEvent = std::make_shared<RepaintEvent>(); @@ -416,7 +315,17 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, break; case ConfigureNotify: + //DEBUG(x11, "ConfigureNotify"); { + //if(!peek) + //{ + // XEvent peekXEvent; + // while(XCheckWindowEvent(display, xwindow, ConfigureNotify, &peekXEvent)) + // { + // xevent.xconfigure = peekXEvent.xconfigure; + // } + //} + if((window.width() != (std::size_t)xevent.xconfigure.width) || (window.height() != (std::size_t)xevent.xconfigure.height)) { @@ -427,7 +336,7 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, event = resizeEvent; } else if((window.windowX() != (std::size_t)xevent.xconfigure.x) || - (window.windowY() != (std::size_t)xevent.xconfigure.y)) + (window.windowY() != (std::size_t)xevent.xconfigure.y)) { auto moveEvent = std::make_shared<MoveEvent>(); moveEvent->window_id = xevent.xconfigure.window; @@ -440,6 +349,7 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, case ButtonPress: case ButtonRelease: + //DEBUG(x11, "ButtonPress"); { if((xevent.xbutton.button == 4) || (xevent.xbutton.button == 5)) { @@ -502,6 +412,7 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, case KeyPress: case KeyRelease: + //DEBUG(x11, "KeyPress"); { auto keyEvent = std::make_shared<KeyEvent>(); keyEvent->window_id = xevent.xkey.window; @@ -539,6 +450,7 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, break; case ClientMessage: + //DEBUG(x11, "ClientMessage"); if(((unsigned int)xevent.xclient.data.l[0] == wmDeleteMessage)) { auto closeEvent = std::make_shared<CloseEvent>(); @@ -549,6 +461,8 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, case EnterNotify: case LeaveNotify: case MapNotify: + case MappingNotify: + //DEBUG(x11, "EnterNotify"); // There's nothing to do here atm. break; @@ -560,4 +474,139 @@ std::shared_ptr<Event> NativeWindowX11::translateXMessage(XEvent& xevent, return event; } +void NativeWindowX11::allocateShmImage(std::size_t width, std::size_t height) +{ + DEBUG(x11, "(Re)alloc XShmImage (%d, %d)", width, height); + + if(image) + { + deallocateShmImage(); + } + + if(!XShmQueryExtension(display)) + { + ERR(x11, "XShmExtension not available"); + return; + } + + image = XShmCreateImage(display, visual, depth, + ZPixmap, nullptr, &shm_info, + width, height); + if(image == nullptr) + { + ERR(x11, "XShmCreateImage failed!\n"); + return; + } + + std::size_t byte_size = image->bytes_per_line * image->height; + + // Allocate shm buffer + int shm_id = shmget(IPC_PRIVATE, byte_size, IPC_CREAT|0777); + if(shm_id == -1) + { + ERR(x11, "shmget failed: %s", strerror(errno)); + return; + } + + shm_info.shmid = shm_id; + + // Attach share memory bufer + void* shm_addr = shmat(shm_id, nullptr, 0); + if(reinterpret_cast<int>(shm_addr) == -1) + { + ERR(x11, "shmat failed: %s", strerror(errno)); + return; + } + + shm_info.shmaddr = reinterpret_cast<char*>(shm_addr); + image->data = shm_info.shmaddr; + shm_info.readOnly = false; + + // This may trigger the X protocol error we're ready to catch: + XShmAttach(display, &shm_info); + XSync(display, false); + + // Make the shm id unavailable to others + shmctl(shm_id, IPC_RMID, 0); +} + +void NativeWindowX11::deallocateShmImage() +{ + if(image == nullptr) + { + return; + } + + XFlush(display); + XShmDetach(display, &shm_info); + XDestroyImage(image); + image = nullptr; + shmdt(shm_info.shmaddr); +} + +void NativeWindowX11::updateImageFromBuffer() +{ + DEBUG(x11, "depth: %d", depth); + + auto width = window.wpixbuf.width; + auto height = window.wpixbuf.height; + + // If image hasn't been allocated yet or if the image backbuffer is + // too small, (re)allocate with a suitable size. + if((image == nullptr) || + ((int)width > image->width) || + ((int)height > image->height)) + { + constexpr std::size_t step_size = 128; // size increments + std::size_t new_width = ((width / step_size) + 1) * step_size; + std::size_t new_height = ((height / step_size) + 1) * step_size; + allocateShmImage(new_width, new_height); + } + + auto stride = image->width; + + std::uint8_t* pixel_buffer = (std::uint8_t*)window.wpixbuf.buf; + + if(depth >= 24) // RGB 888 format + { + std::uint32_t* shm_addr = (std::uint32_t*)shm_info.shmaddr; + + std::size_t x_stride = 0; + std::size_t x_pos = 0; + for(std::size_t y = 0; y < height; ++y) + { + for(std::size_t x = 0; x < width; ++x) + { + const std::uint8_t red = pixel_buffer[x_pos * 3]; + const std::uint8_t green = pixel_buffer[x_pos * 3 + 1]; + const std::uint8_t blue = pixel_buffer[x_pos * 3 + 2]; + shm_addr[x_stride] = (red << 16) | (green << 8) | blue; + ++x_pos; + ++x_stride; + } + x_stride += (stride - width); + } + } + else if(depth >= 15) // RGB 565 format + { + std::uint16_t* shm_addr = (std::uint16_t*)shm_info.shmaddr; + + std::size_t x_stride = 0; + std::size_t x_pos = 0; + for(std::size_t y = 0; y < height; ++y) + { + for(std::size_t x = 0; x < width; ++x) + { + const std::uint8_t red = pixel_buffer[x_pos * 3]; + const std::uint8_t green = pixel_buffer[x_pos * 3 + 1]; + const std::uint8_t blue = pixel_buffer[x_pos * 3 + 2]; + shm_addr[x_stride] = (red << 11) | (green << 5) | blue; + ++x_pos; + ++x_stride; + } + x_stride += (stride - width); + } + } +} + } // GUI:: diff --git a/plugingui/nativewindow_x11.h b/plugingui/nativewindow_x11.h index db6ec76..f36be51 100644 --- a/plugingui/nativewindow_x11.h +++ b/plugingui/nativewindow_x11.h @@ -27,13 +27,18 @@ #pragma once #include <X11/Xlib.h> +#include <X11/extensions/XShm.h> #include "nativewindow.h" -namespace GUI { +namespace GUI +{ class Window; -class NativeWindowX11 : public NativeWindow { + +class NativeWindowX11 + : public NativeWindow +{ public: NativeWindowX11(void* native_window, Window& window); ~NativeWindowX11(); @@ -54,11 +59,22 @@ public: private: std::shared_ptr<Event> translateXMessage(XEvent& xevent, bool peek = false); - XImage* createImageFromBuffer(unsigned char* buf, int width, int height); + + //! Allocate new shared memory buffer for the pixel buffer. + //! Frees the existing buffer if there is one. + void allocateShmImage(std::size_t width, std::size_t height); + + //! Deallocate image and shm resources. + void deallocateShmImage(); + + //! Copy data from the pixel buffer into the shared memory + void updateImageFromBuffer(); + + XShmSegmentInfo shm_info; + XImage* image{nullptr}; ::Window xwindow{0}; GC gc{0}; - XImage* buffer{nullptr}; Window& window; @@ -66,6 +82,8 @@ private: Display* display{nullptr}; int screen{0}; + int depth{0}; + Visual* visual{nullptr}; Atom wmDeleteMessage{0}; }; |