Home | History | Annotate | Download | only in unix
      1 /*
      2 * Copyright 2016 Google Inc.
      3 *
      4 * Use of this source code is governed by a BSD-style license that can be
      5 * found in the LICENSE file.
      6 */
      7 
      8 //#include <tchar.h>
      9 
     10 #include "WindowContextFactory_unix.h"
     11 
     12 #include "SkUtils.h"
     13 #include "Timer.h"
     14 #include "../GLWindowContext.h"
     15 #include "Window_unix.h"
     16 
     17 extern "C" {
     18     #include "keysym2ucs.h"
     19 }
     20 #include <X11/Xutil.h>
     21 #include <X11/XKBlib.h>
     22 
     23 namespace sk_app {
     24 
     25 SkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap;
     26 
     27 Window* Window::CreateNativeWindow(void* platformData) {
     28     Display* display = (Display*)platformData;
     29     SkASSERT(display);
     30 
     31     Window_unix* window = new Window_unix();
     32     if (!window->initWindow(display)) {
     33         delete window;
     34         return nullptr;
     35     }
     36 
     37     return window;
     38 }
     39 
     40 const long kEventMask = ExposureMask | StructureNotifyMask |
     41                         KeyPressMask | KeyReleaseMask |
     42                         PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
     43 
     44 bool Window_unix::initWindow(Display* display) {
     45     if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
     46         this->closeWindow();
     47     }
     48     // we already have a window
     49     if (fDisplay) {
     50         return true;
     51     }
     52     fDisplay = display;
     53 
     54     constexpr int initialWidth = 1280;
     55     constexpr int initialHeight = 960;
     56 
     57     // Attempt to create a window that supports GL
     58 
     59     // We prefer the more recent glXChooseFBConfig but fall back to glXChooseVisual. They have
     60     // slight differences in how attributes are specified.
     61     static int constexpr kChooseFBConfigAtt[] = {
     62         GLX_RENDER_TYPE, GLX_RGBA_BIT,
     63         GLX_DOUBLEBUFFER, True,
     64         GLX_STENCIL_SIZE, 8,
     65         None
     66     };
     67     // For some reason glXChooseVisual takes a non-const pointer to the attributes.
     68     int chooseVisualAtt[] = {
     69         GLX_RGBA,
     70         GLX_DOUBLEBUFFER,
     71         GLX_STENCIL_SIZE, 8,
     72         None
     73     };
     74     SkASSERT(nullptr == fVisualInfo);
     75     if (fRequestedDisplayParams.fMSAASampleCount > 0) {
     76         static const GLint kChooseFBConifgAttCnt = SK_ARRAY_COUNT(kChooseFBConfigAtt);
     77         GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4];
     78         memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt));
     79         SkASSERT(None == msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1]);
     80         msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
     81         msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 0] = 1;
     82         msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 1] = GLX_SAMPLES_ARB;
     83         msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 2] = fRequestedDisplayParams.fMSAASampleCount;
     84         msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 3] = None;
     85         int n;
     86         fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), msaaChooseFBConfigAtt, &n);
     87         if (n > 0) {
     88             fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
     89         } else {
     90             static const GLint kChooseVisualAttCnt = SK_ARRAY_COUNT(chooseVisualAtt);
     91             GLint msaaChooseVisualAtt[kChooseVisualAttCnt + 4];
     92             memcpy(msaaChooseVisualAtt, chooseVisualAtt, sizeof(chooseVisualAtt));
     93             SkASSERT(None == msaaChooseVisualAtt[kChooseVisualAttCnt - 1]);
     94             msaaChooseFBConfigAtt[kChooseVisualAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
     95             msaaChooseFBConfigAtt[kChooseVisualAttCnt + 0] = 1;
     96             msaaChooseFBConfigAtt[kChooseVisualAttCnt + 1] = GLX_SAMPLES_ARB;
     97             msaaChooseFBConfigAtt[kChooseVisualAttCnt + 2] =
     98                     fRequestedDisplayParams.fMSAASampleCount;
     99             msaaChooseFBConfigAtt[kChooseVisualAttCnt + 3] = None;
    100             fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaChooseVisualAtt);
    101             fFBConfig = nullptr;
    102         }
    103     }
    104     if (nullptr == fVisualInfo) {
    105         int n;
    106         fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), kChooseFBConfigAtt, &n);
    107         if (n > 0) {
    108             fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
    109         } else {
    110             fVisualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
    111             fFBConfig = nullptr;
    112         }
    113     }
    114 
    115     if (fVisualInfo) {
    116         Colormap colorMap = XCreateColormap(display,
    117                                             RootWindow(display, fVisualInfo->screen),
    118                                             fVisualInfo->visual,
    119                                             AllocNone);
    120         XSetWindowAttributes swa;
    121         swa.colormap = colorMap;
    122         swa.event_mask = kEventMask;
    123         fWindow = XCreateWindow(display,
    124                                 RootWindow(display, fVisualInfo->screen),
    125                                 0, 0, // x, y
    126                                 initialWidth, initialHeight,
    127                                 0, // border width
    128                                 fVisualInfo->depth,
    129                                 InputOutput,
    130                                 fVisualInfo->visual,
    131                                 CWEventMask | CWColormap,
    132                                 &swa);
    133     } else {
    134         // Create a simple window instead.  We will not be able to show GL
    135         fWindow = XCreateSimpleWindow(display,
    136                                       DefaultRootWindow(display),
    137                                       0, 0,  // x, y
    138                                       initialWidth, initialHeight,
    139                                       0,     // border width
    140                                       0,     // border value
    141                                       0);    // background value
    142         XSelectInput(display, fWindow, kEventMask);
    143     }
    144 
    145     if (!fWindow) {
    146         return false;
    147     }
    148 
    149     fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount;
    150 
    151     // set up to catch window delete message
    152     fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
    153     XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
    154 
    155     // add to hashtable of windows
    156     gWindowMap.add(this);
    157 
    158     // init event variables
    159     fPendingPaint = false;
    160     fPendingResize = false;
    161 
    162     return true;
    163 }
    164 
    165 void Window_unix::closeWindow() {
    166     if (fDisplay) {
    167         this->detach();
    168         if (fGC) {
    169             XFreeGC(fDisplay, fGC);
    170             fGC = nullptr;
    171         }
    172         gWindowMap.remove(fWindow);
    173         XDestroyWindow(fDisplay, fWindow);
    174         fWindow = 0;
    175         if (fFBConfig) {
    176             XFree(fFBConfig);
    177             fFBConfig = nullptr;
    178         }
    179         if (fVisualInfo) {
    180             XFree(fVisualInfo);
    181             fVisualInfo = nullptr;
    182         }
    183         fDisplay = nullptr;
    184     }
    185 }
    186 
    187 static Window::Key get_key(KeySym keysym) {
    188     static const struct {
    189         KeySym      fXK;
    190         Window::Key fKey;
    191     } gPair[] = {
    192         { XK_BackSpace, Window::Key::kBack },
    193         { XK_Clear, Window::Key::kBack },
    194         { XK_Return, Window::Key::kOK },
    195         { XK_Up, Window::Key::kUp },
    196         { XK_Down, Window::Key::kDown },
    197         { XK_Left, Window::Key::kLeft },
    198         { XK_Right, Window::Key::kRight },
    199         { XK_Tab, Window::Key::kTab },
    200         { XK_Page_Up, Window::Key::kPageUp },
    201         { XK_Page_Down, Window::Key::kPageDown },
    202         { XK_Home, Window::Key::kHome },
    203         { XK_End, Window::Key::kEnd },
    204         { XK_Delete, Window::Key::kDelete },
    205         { XK_Escape, Window::Key::kEscape },
    206         { XK_Shift_L, Window::Key::kShift },
    207         { XK_Shift_R, Window::Key::kShift },
    208         { XK_Control_L, Window::Key::kCtrl },
    209         { XK_Control_R, Window::Key::kCtrl },
    210         { XK_Alt_L, Window::Key::kOption },
    211         { XK_Alt_R, Window::Key::kOption },
    212         { 'A', Window::Key::kA },
    213         { 'C', Window::Key::kC },
    214         { 'V', Window::Key::kV },
    215         { 'X', Window::Key::kX },
    216         { 'Y', Window::Key::kY },
    217         { 'Z', Window::Key::kZ },
    218     };
    219     for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
    220         if (gPair[i].fXK == keysym) {
    221             return gPair[i].fKey;
    222         }
    223     }
    224     return Window::Key::kNONE;
    225 }
    226 
    227 static uint32_t get_modifiers(const XEvent& event) {
    228     static const struct {
    229         unsigned    fXMask;
    230         unsigned    fSkMask;
    231     } gModifiers[] = {
    232         { ShiftMask,   Window::kShift_ModifierKey },
    233         { ControlMask, Window::kControl_ModifierKey },
    234         { Mod1Mask,    Window::kOption_ModifierKey },
    235     };
    236 
    237     auto modifiers = 0;
    238     for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
    239         if (event.xkey.state & gModifiers[i].fXMask) {
    240             modifiers |= gModifiers[i].fSkMask;
    241         }
    242     }
    243     return modifiers;
    244 }
    245 
    246 bool Window_unix::handleEvent(const XEvent& event) {
    247     switch (event.type) {
    248         case MapNotify:
    249             if (!fGC) {
    250                 fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
    251             }
    252             break;
    253 
    254         case ClientMessage:
    255             if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
    256                 gWindowMap.count() == 1) {
    257                 return true;
    258             }
    259             break;
    260 
    261         case ButtonPress:
    262             switch (event.xbutton.button) {
    263                 case Button1:
    264                     this->onMouse(event.xbutton.x, event.xbutton.y,
    265                                   Window::kDown_InputState, get_modifiers(event));
    266                     break;
    267                 case Button4:
    268                     this->onMouseWheel(1.0f, get_modifiers(event));
    269                     break;
    270                 case Button5:
    271                     this->onMouseWheel(-1.0f, get_modifiers(event));
    272                     break;
    273             }
    274             break;
    275 
    276         case ButtonRelease:
    277             if (event.xbutton.button == Button1) {
    278                 this->onMouse(event.xbutton.x, event.xbutton.y,
    279                               Window::kUp_InputState, get_modifiers(event));
    280             }
    281             break;
    282 
    283         case MotionNotify:
    284             this->onMouse(event.xmotion.x, event.xmotion.y,
    285                           Window::kMove_InputState, get_modifiers(event));
    286             break;
    287 
    288         case KeyPress: {
    289             int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
    290             KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 0, shiftLevel);
    291             Window::Key key = get_key(keysym);
    292             if (key != Window::Key::kNONE) {
    293                 if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
    294                     if (keysym == XK_Escape) {
    295                         return true;
    296                     }
    297                 }
    298             }
    299 
    300             long uni = keysym2ucs(keysym);
    301             if (uni != -1) {
    302                 (void) this->onChar((SkUnichar) uni, get_modifiers(event));
    303             }
    304         } break;
    305 
    306         case KeyRelease: {
    307             int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
    308             KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
    309                                                0, shiftLevel);
    310             Window::Key key = get_key(keysym);
    311             (void) this->onKey(key, Window::kUp_InputState,
    312                                get_modifiers(event));
    313         } break;
    314 
    315 
    316         default:
    317             // these events should be handled in the main event loop
    318             SkASSERT(event.type != Expose && event.type != ConfigureNotify);
    319             break;
    320     }
    321 
    322     return false;
    323 }
    324 
    325 void Window_unix::setTitle(const char* title) {
    326     XTextProperty textproperty;
    327     XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty);
    328     XSetWMName(fDisplay, fWindow, &textproperty);
    329 }
    330 
    331 void Window_unix::show() {
    332     XMapWindow(fDisplay, fWindow);
    333 }
    334 
    335 bool Window_unix::attach(BackendType attachType) {
    336     this->initWindow(fDisplay);
    337 
    338     window_context_factory::XlibWindowInfo winInfo;
    339     winInfo.fDisplay = fDisplay;
    340     winInfo.fWindow = fWindow;
    341     winInfo.fFBConfig = fFBConfig;
    342     winInfo.fVisualInfo = fVisualInfo;
    343 
    344     XWindowAttributes attrs;
    345     if (XGetWindowAttributes(fDisplay, fWindow, &attrs)) {
    346         winInfo.fWidth = attrs.width;
    347         winInfo.fHeight = attrs.height;
    348     } else {
    349         winInfo.fWidth = winInfo.fHeight = 0;
    350     }
    351 
    352     switch (attachType) {
    353 #ifdef SK_VULKAN
    354         case kVulkan_BackendType:
    355             fWindowContext = window_context_factory::NewVulkanForXlib(winInfo,
    356                                                                       fRequestedDisplayParams);
    357             break;
    358 #endif
    359         case kNativeGL_BackendType:
    360             fWindowContext = window_context_factory::NewGLForXlib(winInfo, fRequestedDisplayParams);
    361             break;
    362         case kRaster_BackendType:
    363             fWindowContext = window_context_factory::NewRasterForXlib(winInfo,
    364                                                                       fRequestedDisplayParams);
    365             break;
    366     }
    367     this->onBackendCreated();
    368 
    369     return (SkToBool(fWindowContext));
    370 }
    371 
    372 void Window_unix::onInval() {
    373     XEvent event;
    374     event.type = Expose;
    375     event.xexpose.send_event = True;
    376     event.xexpose.display = fDisplay;
    377     event.xexpose.window = fWindow;
    378     event.xexpose.x = 0;
    379     event.xexpose.y = 0;
    380     event.xexpose.width = this->width();
    381     event.xexpose.height = this->height();
    382     event.xexpose.count = 0;
    383 
    384     XSendEvent(fDisplay, fWindow, False, 0, &event);
    385 }
    386 
    387 }   // namespace sk_app
    388