SkOSWindow_Unix.cpp revision 93c7ee34dc5c8f6bfad65809f4b39f8d00d7f0d4
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/XKBlib.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*) 36 : fVi(NULL) 37 , fMSAASampleCount(0) { 38 fUnixWindow.fDisplay = NULL; 39 fUnixWindow.fGLContext = NULL; 40 this->initWindow(0, NULL); 41 this->resize(WIDTH, HEIGHT); 42} 43 44SkOSWindow::~SkOSWindow() { 45 this->closeWindow(); 46} 47 48void SkOSWindow::closeWindow() { 49 if (NULL != fUnixWindow.fDisplay) { 50 this->detach(); 51 SkASSERT(NULL != fUnixWindow.fGc); 52 XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc); 53 fUnixWindow.fGc = NULL; 54 XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin); 55 fVi = NULL; 56 XCloseDisplay(fUnixWindow.fDisplay); 57 fUnixWindow.fDisplay = NULL; 58 fMSAASampleCount = 0; 59 } 60} 61 62void SkOSWindow::initWindow(int requestedMSAASampleCount, AttachmentInfo* info) { 63 if (fMSAASampleCount != requestedMSAASampleCount) { 64 this->closeWindow(); 65 } 66 // presence of fDisplay means we already have a window 67 if (NULL != fUnixWindow.fDisplay) { 68 if (NULL != info) { 69 if (NULL != fVi) { 70 glXGetConfig(fUnixWindow.fDisplay, fVi, GLX_SAMPLES_ARB, &info->fSampleCount); 71 glXGetConfig(fUnixWindow.fDisplay, fVi, GLX_STENCIL_SIZE, &info->fStencilBits); 72 } else { 73 info->fSampleCount = 0; 74 info->fStencilBits = 0; 75 } 76 } 77 return; 78 } 79 fUnixWindow.fDisplay = XOpenDisplay(NULL); 80 Display* dsp = fUnixWindow.fDisplay; 81 if (NULL == dsp) { 82 SkDebugf("Could not open an X Display"); 83 return; 84 } 85 // Attempt to create a window that supports GL 86 GLint att[] = { 87 GLX_RGBA, 88 GLX_DEPTH_SIZE, 24, 89 GLX_DOUBLEBUFFER, 90 GLX_STENCIL_SIZE, 8, 91 None 92 }; 93 SkASSERT(NULL == fVi); 94 if (requestedMSAASampleCount > 0) { 95 static const GLint kAttCount = SK_ARRAY_COUNT(att); 96 GLint msaaAtt[kAttCount + 4]; 97 memcpy(msaaAtt, att, sizeof(att)); 98 SkASSERT(None == msaaAtt[kAttCount - 1]); 99 msaaAtt[kAttCount - 1] = GLX_SAMPLE_BUFFERS_ARB; 100 msaaAtt[kAttCount + 0] = 1; 101 msaaAtt[kAttCount + 1] = GLX_SAMPLES_ARB; 102 msaaAtt[kAttCount + 2] = requestedMSAASampleCount; 103 msaaAtt[kAttCount + 3] = None; 104 fVi = glXChooseVisual(dsp, DefaultScreen(dsp), msaaAtt); 105 fMSAASampleCount = requestedMSAASampleCount; 106 } 107 if (NULL == fVi) { 108 fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att); 109 fMSAASampleCount = 0; 110 } 111 112 if (fVi) { 113 if (NULL != info) { 114 glXGetConfig(dsp, fVi, GLX_SAMPLES_ARB, &info->fSampleCount); 115 glXGetConfig(dsp, fVi, GLX_STENCIL_SIZE, &info->fStencilBits); 116 } 117 Colormap colorMap = XCreateColormap(dsp, 118 RootWindow(dsp, fVi->screen), 119 fVi->visual, 120 AllocNone); 121 XSetWindowAttributes swa; 122 swa.colormap = colorMap; 123 swa.event_mask = EVENT_MASK; 124 fUnixWindow.fWin = XCreateWindow(dsp, 125 RootWindow(dsp, fVi->screen), 126 0, 0, // x, y 127 WIDTH, HEIGHT, 128 0, // border width 129 fVi->depth, 130 InputOutput, 131 fVi->visual, 132 CWEventMask | CWColormap, 133 &swa); 134 } else { 135 if (NULL != info) { 136 info->fSampleCount = 0; 137 info->fStencilBits = 0; 138 } 139 // Create a simple window instead. We will not be able to show GL 140 fUnixWindow.fWin = XCreateSimpleWindow(dsp, 141 DefaultRootWindow(dsp), 142 0, 0, // x, y 143 WIDTH, HEIGHT, 144 0, // border width 145 0, // border value 146 0); // background value 147 } 148 this->mapWindowAndWait(); 149 fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, NULL); 150} 151 152 153void SkOSWindow::post_linuxevent() { 154 // Put an event in the X queue to fire an SkEvent. 155 if (NULL == fUnixWindow.fDisplay) { 156 return; 157 } 158 XClientMessageEvent event; 159 event.type = ClientMessage; 160 Atom myAtom(0); 161 event.message_type = myAtom; 162 event.format = 32; 163 event.data.l[0] = 0; 164 XSendEvent(fUnixWindow.fDisplay, fUnixWindow.fWin, false, 0, 165 (XEvent*) &event); 166 XFlush(fUnixWindow.fDisplay); 167} 168 169static unsigned getModi(const XEvent& evt) { 170 static const struct { 171 unsigned fXMask; 172 unsigned fSkMask; 173 } gModi[] = { 174 // X values found by experiment. Is there a better way? 175 { 1, kShift_SkModifierKey }, 176 { 4, kControl_SkModifierKey }, 177 { 8, kOption_SkModifierKey }, 178 }; 179 180 unsigned modi = 0; 181 for (size_t i = 0; i < SK_ARRAY_COUNT(gModi); ++i) { 182 if (evt.xkey.state & gModi[i].fXMask) { 183 modi |= gModi[i].fSkMask; 184 } 185 } 186 return modi; 187} 188 189void SkOSWindow::loop() { 190 Display* dsp = fUnixWindow.fDisplay; 191 if (NULL == dsp) { 192 return; 193 } 194 XSelectInput(dsp, fUnixWindow.fWin, EVENT_MASK); 195 196 bool loop = true; 197 XEvent evt; 198 while (loop) { 199 XNextEvent(dsp, &evt); 200 switch (evt.type) { 201 case Expose: 202 if (evt.xexpose.count == 0) 203 this->inval(NULL); 204 break; 205 case ConfigureNotify: 206 this->resize(evt.xconfigure.width, evt.xconfigure.height); 207 break; 208 case ButtonPress: 209 if (evt.xbutton.button == Button1) 210 this->handleClick(evt.xbutton.x, evt.xbutton.y, 211 SkView::Click::kDown_State, NULL, getModi(evt)); 212 break; 213 case ButtonRelease: 214 if (evt.xbutton.button == Button1) 215 this->handleClick(evt.xbutton.x, evt.xbutton.y, 216 SkView::Click::kUp_State, NULL, getModi(evt)); 217 break; 218 case MotionNotify: 219 this->handleClick(evt.xmotion.x, evt.xmotion.y, 220 SkView::Click::kMoved_State, NULL, getModi(evt)); 221 break; 222 case KeyPress: { 223 KeySym keysym = XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0); 224 //SkDebugf("pressed key %i!\n\tKeySym:%i\n", evt.xkey.keycode, XKeycodeToKeysym(dsp, evt.xkey.keycode, 0)); 225 if (keysym == XK_Escape) { 226 loop = false; 227 break; 228 } 229 this->handleKey(XKeyToSkKey(keysym)); 230 long uni = keysym2ucs(keysym); 231 if (uni != -1) { 232 this->handleChar((SkUnichar) uni); 233 } 234 break; 235 } 236 case KeyRelease: 237 //SkDebugf("released key %i\n", evt.xkey.keycode); 238 this->handleKeyUp(XKeyToSkKey(XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0))); 239 break; 240 case ClientMessage: 241 if (SkEvent::ProcessEvent()) { 242 this->post_linuxevent(); 243 } 244 break; 245 default: 246 // Do nothing for other events 247 break; 248 } 249 } 250} 251 252void SkOSWindow::mapWindowAndWait() { 253 SkASSERT(NULL != fUnixWindow.fDisplay); 254 Display* dsp = fUnixWindow.fDisplay; 255 Window win = fUnixWindow.fWin; 256 XMapWindow(dsp, win); 257 258 long eventMask = StructureNotifyMask; 259 XSelectInput(dsp, win, eventMask); 260 261 // Wait until screen is ready. 262 XEvent evt; 263 do { 264 XNextEvent(dsp, &evt); 265 } while(evt.type != MapNotify); 266 267} 268 269bool SkOSWindow::attach(SkBackEndTypes, int msaaSampleCount, AttachmentInfo* info) { 270 this->initWindow(msaaSampleCount, info); 271 272 if (NULL == fUnixWindow.fDisplay) { 273 return false; 274 } 275 if (NULL == fUnixWindow.fGLContext) { 276 SkASSERT(NULL != fVi); 277 278 fUnixWindow.fGLContext = glXCreateContext(fUnixWindow.fDisplay, 279 fVi, 280 NULL, 281 GL_TRUE); 282 if (NULL == fUnixWindow.fGLContext) { 283 return false; 284 } 285 } 286 glXMakeCurrent(fUnixWindow.fDisplay, 287 fUnixWindow.fWin, 288 fUnixWindow.fGLContext); 289 glViewport(0, 0, 290 SkScalarRound(this->width()), SkScalarRound(this->height())); 291 glClearColor(0, 0, 0, 0); 292 glClearStencil(0); 293 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 294 return true; 295} 296 297void SkOSWindow::detach() { 298 if (NULL == fUnixWindow.fDisplay || NULL == fUnixWindow.fGLContext) { 299 return; 300 } 301 glXMakeCurrent(fUnixWindow.fDisplay, None, NULL); 302 glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext); 303 fUnixWindow.fGLContext = NULL; 304} 305 306void SkOSWindow::present() { 307 if (NULL != fUnixWindow.fDisplay && NULL != fUnixWindow.fGLContext) { 308 glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin); 309 } 310} 311 312void SkOSWindow::onSetTitle(const char title[]) { 313 if (NULL == fUnixWindow.fDisplay) { 314 return; 315 } 316 XTextProperty textProp; 317 textProp.value = (unsigned char*)title; 318 textProp.format = 8; 319 textProp.nitems = strlen((char*)textProp.value); 320 textProp.encoding = XA_STRING; 321 XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp); 322} 323 324void SkOSWindow::onHandleInval(const SkIRect&) { 325 (new SkEvent("inval-imageview", this->getSinkID()))->post(); 326} 327 328bool SkOSWindow::onEvent(const SkEvent& evt) { 329 if (evt.isType("inval-imageview")) { 330 update(NULL); 331 if (NULL == fUnixWindow.fGLContext) 332 this->doPaint(); 333 return true; 334 } 335 return INHERITED::onEvent(evt); 336} 337 338static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap) { 339 sk_bzero(&image, sizeof(image)); 340 341 int bitsPerPixel = bitmap.bytesPerPixel() * 8; 342 image.width = bitmap.width(); 343 image.height = bitmap.height(); 344 image.format = ZPixmap; 345 image.data = (char*) bitmap.getPixels(); 346 image.byte_order = LSBFirst; 347 image.bitmap_unit = bitsPerPixel; 348 image.bitmap_bit_order = LSBFirst; 349 image.bitmap_pad = bitsPerPixel; 350 image.depth = 24; 351 image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * bitmap.bytesPerPixel(); 352 image.bits_per_pixel = bitsPerPixel; 353 return XInitImage(&image); 354} 355 356void SkOSWindow::doPaint() { 357 if (NULL == fUnixWindow.fDisplay) { 358 return; 359 } 360 // Draw the bitmap to the screen. 361 const SkBitmap& bitmap = getBitmap(); 362 int width = bitmap.width(); 363 int height = bitmap.height(); 364 365 XImage image; 366 if (!convertBitmapToXImage(image, bitmap)) { 367 return; 368 } 369 370 XPutImage(fUnixWindow.fDisplay, 371 fUnixWindow.fWin, 372 fUnixWindow.fGc, 373 &image, 374 0, 0, // src x,y 375 0, 0, // dst x,y 376 width, height); 377} 378 379bool SkOSWindow::onHandleChar(SkUnichar) { 380 return false; 381} 382 383bool SkOSWindow::onHandleKey(SkKey) { 384 return false; 385} 386 387bool SkOSWindow::onHandleKeyUp(SkKey) { 388 return false; 389} 390