1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include <X11/Xlib.h> 9#include <X11/Xatom.h> 10#include <X11/keysym.h> 11#include <GL/glx.h> 12#include <GL/gl.h> 13#include <GL/glu.h> 14 15#include "SkWindow.h" 16 17#include "SkBitmap.h" 18#include "SkCanvas.h" 19#include "SkColor.h" 20#include "SkEvent.h" 21#include "SkKey.h" 22#include "SkWindow.h" 23#include "XkeysToSkKeys.h" 24extern "C" { 25 #include "keysym2ucs.h" 26} 27 28const int WIDTH = 500; 29const int HEIGHT = 500; 30 31// Determine which events to listen for. 32const long EVENT_MASK = StructureNotifyMask|ButtonPressMask|ButtonReleaseMask 33 |ExposureMask|PointerMotionMask|KeyPressMask|KeyReleaseMask; 34 35SkOSWindow::SkOSWindow(void* unused) : INHERITED(), fGLAttached(false), fVi(0) 36{ 37 fUnixWindow.fDisplay = XOpenDisplay(NULL); 38 Display* dsp = fUnixWindow.fDisplay; 39 if (dsp) { 40 // Attempt to create a window that supports GL 41 GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, 42 GLX_STENCIL_SIZE, 8, None }; 43 fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att); 44 if (fVi) { 45 Colormap colorMap = XCreateColormap(dsp, RootWindow(dsp, fVi->screen), 46 fVi->visual, AllocNone); 47 XSetWindowAttributes swa; 48 swa.colormap = colorMap; 49 swa.event_mask = EVENT_MASK; 50 fUnixWindow.fWin = XCreateWindow(dsp, RootWindow(dsp, fVi->screen), 51 0, 0, WIDTH, HEIGHT, 0, fVi->depth, 52 InputOutput, fVi->visual, CWEventMask | CWColormap, &swa); 53 54 } else { 55 // Create a simple window instead. We will not be able to 56 // show GL 57 fUnixWindow.fWin = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp), 58 0, 0, WIDTH, HEIGHT, 0, 0, 0); 59 } 60 mapWindowAndWait(); 61 fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, NULL); 62 } 63 this->resize(WIDTH, HEIGHT); 64 fUnixWindow.fGLCreated = false; 65} 66 67SkOSWindow::~SkOSWindow() 68{ 69 if (fUnixWindow.fDisplay) { 70 if (fGLAttached) 71 glXMakeCurrent(fUnixWindow.fDisplay, None, NULL); 72 XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc); 73 if (fUnixWindow.fGLCreated) 74 glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext); 75 XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin); 76 XCloseDisplay(fUnixWindow.fDisplay); 77 fUnixWindow.fDisplay = 0; 78 } 79} 80 81void SkOSWindow::post_linuxevent() 82{ 83 // Put an event in the X queue to fire an SkEvent. 84 if (!fUnixWindow.fDisplay) return; 85 long event_mask = NoEventMask; 86 XClientMessageEvent event; 87 event.type = ClientMessage; 88 Atom myAtom(0); 89 event.message_type = myAtom; 90 event.format = 32; 91 event.data.l[0] = 0; 92 XSendEvent(fUnixWindow.fDisplay, fUnixWindow.fWin, false, 0, 93 (XEvent*) &event); 94 XFlush(fUnixWindow.fDisplay); 95} 96 97void SkOSWindow::loop() 98{ 99 Display* dsp = fUnixWindow.fDisplay; 100 XSelectInput(dsp, fUnixWindow.fWin, EVENT_MASK); 101 102 bool loop = true; 103 XEvent evt; 104 while (loop) { 105 XNextEvent(dsp, &evt); 106 switch (evt.type) { 107 case Expose: 108 if (evt.xexpose.count == 0) 109 this->inval(NULL); 110 break; 111 case ConfigureNotify: 112 this->resize(evt.xconfigure.width, evt.xconfigure.height); 113 break; 114 case ButtonPress: 115 if (evt.xbutton.button == Button1) 116 this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kDown_State); 117 break; 118 case ButtonRelease: 119 if (evt.xbutton.button == Button1) 120 this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kUp_State); 121 break; 122 case MotionNotify: 123 this->handleClick(evt.xmotion.x, evt.xmotion.y, SkView::Click::kMoved_State); 124 break; 125 case KeyPress: 126 { 127 KeySym keysym = XKeycodeToKeysym(dsp, evt.xkey.keycode, 0); 128 //SkDebugf("pressed key %i!\n\tKeySym:%i\n", evt.xkey.keycode, XKeycodeToKeysym(dsp, evt.xkey.keycode, 0)); 129 if (keysym == XK_Escape) { 130 loop = false; 131 break; 132 } 133 this->handleKey(XKeyToSkKey(keysym)); 134 long uni = keysym2ucs(keysym); 135 if (uni != -1) { 136 this->handleChar((SkUnichar) uni); 137 } 138 break; 139 } 140 case KeyRelease: 141 //SkDebugf("released key %i\n", evt.xkey.keycode); 142 this->handleKeyUp(XKeyToSkKey(XKeycodeToKeysym(dsp, evt.xkey.keycode, 0))); 143 break; 144 case ClientMessage: 145 if (SkEvent::ProcessEvent()) { 146 this->post_linuxevent(); 147 } 148 break; 149 default: 150 // Do nothing for other events 151 break; 152 } 153 } 154} 155 156void SkOSWindow::mapWindowAndWait() 157{ 158 Display* dsp = fUnixWindow.fDisplay; 159 Window win = fUnixWindow.fWin; 160 XMapWindow(dsp, win); 161 162 long eventMask = StructureNotifyMask; 163 XSelectInput(dsp, win, eventMask); 164 165 // Wait until screen is ready. 166 XEvent evt; 167 do { 168 XNextEvent(dsp, &evt); 169 } while(evt.type != MapNotify); 170 171} 172 173bool SkOSWindow::attachGL() 174{ 175 if (fGLAttached) return true; 176 Display* dsp = fUnixWindow.fDisplay; 177 if (!dsp || !fVi) return false; 178 179 if (!fUnixWindow.fGLCreated) { 180 fUnixWindow.fGLContext = glXCreateContext(dsp, fVi, NULL, GL_TRUE); 181 fUnixWindow.fGLCreated = true; 182 glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext); 183 glViewport(0, 0, SkScalarRound(this->width()), SkScalarRound(this->height())); 184 glClearColor(0, 0, 0, 0); 185 glClearStencil(0); 186 glStencilMask(0xffffffff); 187 glDisable(GL_SCISSOR_TEST); 188 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 189 } 190 else 191 glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext); 192 fGLAttached = true; 193 194 return true; 195} 196 197void SkOSWindow::detachGL() 198{ 199 if (!fUnixWindow.fDisplay || !fGLAttached) return; 200 fGLAttached = false; 201 // Returns back to normal drawing. 202 glXMakeCurrent(fUnixWindow.fDisplay, None, NULL); 203 // Ensure that we redraw when switching back to raster. 204 this->inval(NULL); 205} 206 207void SkOSWindow::presentGL() 208{ 209 if (fUnixWindow.fDisplay && fGLAttached) { 210 glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin); 211 } 212} 213 214void SkOSWindow::onSetTitle(const char title[]) 215{ 216 if (!fUnixWindow.fDisplay) return; 217 XTextProperty textProp; 218 textProp.value = (unsigned char*)title; 219 textProp.format = 8; 220 textProp.nitems = strlen((char*)textProp.value); 221 textProp.encoding = XA_STRING; 222 XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp); 223} 224 225void SkOSWindow::onHandleInval(const SkIRect&) { 226 (new SkEvent("inval-imageview", this->getSinkID()))->post(); 227} 228 229bool SkOSWindow::onEvent(const SkEvent& evt) 230{ 231 if (evt.isType("inval-imageview")) { 232 update(NULL); 233 if (!fGLAttached) 234 doPaint(); 235 return true; 236 } 237 return INHERITED::onEvent(evt); 238} 239 240static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap) 241{ 242 sk_bzero(&image, sizeof(image)); 243 244 int bitsPerPixel = bitmap.bytesPerPixel() * 8; 245 image.width = bitmap.width(); 246 image.height = bitmap.height(); 247 image.format = ZPixmap; 248 image.data = (char*) bitmap.getPixels(); 249 image.byte_order = LSBFirst; 250 image.bitmap_unit = bitsPerPixel; 251 image.bitmap_bit_order = LSBFirst; 252 image.bitmap_pad = bitsPerPixel; 253 image.depth = 24; 254 image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * bitmap.bytesPerPixel(); 255 image.bits_per_pixel = bitsPerPixel; 256 return XInitImage(&image); 257} 258 259void SkOSWindow::doPaint() { 260 if (!fUnixWindow.fDisplay) return; 261 // Draw the bitmap to the screen. 262 const SkBitmap& bitmap = getBitmap(); 263 int width = bitmap.width(); 264 int height = bitmap.height(); 265 266 XImage image; 267 if (!convertBitmapToXImage(image, bitmap)) return; 268 269 XPutImage(fUnixWindow.fDisplay, fUnixWindow.fWin, fUnixWindow.fGc, &image, 0, 0, 0, 0, width, height); 270} 271 272bool SkOSWindow::onHandleChar(SkUnichar) 273{ 274 return false; 275} 276 277bool SkOSWindow::onHandleKey(SkKey key) 278{ 279 return false; 280} 281 282bool SkOSWindow::onHandleKeyUp(SkKey key) 283{ 284 return false; 285} 286