summaryrefslogtreecommitdiff
path: root/plugingui/nativewindow_x11.cc
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2020-12-29 16:09:43 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2020-12-29 16:39:54 +0100
commit645250e1cd8ce9bc1faea599df7a1b05836bfeb8 (patch)
treec6311afab3900d2dd2aecbfbe1ac348d0e48c428 /plugingui/nativewindow_x11.cc
parentdce64999d3325c5b55499d6ba657066efa48fbff (diff)
Split UI code into application/plugin UI and UI library.
Diffstat (limited to 'plugingui/nativewindow_x11.cc')
-rw-r--r--plugingui/nativewindow_x11.cc715
1 files changed, 0 insertions, 715 deletions
diff --git a/plugingui/nativewindow_x11.cc b/plugingui/nativewindow_x11.cc
deleted file mode 100644
index 33dde7b..0000000
--- a/plugingui/nativewindow_x11.cc
+++ /dev/null
@@ -1,715 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/***************************************************************************
- * nativewindow_x11.cc
- *
- * Fri Dec 28 18:45:57 CET 2012
- * Copyright 2012 Bent Bisballe Nyeng
- * deva@aasimon.org
- ****************************************************************************/
-
-/*
- * This file is part of DrumGizmo.
- *
- * DrumGizmo is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * DrumGizmo is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with DrumGizmo; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-#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 <cassert>
-
-#include <chrono>
-
-#include <hugin.hpp>
-
-#include "window.h"
-
-namespace GUI
-{
-
-#define _NET_WM_STATE_REMOVE 0 // remove/unset property
-#define _NET_WM_STATE_ADD 1 // add/set property
-
-void setWindowFront(Display *disp, ::Window wind, bool enable)
-{
- Atom wm_state, wm_state_above;
- XEvent event;
-
- if((wm_state = XInternAtom(disp, "_NET_WM_STATE", False)) == None)
- {
- return;
- }
-
- if((wm_state_above = XInternAtom(disp, "_NET_WM_STATE_ABOVE", False)) == None)
- {
- return;
- }
- //
- //window = the respective client window
- //message_type = _NET_WM_STATE
- //format = 32
- //data.l[0] = the action, as listed below
- //data.l[1] = first property to alter
- //data.l[2] = second property to alter
- //data.l[3] = source indication (0-unk,1-normal app,2-pager)
- //other data.l[] elements = 0
- //
-
- // sending a ClientMessage
- event.xclient.type = ClientMessage;
-
- // value unimportant in this case
- event.xclient.serial = 0;
-
- // coming from a SendEvent request, so True
- event.xclient.send_event = True;
-
- // the event originates from disp
- event.xclient.display = disp;
-
- // the window whose state will be modified
- event.xclient.window = wind;
-
- // the component Atom being modified in the window
- event.xclient.message_type = wm_state;
-
- // specifies that data.l will be used
- event.xclient.format = 32;
-
- // 0 is _NET_WM_STATE_REMOVE, 1 is _NET_WM_STATE_ADD
- event.xclient.data.l[0] =
- enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
-
- // the atom being added
- event.xclient.data.l[1] = wm_state_above;
-
- // unused
- event.xclient.data.l[2] = 0;
- event.xclient.data.l[3] = 0;
- event.xclient.data.l[4] = 0;
-
- // actually send the event
- XSendEvent(disp, DefaultRootWindow(disp), False,
- SubstructureRedirectMask | SubstructureNotifyMask, &event);
-}
-
-NativeWindowX11::NativeWindowX11(void* native_window, Window& window)
- : window(window)
-{
- display = XOpenDisplay(nullptr);
- if(display == nullptr)
- {
- ERR(X11, "XOpenDisplay failed");
- return;
- }
-
- screen = DefaultScreen(display);
- visual = DefaultVisual(display, screen);
- depth = DefaultDepth(display, screen);
-
- if(native_window)
- {
- parent_window = (::Window)native_window;
-
- // Track size changes on the parent window
- XSelectInput(display, parent_window, StructureNotifyMask);
- }
- else
- {
- parent_window = DefaultRootWindow(display);
- }
-
- // Create the window
- XSetWindowAttributes swa;
- swa.backing_store = Always;
- xwindow = XCreateWindow(display,
- parent_window,
- 0, 0, //window.x(), window.y(),
- 1, 1, //window.width(), window.height(),
- 0, // border
- CopyFromParent, // depth
- CopyFromParent, // class
- CopyFromParent, // visual
- 0,//CWBackingStore,
- &swa);
-
- long mask = (StructureNotifyMask |
- PointerMotionMask |
- ButtonPressMask |
- ButtonReleaseMask |
- KeyPressMask |
- KeyReleaseMask|
- ExposureMask |
- StructureNotifyMask |
- SubstructureNotifyMask |
- EnterWindowMask |
- LeaveWindowMask);
- XSelectInput(display, xwindow, mask);
-
- // Register the delete window message:
- wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false);
-
- Atom protocols[] = { wmDeleteMessage };
- XSetWMProtocols(display, xwindow, protocols,
- sizeof(protocols) / sizeof(*protocols));
-
- // Create a "Graphics Context"
- gc = XCreateGC(display, xwindow, 0, nullptr);
-}
-
-NativeWindowX11::~NativeWindowX11()
-{
- if(display == nullptr)
- {
- return;
- }
-
- deallocateShmImage();
-
- XFreeGC(display, gc);
-
- XDestroyWindow(display, xwindow);
- XCloseDisplay(display);
-}
-
-void NativeWindowX11::setFixedSize(std::size_t width, std::size_t height)
-{
- if(display == nullptr)
- {
- return;
- }
-
- resize(width, height);
-
- XSizeHints size_hints;
- memset(&size_hints, 0, sizeof(size_hints));
-
- size_hints.flags = PMinSize|PMaxSize;
- size_hints.min_width = size_hints.max_width = (int)width;
- size_hints.min_height = size_hints.max_height = (int)height;
-
- XSetNormalHints(display, xwindow, &size_hints);
-}
-
-void NativeWindowX11::setAlwaysOnTop(bool always_on_top)
-{
- setWindowFront(display, xwindow, always_on_top);
-}
-
-void NativeWindowX11::resize(std::size_t width, std::size_t height)
-{
- if(display == nullptr)
- {
- return;
- }
-
- XResizeWindow(display, xwindow, width, height);
-}
-
-std::pair<std::size_t, std::size_t> NativeWindowX11::getSize() const
-{
-// XWindowAttributes attributes;
-// XGetWindowAttributes(display, xwindow, &attributes);
-// return std::make_pair(attributes.width, attributes.height);
-
- ::Window root_window;
- int x, y;
- unsigned int width, height, border, depth;
-
- XGetGeometry(display, xwindow, &root_window,
- &x, &y,
- &width, &height, &border, &depth);
-
- return {width, height};
-}
-
-void NativeWindowX11::move(int x, int y)
-{
- if(display == nullptr)
- {
- return;
- }
-
- XMoveWindow(display, xwindow, x, y);
-}
-
-std::pair<int, int> NativeWindowX11::getPosition() const
-{
- ::Window root_window;
- ::Window child_window;
- int x, y;
- unsigned int width, height, border, depth;
-
- XGetGeometry(display, xwindow, &root_window,
- &x, &y,
- &width, &height, &border, &depth);
-
- XTranslateCoordinates(display, xwindow, root_window,
- 0, 0, &x, &y, &child_window);
-
- return std::make_pair(x, y);
-}
-
-void NativeWindowX11::show()
-{
- if(display == nullptr)
- {
- return;
- }
-
- XMapWindow(display, xwindow);
-}
-
-void NativeWindowX11::hide()
-{
- if(display == nullptr)
- {
- return;
- }
-
- XUnmapWindow(display, xwindow);
-}
-
-bool NativeWindowX11::visible() const
-{
- if(display == nullptr)
- {
- return false;
- }
-
- XWindowAttributes xwa;
- XGetWindowAttributes(display, xwindow, &xwa);
- return (xwa.map_state == IsViewable);
-}
-
-void NativeWindowX11::redraw(const Rect& dirty_rect)
-{
- if(display == nullptr)
- {
- return;
- }
-
- auto x1 = dirty_rect.x1;
- auto y1 = dirty_rect.y1;
- auto x2 = dirty_rect.x2;
- auto y2 = dirty_rect.y2;
-
- // Assert that we don't try to paint a backwards rect.
- assert(x1 <= x2);
- assert(y1 <= y2);
-
- updateImageFromBuffer(x1, y1, x2, y2);
-
- XShmPutImage(display, xwindow, gc, image, x1, y1, x1, y1,
- std::min((std::size_t)image->width, (x2 - x1)),
- std::min((std::size_t)image->height, (y2 - y1)), false);
- XFlush(display);
-}
-
-void NativeWindowX11::setCaption(const std::string &caption)
-{
- if(display == nullptr)
- {
- return;
- }
-
- XStoreName(display, xwindow, caption.c_str());
-}
-
-void NativeWindowX11::grabMouse(bool grab)
-{
- (void)grab;
- // Don't need to do anything on this platform...
-}
-
-EventQueue NativeWindowX11::getEvents()
-{
- while(XPending(display))
- {
- XEvent xEvent;
- XNextEvent(display, &xEvent);
- translateXMessage(xEvent);
- }
-
- EventQueue events;
- std::swap(events, event_queue);
- return events;
-}
-
-void* NativeWindowX11::getNativeWindowHandle() const
-{
- return (void*)xwindow;
-}
-
-Point NativeWindowX11::translateToScreen(const Point& point)
-{
- ::Window child_window;
- Point p;
- XTranslateCoordinates(display, xwindow, DefaultRootWindow(display),
- point.x, point.y, &p.x, &p.y, &child_window);
- return p;
-}
-
-void NativeWindowX11::translateXMessage(XEvent& xevent)
-{
- switch(xevent.type)
- {
- case MotionNotify:
- //DEBUG(x11, "MotionNotify");
- {
- auto mouseMoveEvent = std::make_shared<MouseMoveEvent>();
- mouseMoveEvent->x = xevent.xmotion.x;
- mouseMoveEvent->y = xevent.xmotion.y;
- event_queue.push_back(mouseMoveEvent);
- }
- break;
-
- case Expose:
- //DEBUG(x11, "Expose");
- if(xevent.xexpose.count == 0)
- {
- auto repaintEvent = std::make_shared<RepaintEvent>();
- repaintEvent->x = xevent.xexpose.x;
- repaintEvent->y = xevent.xexpose.y;
- repaintEvent->width = xevent.xexpose.width;
- repaintEvent->height = xevent.xexpose.height;
- event_queue.push_back(repaintEvent);
-
- if(image)
- {
- // Redraw the entire window.
- Rect rect{0, 0, window.wpixbuf.width, window.wpixbuf.height};
- redraw(rect);
- }
- }
- break;
-
- case ConfigureNotify:
- //DEBUG(x11, "ConfigureNotify");
-
- // The parent window size changed, reflect the new size in our own window.
- if(xevent.xconfigure.window == parent_window)
- {
- resize(xevent.xconfigure.width, xevent.xconfigure.height);
- return;
- }
-
- {
- if((window.width() != (std::size_t)xevent.xconfigure.width) ||
- (window.height() != (std::size_t)xevent.xconfigure.height))
- {
- auto resizeEvent = std::make_shared<ResizeEvent>();
- resizeEvent->width = xevent.xconfigure.width;
- resizeEvent->height = xevent.xconfigure.height;
- event_queue.push_back(resizeEvent);
- }
-
- if((window.x() != xevent.xconfigure.x) ||
- (window.y() != xevent.xconfigure.y))
- {
- auto moveEvent = std::make_shared<MoveEvent>();
- moveEvent->x = xevent.xconfigure.x;
- moveEvent->y = xevent.xconfigure.y;
- event_queue.push_back(moveEvent);
- }
- }
- break;
-
- case ButtonPress:
- case ButtonRelease:
- //DEBUG(x11, "ButtonPress");
- {
- if((xevent.xbutton.button == 4) || (xevent.xbutton.button == 5))
- {
- if(xevent.type == ButtonPress)
- {
- int scroll = 1;
- auto scrollEvent = std::make_shared<ScrollEvent>();
- scrollEvent->x = xevent.xbutton.x;
- scrollEvent->y = xevent.xbutton.y;
- scrollEvent->delta = scroll * ((xevent.xbutton.button == 4) ? -1 : 1);
- event_queue.push_back(scrollEvent);
- }
- }
- else if ((xevent.xbutton.button == 6) || (xevent.xbutton.button == 7))
- {
- // Horizontal scrolling case
- // FIXME Introduce horizontal scrolling event to handle this.
- }
- else
- {
- auto buttonEvent = std::make_shared<ButtonEvent>();
- buttonEvent->x = xevent.xbutton.x;
- buttonEvent->y = xevent.xbutton.y;
- switch(xevent.xbutton.button) {
- case 1:
- buttonEvent->button = MouseButton::left;
- break;
- case 2:
- buttonEvent->button = MouseButton::middle;
- break;
- case 3:
- buttonEvent->button = MouseButton::right;
- break;
- default:
- WARN(X11, "Unknown button %d, setting to MouseButton::left\n",
- xevent.xbutton.button);
- buttonEvent->button = MouseButton::left;
- break;
- }
-
- buttonEvent->direction =
- (xevent.type == ButtonPress) ?
- Direction::down : Direction::up;
-
- // This is a fix for hosts (e.g. those using JUCE) that set the
- // event time to '0'.
- if(xevent.xbutton.time == 0)
- {
- auto now = std::chrono::system_clock::now().time_since_epoch();
- xevent.xbutton.time =
- std::chrono::duration_cast<std::chrono::milliseconds>(now).count();
- }
-
- buttonEvent->doubleClick =
- (xevent.type == ButtonPress) &&
- ((xevent.xbutton.time - last_click) < 200);
-
- if(xevent.type == ButtonPress)
- {
- last_click = xevent.xbutton.time;
- }
- event_queue.push_back(buttonEvent);
- }
- }
- break;
-
- case KeyPress:
- case KeyRelease:
- //DEBUG(x11, "KeyPress");
- {
- auto keyEvent = std::make_shared<KeyEvent>();
-
- switch(xevent.xkey.keycode) {
- case 113: keyEvent->keycode = Key::left; break;
- case 114: keyEvent->keycode = Key::right; break;
- case 111: keyEvent->keycode = Key::up; break;
- case 116: keyEvent->keycode = Key::down; break;
- case 119: keyEvent->keycode = Key::deleteKey; break;
- case 22: keyEvent->keycode = Key::backspace; break;
- case 110: keyEvent->keycode = Key::home; break;
- case 115: keyEvent->keycode = Key::end; break;
- case 117: keyEvent->keycode = Key::pageDown; break;
- case 112: keyEvent->keycode = Key::pageUp; break;
- case 36: keyEvent->keycode = Key::enter; break;
- default: keyEvent->keycode = Key::unknown; break;
- }
-
- char stringBuffer[1024];
- int size = XLookupString(&xevent.xkey, stringBuffer,
- sizeof(stringBuffer), nullptr, nullptr);
- if(size && keyEvent->keycode == Key::unknown)
- {
- keyEvent->keycode = Key::character;
- }
-
- keyEvent->text.append(stringBuffer, size);
-
- keyEvent->direction =
- (xevent.type == KeyPress) ? Direction::down : Direction::up;
-
- event_queue.push_back(keyEvent);
- }
- break;
-
- case ClientMessage:
- //DEBUG(x11, "ClientMessage");
- if(((unsigned int)xevent.xclient.data.l[0] == wmDeleteMessage))
- {
- auto closeEvent = std::make_shared<CloseEvent>();
- event_queue.push_back(closeEvent);
- }
- break;
-
- case EnterNotify:
- //DEBUG(x11, "EnterNotify");
- {
- auto enterEvent = std::make_shared<MouseEnterEvent>();
- enterEvent->x = xevent.xcrossing.x;
- enterEvent->y = xevent.xcrossing.y;
- event_queue.push_back(enterEvent);
- }
- break;
-
- case LeaveNotify:
- //DEBUG(x11, "LeaveNotify");
- {
- auto leaveEvent = std::make_shared<MouseLeaveEvent>();
- leaveEvent->x = xevent.xcrossing.x;
- leaveEvent->y = xevent.xcrossing.y;
- event_queue.push_back(leaveEvent);
- }
- break;
-
- case MapNotify:
- case MappingNotify:
- //DEBUG(x11, "EnterNotify");
- // There's nothing to do here atm.
- break;
-
- default:
- WARN(X11, "Unhandled xevent.type: %d\n", xevent.type);
- break;
- }
-}
-
-void NativeWindowX11::allocateShmImage(std::size_t width, std::size_t height)
-{
- DEBUG(x11, "(Re)alloc XShmImage (%d, %d)", (int)width, (int)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<long 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(std::size_t x1, std::size_t y1,
- std::size_t x2, std::size_t y2)
-{
- //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);
- x1 = 0;
- y1 = 0;
- x2 = width;
- y2 = 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;
- for(std::size_t y = y1; y < y2; ++y)
- {
- for(std::size_t x = x1; x < x2; ++x)
- {
- const std::size_t pin = y * width + x;
- const std::size_t pout = y * stride + x;
- const std::uint8_t red = pixel_buffer[pin * 3];
- const std::uint8_t green = pixel_buffer[pin * 3 + 1];
- const std::uint8_t blue = pixel_buffer[pin * 3 + 2];
- shm_addr[pout] = (red << 16) | (green << 8) | blue;
- }
- }
- }
- else if(depth >= 15) // RGB 565 format
- {
- std::uint16_t* shm_addr = (std::uint16_t*)shm_info.shmaddr;
-
- for(std::size_t y = y1; y < y2; ++y)
- {
- for(std::size_t x = x1; x < x2; ++x)
- {
- const std::size_t pin = y * width + x;
- const std::size_t pout = y * stride + x;
- const std::uint8_t red = pixel_buffer[pin * 3];
- const std::uint8_t green = pixel_buffer[pin * 3 + 1];
- const std::uint8_t blue = pixel_buffer[pin * 3 + 2];
- shm_addr[pout] = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
- }
- }
- }
-}
-
-} // GUI::