/* -*- 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 General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU 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" #include #include #include #include "window.h" namespace GUI { NativeWindowX11::NativeWindowX11(Window* window) : NativeWindow() , buffer(nullptr) , window(window) { display = XOpenDisplay(nullptr); if(display == nullptr) { ERR(X11, "XOpenDisplay failed"); return; } screen = DefaultScreen(display); // Get some colors int blackColor = BlackPixel(display, screen); ::Window rootWindow = DefaultRootWindow(display); // Create the window unsigned long border = 0; xwindow = XCreateSimpleWindow(display, rootWindow, window->x(), window->y(), window->width(), window->height(), border, blackColor, blackColor); long mask = (StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask| ExposureMask | StructureNotifyMask | SubstructureNotifyMask); XSelectInput(display, xwindow, mask); // Register the delete window message: wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false); Atom protocols[] = { wmDeleteMessage }; int count = sizeof(protocols)/sizeof(Atom); XSetWMProtocols(display, xwindow, protocols, count); // "Map" the window (that is, make it appear on the screen) XMapWindow(display, xwindow); // Create a "Graphics Context" gc = XCreateGC(display, xwindow, 0, nullptr); } NativeWindowX11::~NativeWindowX11() { if(display == nullptr) { return; } XDestroyWindow(display, xwindow); XCloseDisplay(display); } void NativeWindowX11::setFixedSize(int width, int height) { if(display == nullptr) { return; } resize(width, height); XSizeHints* size_hints; size_hints = XAllocSizeHints(); if(size_hints == nullptr) { ERR(X11,"XMallocSizeHints() failed"); return; } size_hints->flags = USPosition | PMinSize | PMaxSize; size_hints->min_width = size_hints->max_width = width; size_hints->min_height = size_hints->max_height = height; //size_hints->min_aspect.x = (float)window->width()/(float)window->height(); //size_hints->max_aspect.x = (float)window->width()/(float)window->height(); //size_hints->min_aspect.y = (float)window->width()/(float)window->height(); //size_hints->max_aspect.y = size_hints->min_aspect.y; XSetWMNormalHints(display, xwindow, size_hints); } void NativeWindowX11::resize(int width, int height) { if(display == nullptr) { return; } XResizeWindow(display, xwindow, width, height); } void NativeWindowX11::move(int x, int y) { if(display == nullptr) { return; } XMoveWindow(display, xwindow, x, y); } void NativeWindowX11::show() { if(display == nullptr) { return; } XMapWindow(display, xwindow); } void NativeWindowX11::hide() { if(display == nullptr) { return; } 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); } void NativeWindowX11::redraw() { if(display == nullptr) { return; } if(buffer == nullptr) { window->updateBuffer(); } XPutImage(display, xwindow, gc, buffer, 0, 0, 0, 0, window->width(), window->height()); 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... } bool NativeWindowX11::hasEvent() { if(display == nullptr) { return false; } return XPending(display); } Event* NativeWindowX11::getNextEvent() { if(display == nullptr) { return nullptr; } Event* event = nullptr; XEvent xevent; XNextEvent(display, &xevent); if(xevent.type == MotionNotify) { while(true) // Hack to make sure only the last event is played. { if(!hasEvent()) { break; } XEvent peekXEvent; XPeekEvent(display, &peekXEvent); if(peekXEvent.type != MotionNotify) { break; } XNextEvent(display, &xevent); } auto mouseMoveEvent = new MouseMoveEvent(); mouseMoveEvent->window_id = xevent.xmotion.window; mouseMoveEvent->x = xevent.xmotion.x; mouseMoveEvent->y = xevent.xmotion.y; event = mouseMoveEvent; } if(xevent.type == Expose && xevent.xexpose.count == 0) { auto repaintEvent = new RepaintEvent(); repaintEvent->window_id = xevent.xexpose.window; repaintEvent->x = xevent.xexpose.x; repaintEvent->y = xevent.xexpose.y; repaintEvent->width = xevent.xexpose.width; repaintEvent->height = xevent.xexpose.height; event = repaintEvent; } if(xevent.type == ConfigureNotify) { auto resizeEvent = new ResizeEvent(); resizeEvent->window_id = xevent.xconfigure.window; //resizeEvent->x = xevent.xconfigure.x; //resizeEvent->y = xevent.xconfigure.y; resizeEvent->width = xevent.xconfigure.width; resizeEvent->height = xevent.xconfigure.height; event = resizeEvent; } if(xevent.type == ButtonPress || xevent.type == ButtonRelease) { if((xevent.xbutton.button == 4) || (xevent.xbutton.button == 5)) { int scroll = 1; while(true) // Hack to make sure only the last event is played. { if(!hasEvent()) { break; } XEvent peekXEvent; XPeekEvent(display, &peekXEvent); if((peekXEvent.type != ButtonPress) && (peekXEvent.type != ButtonRelease)) { break; } scroll += 1; XNextEvent(display, &xevent); } auto scrollEvent = new ScrollEvent(); scrollEvent->window_id = xevent.xbutton.window; scrollEvent->x = xevent.xbutton.x; scrollEvent->y = xevent.xbutton.y; scrollEvent->delta = scroll * ((xevent.xbutton.button == 4) ? -1 : 1); event = scrollEvent; } else { auto buttonEvent = new ButtonEvent(); buttonEvent->window_id = xevent.xbutton.window; buttonEvent->x = xevent.xbutton.x; buttonEvent->y = xevent.xbutton.y; switch(xevent.xbutton.button) { case 1: buttonEvent->button = ButtonEvent::Left; break; case 2: buttonEvent->button = ButtonEvent::Middle; break; case 3: buttonEvent->button = ButtonEvent::Right; break; default: WARN(X11, "Unknown button %d, setting to Left\n", xevent.xbutton.button); buttonEvent->button = ButtonEvent::Left; break; } buttonEvent->direction = (xevent.type == ButtonPress) ? ButtonEvent::Down : ButtonEvent::Up; buttonEvent->doubleclick = (xevent.type == ButtonPress) && ((xevent.xbutton.time - last_click) < 200); if(xevent.type == ButtonPress) { last_click = xevent.xbutton.time; } event = buttonEvent; } } if(xevent.type == KeyPress || xevent.type == KeyRelease) { //printf("key: %d\n", xevent.xkey.keycode); auto keyEvent = new KeyEvent(); keyEvent->window_id = xevent.xkey.window; switch(xevent.xkey.keycode) { case 113: keyEvent->keycode = KeyEvent::KEY_LEFT; break; case 114: keyEvent->keycode = KeyEvent::KEY_RIGHT; break; case 111: keyEvent->keycode = KeyEvent::KEY_UP; break; case 116: keyEvent->keycode = KeyEvent::KEY_DOWN; break; case 119: keyEvent->keycode = KeyEvent::KEY_DELETE; break; case 22: keyEvent->keycode = KeyEvent::KEY_BACKSPACE; break; case 110: keyEvent->keycode = KeyEvent::KEY_HOME; break; case 115: keyEvent->keycode = KeyEvent::KEY_END; break; case 117: keyEvent->keycode = KeyEvent::KEY_PGDOWN; break; case 112: keyEvent->keycode = KeyEvent::KEY_PGUP; break; case 36: keyEvent->keycode = KeyEvent::KEY_ENTER; break; default: keyEvent->keycode = KeyEvent::KEY_UNKNOWN; break; } char stringBuffer[1024]; int size = XLookupString(&xevent.xkey, stringBuffer, sizeof(stringBuffer), nullptr, nullptr); if(size && keyEvent->keycode == KeyEvent::KEY_UNKNOWN) { keyEvent->keycode = KeyEvent::KEY_CHARACTER; } keyEvent->text.append(stringBuffer, size); keyEvent->direction = (xevent.type == KeyPress) ? KeyEvent::Down : KeyEvent::Up; event = keyEvent; } if((xevent.type == ClientMessage) && ((unsigned int)xevent.xclient.data.l[0] == wmDeleteMessage)) { auto closeEvent = new CloseEvent(); event = closeEvent; } return event; } } // GUI::