/* -*- 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" #ifdef X11 #include #include #include "window.h" GUI::NativeWindowX11::NativeWindowX11(GUI::Window *window) : GUI::NativeWindow() { display = XOpenDisplay(NULL); this->window = window; buffer = NULL; // Get some colors int blackColor = BlackPixel(display, DefaultScreen(display)); ::Window w = DefaultRootWindow(display); // Create the window xwindow = XCreateSimpleWindow(display, w, window->x(), window->y(), window->width(), window->height(), 0, blackColor, blackColor); XSelectInput(display, xwindow, StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask| ExposureMask | StructureNotifyMask | SubstructureNotifyMask); // register interest in the delete window message wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false); XSetWMProtocols(display, xwindow, &wmDeleteMessage, 1); // "Map" the window (that is, make it appear on the screen) XMapWindow(display, xwindow); // Create a "Graphics Context" gc = XCreateGC(display, xwindow, 0, NULL); } GUI::NativeWindowX11::~NativeWindowX11() { XDestroyWindow(display, xwindow); //widgets.erase(window); XCloseDisplay(display); } void GUI::NativeWindowX11::setFixedSize(int width, int height) { resize(width, height); XSizeHints *size_hints; size_hints = XAllocSizeHints(); if(size_hints == NULL) { //fprintf(stderr,"XMallocSizeHints() failed\n"); //exit(1); 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 = window->width()/window->height(); size_hints->max_aspect.x = window->width()/window->height(); size_hints->min_aspect.y = window->width()/window->height(); size_hints->max_aspect.y = size_hints->min_aspect.y; */ XSetWMNormalHints(display, xwindow, size_hints); } void GUI::NativeWindowX11::resize(int width, int height) { XResizeWindow(display, xwindow, width, height); } void GUI::NativeWindowX11::move(int x, int y) { XMoveWindow(display, xwindow, x, y); } void GUI::NativeWindowX11::show() { XMapWindow(display, xwindow); } void GUI::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; } } static XImage *create_image_from_buffer(Display *dis, int screen, unsigned char *buf, int width, int height) { int depth; XImage *img = NULL; Visual *vis; double rRatio; double gRatio; double bRatio; int outIndex = 0; int i; int numBufBytes = (3 * (width * height)); depth = DefaultDepth(dis, screen); vis = DefaultVisual(dis, 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 (dis, 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(dis, 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 NULL; } 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 GUI::NativeWindowX11::handleBuffer() { if(buffer) XDestroyImage(buffer); buffer = create_image_from_buffer(display, DefaultScreen(display), window->wpixbuf.buf, window->wpixbuf.width, window->wpixbuf.height); } void GUI::NativeWindowX11::redraw() { // http://stackoverflow.com/questions/6384987/load-image-onto-a-window-using-xlib if(buffer == NULL) window->updateBuffer(); XPutImage(display, xwindow, gc, buffer, 0, 0, 0, 0, window->width(), window->height()); XFlush(display); } void GUI::NativeWindowX11::setCaption(const std::string &caption) { XStoreName(display, xwindow, caption.c_str()); } void GUI::NativeWindowX11::grabMouse(bool grab) { (void)grab; // Don't need to do anything on this platoform... } bool GUI::NativeWindowX11::hasEvent() { return XPending(display); } GUI::Event *GUI::NativeWindowX11::getNextEvent() { Event *event = NULL; XEvent xe; XNextEvent(display, &xe); if(xe.type == MotionNotify) { while(true) { // Hack to make sure only the last event is played. if(!hasEvent()) break; XEvent nxe; XPeekEvent(display, &nxe); if(nxe.type != MotionNotify) break; XNextEvent(display, &xe); } MouseMoveEvent *e = new MouseMoveEvent(); e->window_id = xe.xmotion.window; e->x = xe.xmotion.x; e->y = xe.xmotion.y; event = e; } if(xe.type == Expose && xe.xexpose.count == 0) { RepaintEvent *e = new RepaintEvent(); e->window_id = xe.xexpose.window; e->x = xe.xexpose.x; e->y = xe.xexpose.y; e->width = xe.xexpose.width; e->height = xe.xexpose.height; event = e; } if(xe.type == ConfigureNotify) { ResizeEvent *e = new ResizeEvent(); e->window_id = xe.xconfigure.window; // e->x = xe.xconfigure.x; // e->y = xe.xconfigure.y; e->width = xe.xconfigure.width; e->height = xe.xconfigure.height; event = e; } if(xe.type == ButtonPress || xe.type == ButtonRelease) { if(xe.xbutton.button == 4 || xe.xbutton.button == 5) { int scroll = 1; while(true) { // Hack to make sure only the last event is played. if(!hasEvent()) break; XEvent nxe; XPeekEvent(display, &nxe); if(nxe.type != ButtonPress && nxe.type != ButtonRelease) break; scroll += 1; XNextEvent(display, &xe); } ScrollEvent *e = new ScrollEvent(); e->window_id = xe.xbutton.window; e->x = xe.xbutton.x; e->y = xe.xbutton.y; e->delta = scroll * (xe.xbutton.button==4?-1:1); event = e; } else { ButtonEvent *e = new ButtonEvent(); e->window_id = xe.xbutton.window; e->x = xe.xbutton.x; e->y = xe.xbutton.y; e->button = 0; e->direction = xe.type == ButtonPress?1:-1; e->doubleclick = xe.type == ButtonPress && (xe.xbutton.time - last_click) < 200; if(xe.type == ButtonPress) last_click = xe.xbutton.time; event = e; } } if(xe.type == KeyPress || xe.type == KeyRelease) { //printf("key: %d\n", xe.xkey.keycode); KeyEvent *e = new KeyEvent(); e->window_id = xe.xkey.window; switch(xe.xkey.keycode) { case 113: e->keycode = KeyEvent::KEY_LEFT; break; case 114: e->keycode = KeyEvent::KEY_RIGHT; break; case 111: e->keycode = KeyEvent::KEY_UP; break; case 116: e->keycode = KeyEvent::KEY_DOWN; break; case 119: e->keycode = KeyEvent::KEY_DELETE; break; case 22: e->keycode = KeyEvent::KEY_BACKSPACE; break; case 110: e->keycode = KeyEvent::KEY_HOME; break; case 115: e->keycode = KeyEvent::KEY_END; break; case 117: e->keycode = KeyEvent::KEY_PGDOWN; break; case 112: e->keycode = KeyEvent::KEY_PGUP; break; case 36: e->keycode = KeyEvent::KEY_ENTER; break; default: e->keycode = KeyEvent::KEY_UNKNOWN; break; } char buf[1024]; int sz = XLookupString(&xe.xkey, buf, sizeof(buf), NULL, NULL); if(sz && e->keycode == KeyEvent::KEY_UNKNOWN) { e->keycode = KeyEvent::KEY_CHARACTER; } e->text.append(buf, sz); e->direction = xe.type == KeyPress?1:-1; event = e; } if(xe.type == ClientMessage && (unsigned int)xe.xclient.data.l[0] == wmDeleteMessage) { CloseEvent *e = new CloseEvent(); event = e; } return event; } #endif/*X11*/