Edit

kc3-lang/angle/util/x11/X11Window.cpp

Branch :

  • Show log

    Commit

  • Author : Yunchao He
    Date : 2017-04-24 10:49:17
    Hash : f81ce4a3
    Message : Refactoring: replace NULL by nullptr for pointers (3rd CL). This CL mainly handles passing/returning NULL to/from a function. BUG=angleproject:2001 Change-Id: I34802f792e710e3d7ff697cbe4701dc1bf5ab009 Reviewed-on: https://chromium-review.googlesource.com/485060 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • util/x11/X11Window.cpp
  • //
    // Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // X11Window.cpp: Implementation of OSWindow for X11
    
    #include "x11/X11Window.h"
    
    #include "common/debug.h"
    #include "system_utils.h"
    #include "Timer.h"
    
    namespace {
    
    Bool WaitForMapNotify(Display *dpy, XEvent *event, XPointer window)
    {
        return event->type == MapNotify && event->xmap.window == reinterpret_cast<Window>(window);
    }
    
    static Key X11CodeToKey(Display *display, unsigned int scancode)
    {
        int temp;
        KeySym *keySymbols;
        keySymbols = XGetKeyboardMapping(display, scancode, 1, &temp);
    
        unsigned int keySymbol = keySymbols[0];
        XFree(keySymbols);
    
        switch (keySymbol)
        {
          case XK_Shift_L:     return KEY_LSHIFT;
          case XK_Shift_R:     return KEY_RSHIFT;
          case XK_Alt_L:       return KEY_LALT;
          case XK_Alt_R:       return KEY_RALT;
          case XK_Control_L:   return KEY_LCONTROL;
          case XK_Control_R:   return KEY_RCONTROL;
          case XK_Super_L:     return KEY_LSYSTEM;
          case XK_Super_R:     return KEY_RSYSTEM;
          case XK_Menu:        return KEY_MENU;
    
          case XK_semicolon:   return KEY_SEMICOLON;
          case XK_slash:       return KEY_SLASH;
          case XK_equal:       return KEY_EQUAL;
          case XK_minus:       return KEY_DASH;
          case XK_bracketleft: return KEY_LBRACKET;
          case XK_bracketright:return KEY_RBRACKET;
          case XK_comma:       return KEY_COMMA;
          case XK_period:      return KEY_PERIOD;
          case XK_backslash:   return KEY_BACKSLASH;
          case XK_asciitilde:  return KEY_TILDE;
          case XK_Escape:      return KEY_ESCAPE;
          case XK_space:       return KEY_SPACE;
          case XK_Return:      return KEY_RETURN;
          case XK_BackSpace:   return KEY_BACK;
          case XK_Tab:         return KEY_TAB;
          case XK_Page_Up:     return KEY_PAGEUP;
          case XK_Page_Down:   return KEY_PAGEDOWN;
          case XK_End:         return KEY_END;
          case XK_Home:        return KEY_HOME;
          case XK_Insert:      return KEY_INSERT;
          case XK_Delete:      return KEY_DELETE;
          case XK_KP_Add:      return KEY_ADD;
          case XK_KP_Subtract: return KEY_SUBTRACT;
          case XK_KP_Multiply: return KEY_MULTIPLY;
          case XK_KP_Divide:   return KEY_DIVIDE;
          case XK_Pause:       return KEY_PAUSE;
    
          case XK_F1:          return KEY_F1;
          case XK_F2:          return KEY_F2;
          case XK_F3:          return KEY_F3;
          case XK_F4:          return KEY_F4;
          case XK_F5:          return KEY_F5;
          case XK_F6:          return KEY_F6;
          case XK_F7:          return KEY_F7;
          case XK_F8:          return KEY_F8;
          case XK_F9:          return KEY_F9;
          case XK_F10:         return KEY_F10;
          case XK_F11:         return KEY_F11;
          case XK_F12:         return KEY_F12;
          case XK_F13:         return KEY_F13;
          case XK_F14:         return KEY_F14;
          case XK_F15:         return KEY_F15;
    
          case XK_Left:        return KEY_LEFT;
          case XK_Right:       return KEY_RIGHT;
          case XK_Down:        return KEY_DOWN;
          case XK_Up:          return KEY_UP;
    
          case XK_KP_Insert:   return KEY_NUMPAD0;
          case XK_KP_End:      return KEY_NUMPAD1;
          case XK_KP_Down:     return KEY_NUMPAD2;
          case XK_KP_Page_Down:return KEY_NUMPAD3;
          case XK_KP_Left:     return KEY_NUMPAD4;
          case XK_KP_5:        return KEY_NUMPAD5;
          case XK_KP_Right:    return KEY_NUMPAD6;
          case XK_KP_Home:     return KEY_NUMPAD7;
          case XK_KP_Up:       return KEY_NUMPAD8;
          case XK_KP_Page_Up:  return KEY_NUMPAD9;
    
          case XK_a:           return KEY_A;
          case XK_b:           return KEY_B;
          case XK_c:           return KEY_C;
          case XK_d:           return KEY_D;
          case XK_e:           return KEY_E;
          case XK_f:           return KEY_F;
          case XK_g:           return KEY_G;
          case XK_h:           return KEY_H;
          case XK_i:           return KEY_I;
          case XK_j:           return KEY_J;
          case XK_k:           return KEY_K;
          case XK_l:           return KEY_L;
          case XK_m:           return KEY_M;
          case XK_n:           return KEY_N;
          case XK_o:           return KEY_O;
          case XK_p:           return KEY_P;
          case XK_q:           return KEY_Q;
          case XK_r:           return KEY_R;
          case XK_s:           return KEY_S;
          case XK_t:           return KEY_T;
          case XK_u:           return KEY_U;
          case XK_v:           return KEY_V;
          case XK_w:           return KEY_W;
          case XK_x:           return KEY_X;
          case XK_y:           return KEY_Y;
          case XK_z:           return KEY_Z;
    
          case XK_1:           return KEY_NUM1;
          case XK_2:           return KEY_NUM2;
          case XK_3:           return KEY_NUM3;
          case XK_4:           return KEY_NUM4;
          case XK_5:           return KEY_NUM5;
          case XK_6:           return KEY_NUM6;
          case XK_7:           return KEY_NUM7;
          case XK_8:           return KEY_NUM8;
          case XK_9:           return KEY_NUM9;
          case XK_0:           return KEY_NUM0;
        }
    
        return Key(0);
    }
    
    static void AddX11KeyStateToEvent(Event *event, unsigned int state)
    {
        event->Key.Shift = state & ShiftMask;
        event->Key.Control = state & ControlMask;
        event->Key.Alt = state & Mod1Mask;
        event->Key.System = state & Mod4Mask;
    }
    
    }
    
    X11Window::X11Window()
        : WM_DELETE_WINDOW(None),
          WM_PROTOCOLS(None),
          TEST_EVENT(None),
          mDisplay(nullptr),
          mWindow(0),
          mRequestedVisualId(-1),
          mVisible(false)
    {
    }
    
    X11Window::X11Window(int visualId)
        : WM_DELETE_WINDOW(None),
          WM_PROTOCOLS(None),
          TEST_EVENT(None),
          mDisplay(nullptr),
          mWindow(0),
          mRequestedVisualId(visualId),
          mVisible(false)
    {
    }
    
    X11Window::~X11Window()
    {
        destroy();
    }
    
    bool X11Window::initialize(const std::string &name, size_t width, size_t height)
    {
        destroy();
    
        mDisplay = XOpenDisplay(nullptr);
        if (!mDisplay)
        {
            return false;
        }
    
        {
            int screen = DefaultScreen(mDisplay);
            Window root = RootWindow(mDisplay, screen);
    
            Visual *visual;
            if (mRequestedVisualId == -1)
            {
                visual = DefaultVisual(mDisplay, screen);
            }
            else
            {
                XVisualInfo visualTemplate;
                visualTemplate.visualid = mRequestedVisualId;
    
                int numVisuals       = 0;
                XVisualInfo *visuals = XGetVisualInfo(mDisplay, VisualIDMask, &visualTemplate, &numVisuals);
                if (numVisuals <= 0)
                {
                    return false;
                }
                ASSERT(numVisuals == 1);
    
                visual = visuals[0].visual;
                XFree(visuals);
            }
    
            int depth = DefaultDepth(mDisplay, screen);
            Colormap colormap = XCreateColormap(mDisplay, root, visual, AllocNone);
    
            XSetWindowAttributes attributes;
            unsigned long attributeMask = CWBorderPixel | CWColormap | CWEventMask;
    
            attributes.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask |
                                    ButtonReleaseMask | FocusChangeMask | EnterWindowMask |
                                    LeaveWindowMask | KeyPressMask | KeyReleaseMask;
            attributes.border_pixel = 0;
            attributes.colormap = colormap;
    
            mWindow = XCreateWindow(mDisplay, root, 0, 0, width, height, 0, depth, InputOutput,
                                    visual, attributeMask, &attributes);
            XFreeColormap(mDisplay, colormap);
        }
    
        if (!mWindow)
        {
            destroy();
            return false;
        }
    
        // Tell the window manager to notify us when the user wants to close the
        // window so we can do it ourselves.
        WM_DELETE_WINDOW = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
        WM_PROTOCOLS = XInternAtom(mDisplay, "WM_PROTOCOLS", False);
        if (WM_DELETE_WINDOW == None || WM_PROTOCOLS == None)
        {
            destroy();
            return false;
        }
    
        if(XSetWMProtocols(mDisplay, mWindow, &WM_DELETE_WINDOW, 1) == 0)
        {
            destroy();
            return false;
        }
    
        // Create an atom to identify our test event
        TEST_EVENT = XInternAtom(mDisplay, "ANGLE_TEST_EVENT", False);
        if (TEST_EVENT == None)
        {
            destroy();
            return false;
        }
    
        XFlush(mDisplay);
    
        mX = 0;
        mY = 0;
        mWidth = width;
        mHeight = height;
    
        return true;
    }
    
    void X11Window::destroy()
    {
        if (mWindow)
        {
            XDestroyWindow(mDisplay, mWindow);
            mWindow = 0;
        }
        if (mDisplay)
        {
            XCloseDisplay(mDisplay);
            mDisplay = nullptr;
        }
        WM_DELETE_WINDOW = None;
        WM_PROTOCOLS = None;
    }
    
    EGLNativeWindowType X11Window::getNativeWindow() const
    {
        return mWindow;
    }
    
    EGLNativeDisplayType X11Window::getNativeDisplay() const
    {
        return mDisplay;
    }
    
    void X11Window::messageLoop()
    {
        int eventCount = XPending(mDisplay);
        while (eventCount--)
        {
            XEvent event;
            XNextEvent(mDisplay, &event);
            processEvent(event);
        }
    }
    
    void X11Window::setMousePosition(int x, int y)
    {
        XWarpPointer(mDisplay, None, mWindow, 0, 0, 0, 0, x, y);
    }
    
    OSWindow *CreateOSWindow()
    {
        return new X11Window();
    }
    
    bool X11Window::setPosition(int x, int y)
    {
        XMoveWindow(mDisplay, mWindow, x, y);
        XFlush(mDisplay);
        return true;
    }
    
    bool X11Window::resize(int width, int height)
    {
        XResizeWindow(mDisplay, mWindow, width, height);
        XFlush(mDisplay);
    
        Timer* timer = CreateTimer();
        timer->start();
    
        // Wait until the window as actually been resized so that the code calling resize
        // can assume the window has been resized.
        const double kResizeWaitDelay = 0.2;
        while (mHeight != height && mWidth != width && timer->getElapsedTime() < kResizeWaitDelay)
        {
            messageLoop();
            angle::Sleep(10);
        }
    
        delete timer;
    
        return true;
    }
    
    void X11Window::setVisible(bool isVisible)
    {
        if (mVisible == isVisible)
        {
            return;
        }
    
        if (isVisible)
        {
            XMapWindow(mDisplay, mWindow);
    
            // Wait until we get an event saying this window is mapped so that the
            // code calling setVisible can assume the window is visible.
            // This is important when creating a framebuffer as the framebuffer content
            // is undefined when the window is not visible.
            XEvent dummyEvent;
            XIfEvent(mDisplay, &dummyEvent, WaitForMapNotify, reinterpret_cast<XPointer>(mWindow));
        }
        else
        {
            XUnmapWindow(mDisplay, mWindow);
            XFlush(mDisplay);
        }
        mVisible = isVisible;
    }
    
    void X11Window::signalTestEvent()
    {
        XEvent event;
        event.type = ClientMessage;
        event.xclient.message_type = TEST_EVENT;
        // Format needs to be valid or a BadValue is generated
        event.xclient.format = 32;
    
        // Hijack StructureNotifyMask as we know we will be listening for it.
        XSendEvent(mDisplay, mWindow, False, StructureNotifyMask, &event);
    }
    
    void X11Window::processEvent(const XEvent &xEvent)
    {
        // TODO(cwallez) text events
        switch (xEvent.type)
        {
          case ButtonPress:
            {
                Event event;
                MouseButton button = MOUSEBUTTON_UNKNOWN;
                int wheelY = 0;
    
                // The mouse wheel updates are sent via button events.
                switch (xEvent.xbutton.button)
                {
                  case Button4:
                    wheelY = 1;
                    break;
                  case Button5:
                    wheelY = -1;
                    break;
                  case 6:
                    break;
                  case 7:
                    break;
    
                  case Button1:
                    button = MOUSEBUTTON_LEFT;
                    break;
                  case Button2:
                    button = MOUSEBUTTON_MIDDLE;
                    break;
                  case Button3:
                    button = MOUSEBUTTON_RIGHT;
                    break;
                  case 8:
                    button = MOUSEBUTTON_BUTTON4;
                    break;
                  case 9:
                    button = MOUSEBUTTON_BUTTON5;
                    break;
    
                  default:
                    break;
                }
    
                if (wheelY != 0)
                {
                    event.Type = Event::EVENT_MOUSE_WHEEL_MOVED;
                    event.MouseWheel.Delta = wheelY;
                    pushEvent(event);
                }
    
                if (button != MOUSEBUTTON_UNKNOWN)
                {
                    event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
                    event.MouseButton.Button = button;
                    event.MouseButton.X = xEvent.xbutton.x;
                    event.MouseButton.Y = xEvent.xbutton.y;
                    pushEvent(event);
                }
            }
            break;
    
          case ButtonRelease:
            {
                Event event;
                MouseButton button = MOUSEBUTTON_UNKNOWN;
    
                switch (xEvent.xbutton.button)
                {
                  case Button1:
                    button = MOUSEBUTTON_LEFT;
                    break;
                  case Button2:
                    button = MOUSEBUTTON_MIDDLE;
                    break;
                  case Button3:
                    button = MOUSEBUTTON_RIGHT;
                    break;
                  case 8:
                    button = MOUSEBUTTON_BUTTON4;
                    break;
                  case 9:
                    button = MOUSEBUTTON_BUTTON5;
                    break;
    
                  default:
                    break;
                }
    
                if (button != MOUSEBUTTON_UNKNOWN)
                {
                    event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
                    event.MouseButton.Button = button;
                    event.MouseButton.X = xEvent.xbutton.x;
                    event.MouseButton.Y = xEvent.xbutton.y;
                    pushEvent(event);
                }
            }
            break;
    
          case KeyPress:
            {
                Event event;
                event.Type = Event::EVENT_KEY_PRESSED;
                event.Key.Code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
                AddX11KeyStateToEvent(&event, xEvent.xkey.state);
                pushEvent(event);
            }
            break;
    
          case KeyRelease:
            {
                Event event;
                event.Type = Event::EVENT_KEY_RELEASED;
                event.Key.Code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
                AddX11KeyStateToEvent(&event, xEvent.xkey.state);
                pushEvent(event);
            }
            break;
    
          case EnterNotify:
            {
                Event event;
                event.Type = Event::EVENT_MOUSE_ENTERED;
                pushEvent(event);
            }
            break;
    
          case LeaveNotify:
            {
                Event event;
                event.Type = Event::EVENT_MOUSE_LEFT;
                pushEvent(event);
            }
            break;
    
          case MotionNotify:
            {
                Event event;
                event.Type = Event::EVENT_MOUSE_MOVED;
                event.MouseMove.X = xEvent.xmotion.x;
                event.MouseMove.Y = xEvent.xmotion.y;
                pushEvent(event);
            }
            break;
    
          case ConfigureNotify:
            {
                if (xEvent.xconfigure.width != mWidth || xEvent.xconfigure.height != mHeight)
                {
                    Event event;
                    event.Type = Event::EVENT_RESIZED;
                    event.Size.Width = xEvent.xconfigure.width;
                    event.Size.Height = xEvent.xconfigure.height;
                    pushEvent(event);
                }
                if (xEvent.xconfigure.x != mX || xEvent.xconfigure.y != mY)
                {
                    // Sometimes, the window manager reparents our window (for example
                    // when resizing) then the X and Y coordinates will be with respect to
                    // the new parent and not what the user wants to know. Use
                    // XTranslateCoordinates to get the coordinates on the screen.
                    int screen = DefaultScreen(mDisplay);
                    Window root = RootWindow(mDisplay, screen);
    
                    int x, y;
                    Window child;
                    XTranslateCoordinates(mDisplay, mWindow, root, 0, 0, &x, &y, &child);
    
                    if (x != mX || y != mY)
                    {
                        Event event;
                        event.Type = Event::EVENT_MOVED;
                        event.Move.X = x;
                        event.Move.Y = y;
                        pushEvent(event);
                    }
                }
            }
            break;
    
          case FocusIn:
            if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
            {
                Event event;
                event.Type = Event::EVENT_GAINED_FOCUS;
                pushEvent(event);
            }
            break;
    
          case FocusOut:
            if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
            {
                Event event;
                event.Type = Event::EVENT_LOST_FOCUS;
                pushEvent(event);
            }
            break;
    
          case DestroyNotify:
            // We already received WM_DELETE_WINDOW
            break;
    
          case ClientMessage:
            if (xEvent.xclient.message_type == WM_PROTOCOLS &&
                static_cast<Atom>(xEvent.xclient.data.l[0]) == WM_DELETE_WINDOW)
            {
                Event event;
                event.Type = Event::EVENT_CLOSED;
                pushEvent(event);
            }
            else if (xEvent.xclient.message_type == TEST_EVENT)
            {
                Event event;
                event.Type = Event::EVENT_TEST;
                pushEvent(event);
            }
            break;
        }
    }