| |
| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| #include <X11/Xlib.h> |
| #include <X11/Xatom.h> |
| #include <X11/keysym.h> |
| #include <GL/glx.h> |
| #include <GL/gl.h> |
| #include <GL/glu.h> |
| |
| #include "SkWindow.h" |
| |
| #include "SkBitmap.h" |
| #include "SkCanvas.h" |
| #include "SkColor.h" |
| #include "SkEvent.h" |
| #include "SkKey.h" |
| #include "SkWindow.h" |
| #include "XkeysToSkKeys.h" |
| extern "C" { |
| #include "keysym2ucs.h" |
| } |
| |
| const int WIDTH = 500; |
| const int HEIGHT = 500; |
| |
| // Determine which events to listen for. |
| const long EVENT_MASK = StructureNotifyMask|ButtonPressMask|ButtonReleaseMask |
| |ExposureMask|PointerMotionMask|KeyPressMask|KeyReleaseMask; |
| |
| SkOSWindow::SkOSWindow(void* unused) : INHERITED(), fGLAttached(false), fVi(0) |
| { |
| fUnixWindow.fDisplay = XOpenDisplay(NULL); |
| Display* dsp = fUnixWindow.fDisplay; |
| if (dsp) { |
| // Attempt to create a window that supports GL |
| GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, |
| GLX_STENCIL_SIZE, 8, None }; |
| fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att); |
| if (fVi) { |
| Colormap colorMap = XCreateColormap(dsp, RootWindow(dsp, fVi->screen), |
| fVi->visual, AllocNone); |
| XSetWindowAttributes swa; |
| swa.colormap = colorMap; |
| swa.event_mask = EVENT_MASK; |
| fUnixWindow.fWin = XCreateWindow(dsp, RootWindow(dsp, fVi->screen), |
| 0, 0, WIDTH, HEIGHT, 0, fVi->depth, |
| InputOutput, fVi->visual, CWEventMask | CWColormap, &swa); |
| |
| } else { |
| // Create a simple window instead. We will not be able to |
| // show GL |
| fUnixWindow.fWin = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), |
| 0, 0, WIDTH, HEIGHT, 0, 0, 0); |
| } |
| mapWindowAndWait(); |
| fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, NULL); |
| } |
| this->resize(WIDTH, HEIGHT); |
| fUnixWindow.fGLCreated = false; |
| } |
| |
| SkOSWindow::~SkOSWindow() |
| { |
| if (fUnixWindow.fDisplay) { |
| if (fGLAttached) |
| glXMakeCurrent(fUnixWindow.fDisplay, None, NULL); |
| XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc); |
| if (fUnixWindow.fGLCreated) |
| glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext); |
| XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin); |
| XCloseDisplay(fUnixWindow.fDisplay); |
| fUnixWindow.fDisplay = 0; |
| } |
| } |
| |
| void SkOSWindow::post_linuxevent() |
| { |
| // Put an event in the X queue to fire an SkEvent. |
| if (!fUnixWindow.fDisplay) return; |
| long event_mask = NoEventMask; |
| XClientMessageEvent event; |
| event.type = ClientMessage; |
| Atom myAtom(0); |
| event.message_type = myAtom; |
| event.format = 32; |
| event.data.l[0] = 0; |
| XSendEvent(fUnixWindow.fDisplay, fUnixWindow.fWin, false, 0, |
| (XEvent*) &event); |
| XFlush(fUnixWindow.fDisplay); |
| } |
| |
| void SkOSWindow::loop() |
| { |
| Display* dsp = fUnixWindow.fDisplay; |
| XSelectInput(dsp, fUnixWindow.fWin, EVENT_MASK); |
| |
| bool loop = true; |
| XEvent evt; |
| while (loop) { |
| XNextEvent(dsp, &evt); |
| switch (evt.type) { |
| case Expose: |
| if (evt.xexpose.count == 0) |
| this->inval(NULL); |
| break; |
| case ConfigureNotify: |
| this->resize(evt.xconfigure.width, evt.xconfigure.height); |
| break; |
| case ButtonPress: |
| if (evt.xbutton.button == Button1) |
| this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kDown_State); |
| break; |
| case ButtonRelease: |
| if (evt.xbutton.button == Button1) |
| this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kUp_State); |
| break; |
| case MotionNotify: |
| this->handleClick(evt.xmotion.x, evt.xmotion.y, SkView::Click::kMoved_State); |
| break; |
| case KeyPress: |
| { |
| KeySym keysym = XKeycodeToKeysym(dsp, evt.xkey.keycode, 0); |
| //SkDebugf("pressed key %i!\n\tKeySym:%i\n", evt.xkey.keycode, XKeycodeToKeysym(dsp, evt.xkey.keycode, 0)); |
| if (keysym == XK_Escape) { |
| loop = false; |
| break; |
| } |
| this->handleKey(XKeyToSkKey(keysym)); |
| long uni = keysym2ucs(keysym); |
| if (uni != -1) { |
| this->handleChar((SkUnichar) uni); |
| } |
| break; |
| } |
| case KeyRelease: |
| //SkDebugf("released key %i\n", evt.xkey.keycode); |
| this->handleKeyUp(XKeyToSkKey(XKeycodeToKeysym(dsp, evt.xkey.keycode, 0))); |
| break; |
| case ClientMessage: |
| if (SkEvent::ProcessEvent()) { |
| this->post_linuxevent(); |
| } |
| break; |
| default: |
| // Do nothing for other events |
| break; |
| } |
| } |
| } |
| |
| void SkOSWindow::mapWindowAndWait() |
| { |
| Display* dsp = fUnixWindow.fDisplay; |
| Window win = fUnixWindow.fWin; |
| XMapWindow(dsp, win); |
| |
| long eventMask = StructureNotifyMask; |
| XSelectInput(dsp, win, eventMask); |
| |
| // Wait until screen is ready. |
| XEvent evt; |
| do { |
| XNextEvent(dsp, &evt); |
| } while(evt.type != MapNotify); |
| |
| } |
| |
| bool SkOSWindow::attachGL() |
| { |
| if (fGLAttached) return true; |
| Display* dsp = fUnixWindow.fDisplay; |
| if (!dsp || !fVi) return false; |
| |
| if (!fUnixWindow.fGLCreated) { |
| fUnixWindow.fGLContext = glXCreateContext(dsp, fVi, NULL, GL_TRUE); |
| fUnixWindow.fGLCreated = true; |
| glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext); |
| glViewport(0, 0, SkScalarRound(this->width()), SkScalarRound(this->height())); |
| glClearColor(0, 0, 0, 0); |
| glClearStencil(0); |
| glStencilMask(0xffffffff); |
| glDisable(GL_SCISSOR_TEST); |
| glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| } |
| else |
| glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext); |
| fGLAttached = true; |
| |
| return true; |
| } |
| |
| void SkOSWindow::detachGL() |
| { |
| if (!fUnixWindow.fDisplay || !fGLAttached) return; |
| fGLAttached = false; |
| // Returns back to normal drawing. |
| glXMakeCurrent(fUnixWindow.fDisplay, None, NULL); |
| // Ensure that we redraw when switching back to raster. |
| this->inval(NULL); |
| } |
| |
| void SkOSWindow::presentGL() |
| { |
| if (fUnixWindow.fDisplay && fGLAttached) { |
| glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin); |
| } |
| } |
| |
| void SkOSWindow::onSetTitle(const char title[]) |
| { |
| if (!fUnixWindow.fDisplay) return; |
| XTextProperty textProp; |
| textProp.value = (unsigned char*)title; |
| textProp.format = 8; |
| textProp.nitems = strlen((char*)textProp.value); |
| textProp.encoding = XA_STRING; |
| XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp); |
| } |
| |
| void SkOSWindow::onHandleInval(const SkIRect&) { |
| (new SkEvent("inval-imageview", this->getSinkID()))->post(); |
| } |
| |
| bool SkOSWindow::onEvent(const SkEvent& evt) |
| { |
| if (evt.isType("inval-imageview")) { |
| update(NULL); |
| if (!fGLAttached) |
| doPaint(); |
| return true; |
| } |
| return INHERITED::onEvent(evt); |
| } |
| |
| static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap) |
| { |
| sk_bzero(&image, sizeof(image)); |
| |
| int bitsPerPixel = bitmap.bytesPerPixel() * 8; |
| image.width = bitmap.width(); |
| image.height = bitmap.height(); |
| image.format = ZPixmap; |
| image.data = (char*) bitmap.getPixels(); |
| image.byte_order = LSBFirst; |
| image.bitmap_unit = bitsPerPixel; |
| image.bitmap_bit_order = LSBFirst; |
| image.bitmap_pad = bitsPerPixel; |
| image.depth = 24; |
| image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * bitmap.bytesPerPixel(); |
| image.bits_per_pixel = bitsPerPixel; |
| return XInitImage(&image); |
| } |
| |
| void SkOSWindow::doPaint() { |
| if (!fUnixWindow.fDisplay) return; |
| // Draw the bitmap to the screen. |
| const SkBitmap& bitmap = getBitmap(); |
| int width = bitmap.width(); |
| int height = bitmap.height(); |
| |
| XImage image; |
| if (!convertBitmapToXImage(image, bitmap)) return; |
| |
| XPutImage(fUnixWindow.fDisplay, fUnixWindow.fWin, fUnixWindow.fGc, &image, 0, 0, 0, 0, width, height); |
| } |
| |
| bool SkOSWindow::onHandleChar(SkUnichar) |
| { |
| return false; |
| } |
| |
| bool SkOSWindow::onHandleKey(SkKey key) |
| { |
| return false; |
| } |
| |
| bool SkOSWindow::onHandleKeyUp(SkKey key) |
| { |
| return false; |
| } |