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