1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci* Copyright 2016 Google Inc.
3cb93a386Sopenharmony_ci*
4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci* f 49
6cb93a386Sopenharmony_ci* Prev
7cb93a386Sopenharmony_ci* Up
8cb93a386Sopenharmony_ci*
9cb93a386Sopenharmony_ci*
10cb93a386Sopenharmony_ci* found in the LICENSE file.
11cb93a386Sopenharmony_ci*/
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_ci//#include <tchar.h>
14cb93a386Sopenharmony_ci
15cb93a386Sopenharmony_ci#include "tools/sk_app/unix/WindowContextFactory_unix.h"
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci#include "src/utils/SkUTF.h"
18cb93a386Sopenharmony_ci#include "tools/sk_app/GLWindowContext.h"
19cb93a386Sopenharmony_ci#include "tools/sk_app/unix/Window_unix.h"
20cb93a386Sopenharmony_ci#include "tools/skui/ModifierKey.h"
21cb93a386Sopenharmony_ci#include "tools/timer/Timer.h"
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ciextern "C" {
24cb93a386Sopenharmony_ci    #include "tools/sk_app/unix/keysym2ucs.h"
25cb93a386Sopenharmony_ci}
26cb93a386Sopenharmony_ci#include <X11/Xatom.h>
27cb93a386Sopenharmony_ci#include <X11/Xutil.h>
28cb93a386Sopenharmony_ci#include <X11/XKBlib.h>
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_cinamespace sk_app {
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ciSkTDynamicHash<Window_unix, XWindow> Window_unix::gWindowMap;
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ciWindow* Window::CreateNativeWindow(void* platformData) {
35cb93a386Sopenharmony_ci    Display* display = (Display*)platformData;
36cb93a386Sopenharmony_ci    SkASSERT(display);
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci    Window_unix* window = new Window_unix();
39cb93a386Sopenharmony_ci    if (!window->initWindow(display)) {
40cb93a386Sopenharmony_ci        delete window;
41cb93a386Sopenharmony_ci        return nullptr;
42cb93a386Sopenharmony_ci    }
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci    return window;
45cb93a386Sopenharmony_ci}
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ciconst long kEventMask = ExposureMask | StructureNotifyMask |
48cb93a386Sopenharmony_ci                        KeyPressMask | KeyReleaseMask |
49cb93a386Sopenharmony_ci                        PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_cibool Window_unix::initWindow(Display* display) {
52cb93a386Sopenharmony_ci    if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) {
53cb93a386Sopenharmony_ci        this->closeWindow();
54cb93a386Sopenharmony_ci    }
55cb93a386Sopenharmony_ci    // we already have a window
56cb93a386Sopenharmony_ci    if (fDisplay) {
57cb93a386Sopenharmony_ci        return true;
58cb93a386Sopenharmony_ci    }
59cb93a386Sopenharmony_ci    fDisplay = display;
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    constexpr int initialWidth = 1280;
62cb93a386Sopenharmony_ci    constexpr int initialHeight = 960;
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci    // Attempt to create a window that supports GL
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci    // We prefer the more recent glXChooseFBConfig but fall back to glXChooseVisual. They have
67cb93a386Sopenharmony_ci    // slight differences in how attributes are specified.
68cb93a386Sopenharmony_ci    static int constexpr kChooseFBConfigAtt[] = {
69cb93a386Sopenharmony_ci        GLX_RENDER_TYPE, GLX_RGBA_BIT,
70cb93a386Sopenharmony_ci        GLX_DOUBLEBUFFER, True,
71cb93a386Sopenharmony_ci        GLX_STENCIL_SIZE, 8,
72cb93a386Sopenharmony_ci        None
73cb93a386Sopenharmony_ci    };
74cb93a386Sopenharmony_ci    // For some reason glXChooseVisual takes a non-const pointer to the attributes.
75cb93a386Sopenharmony_ci    int chooseVisualAtt[] = {
76cb93a386Sopenharmony_ci        GLX_RGBA,
77cb93a386Sopenharmony_ci        GLX_DOUBLEBUFFER,
78cb93a386Sopenharmony_ci        GLX_STENCIL_SIZE, 8,
79cb93a386Sopenharmony_ci        None
80cb93a386Sopenharmony_ci    };
81cb93a386Sopenharmony_ci    SkASSERT(nullptr == fVisualInfo);
82cb93a386Sopenharmony_ci    if (fRequestedDisplayParams.fMSAASampleCount > 1) {
83cb93a386Sopenharmony_ci        static const GLint kChooseFBConifgAttCnt = SK_ARRAY_COUNT(kChooseFBConfigAtt);
84cb93a386Sopenharmony_ci        GLint msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 4];
85cb93a386Sopenharmony_ci        memcpy(msaaChooseFBConfigAtt, kChooseFBConfigAtt, sizeof(kChooseFBConfigAtt));
86cb93a386Sopenharmony_ci        SkASSERT(None == msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1]);
87cb93a386Sopenharmony_ci        msaaChooseFBConfigAtt[kChooseFBConifgAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
88cb93a386Sopenharmony_ci        msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 0] = 1;
89cb93a386Sopenharmony_ci        msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 1] = GLX_SAMPLES_ARB;
90cb93a386Sopenharmony_ci        msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 2] = fRequestedDisplayParams.fMSAASampleCount;
91cb93a386Sopenharmony_ci        msaaChooseFBConfigAtt[kChooseFBConifgAttCnt + 3] = None;
92cb93a386Sopenharmony_ci        int n;
93cb93a386Sopenharmony_ci        fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), msaaChooseFBConfigAtt, &n);
94cb93a386Sopenharmony_ci        if (n > 0) {
95cb93a386Sopenharmony_ci            fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
96cb93a386Sopenharmony_ci        } else {
97cb93a386Sopenharmony_ci            static const GLint kChooseVisualAttCnt = SK_ARRAY_COUNT(chooseVisualAtt);
98cb93a386Sopenharmony_ci            GLint msaaChooseVisualAtt[kChooseVisualAttCnt + 4];
99cb93a386Sopenharmony_ci            memcpy(msaaChooseVisualAtt, chooseVisualAtt, sizeof(chooseVisualAtt));
100cb93a386Sopenharmony_ci            SkASSERT(None == msaaChooseVisualAtt[kChooseVisualAttCnt - 1]);
101cb93a386Sopenharmony_ci            msaaChooseFBConfigAtt[kChooseVisualAttCnt - 1] = GLX_SAMPLE_BUFFERS_ARB;
102cb93a386Sopenharmony_ci            msaaChooseFBConfigAtt[kChooseVisualAttCnt + 0] = 1;
103cb93a386Sopenharmony_ci            msaaChooseFBConfigAtt[kChooseVisualAttCnt + 1] = GLX_SAMPLES_ARB;
104cb93a386Sopenharmony_ci            msaaChooseFBConfigAtt[kChooseVisualAttCnt + 2] =
105cb93a386Sopenharmony_ci                    fRequestedDisplayParams.fMSAASampleCount;
106cb93a386Sopenharmony_ci            msaaChooseFBConfigAtt[kChooseVisualAttCnt + 3] = None;
107cb93a386Sopenharmony_ci            fVisualInfo = glXChooseVisual(display, DefaultScreen(display), msaaChooseVisualAtt);
108cb93a386Sopenharmony_ci            fFBConfig = nullptr;
109cb93a386Sopenharmony_ci        }
110cb93a386Sopenharmony_ci    }
111cb93a386Sopenharmony_ci    if (nullptr == fVisualInfo) {
112cb93a386Sopenharmony_ci        int n;
113cb93a386Sopenharmony_ci        fFBConfig = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay), kChooseFBConfigAtt, &n);
114cb93a386Sopenharmony_ci        if (n > 0) {
115cb93a386Sopenharmony_ci            fVisualInfo = glXGetVisualFromFBConfig(fDisplay, *fFBConfig);
116cb93a386Sopenharmony_ci        } else {
117cb93a386Sopenharmony_ci            fVisualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
118cb93a386Sopenharmony_ci            fFBConfig = nullptr;
119cb93a386Sopenharmony_ci        }
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    if (fVisualInfo) {
123cb93a386Sopenharmony_ci        Colormap colorMap = XCreateColormap(display,
124cb93a386Sopenharmony_ci                                            RootWindow(display, fVisualInfo->screen),
125cb93a386Sopenharmony_ci                                            fVisualInfo->visual,
126cb93a386Sopenharmony_ci                                            AllocNone);
127cb93a386Sopenharmony_ci        XSetWindowAttributes swa;
128cb93a386Sopenharmony_ci        swa.colormap = colorMap;
129cb93a386Sopenharmony_ci        swa.event_mask = kEventMask;
130cb93a386Sopenharmony_ci        fWindow = XCreateWindow(display,
131cb93a386Sopenharmony_ci                                RootWindow(display, fVisualInfo->screen),
132cb93a386Sopenharmony_ci                                0, 0, // x, y
133cb93a386Sopenharmony_ci                                initialWidth, initialHeight,
134cb93a386Sopenharmony_ci                                0, // border width
135cb93a386Sopenharmony_ci                                fVisualInfo->depth,
136cb93a386Sopenharmony_ci                                InputOutput,
137cb93a386Sopenharmony_ci                                fVisualInfo->visual,
138cb93a386Sopenharmony_ci                                CWEventMask | CWColormap,
139cb93a386Sopenharmony_ci                                &swa);
140cb93a386Sopenharmony_ci    } else {
141cb93a386Sopenharmony_ci        // Create a simple window instead.  We will not be able to show GL
142cb93a386Sopenharmony_ci        fWindow = XCreateSimpleWindow(display,
143cb93a386Sopenharmony_ci                                      DefaultRootWindow(display),
144cb93a386Sopenharmony_ci                                      0, 0,  // x, y
145cb93a386Sopenharmony_ci                                      initialWidth, initialHeight,
146cb93a386Sopenharmony_ci                                      0,     // border width
147cb93a386Sopenharmony_ci                                      0,     // border value
148cb93a386Sopenharmony_ci                                      0);    // background value
149cb93a386Sopenharmony_ci        XSelectInput(display, fWindow, kEventMask);
150cb93a386Sopenharmony_ci    }
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ci    if (!fWindow) {
153cb93a386Sopenharmony_ci        return false;
154cb93a386Sopenharmony_ci    }
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount;
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci    // set up to catch window delete message
159cb93a386Sopenharmony_ci    fWmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
160cb93a386Sopenharmony_ci    XSetWMProtocols(display, fWindow, &fWmDeleteMessage, 1);
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci    // add to hashtable of windows
163cb93a386Sopenharmony_ci    gWindowMap.add(this);
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci    // init event variables
166cb93a386Sopenharmony_ci    fPendingPaint = false;
167cb93a386Sopenharmony_ci    fPendingResize = false;
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci    return true;
170cb93a386Sopenharmony_ci}
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_civoid Window_unix::closeWindow() {
173cb93a386Sopenharmony_ci    if (fDisplay) {
174cb93a386Sopenharmony_ci        this->detach();
175cb93a386Sopenharmony_ci        if (fGC) {
176cb93a386Sopenharmony_ci            XFreeGC(fDisplay, fGC);
177cb93a386Sopenharmony_ci            fGC = nullptr;
178cb93a386Sopenharmony_ci        }
179cb93a386Sopenharmony_ci        gWindowMap.remove(fWindow);
180cb93a386Sopenharmony_ci        XDestroyWindow(fDisplay, fWindow);
181cb93a386Sopenharmony_ci        fWindow = 0;
182cb93a386Sopenharmony_ci        if (fFBConfig) {
183cb93a386Sopenharmony_ci            XFree(fFBConfig);
184cb93a386Sopenharmony_ci            fFBConfig = nullptr;
185cb93a386Sopenharmony_ci        }
186cb93a386Sopenharmony_ci        if (fVisualInfo) {
187cb93a386Sopenharmony_ci            XFree(fVisualInfo);
188cb93a386Sopenharmony_ci            fVisualInfo = nullptr;
189cb93a386Sopenharmony_ci        }
190cb93a386Sopenharmony_ci        fDisplay = nullptr;
191cb93a386Sopenharmony_ci    }
192cb93a386Sopenharmony_ci}
193cb93a386Sopenharmony_ci
194cb93a386Sopenharmony_cistatic skui::Key get_key(KeySym keysym) {
195cb93a386Sopenharmony_ci    static const struct {
196cb93a386Sopenharmony_ci        KeySym      fXK;
197cb93a386Sopenharmony_ci        skui::Key fKey;
198cb93a386Sopenharmony_ci    } gPair[] = {
199cb93a386Sopenharmony_ci        { XK_BackSpace, skui::Key::kBack     },
200cb93a386Sopenharmony_ci        { XK_Clear,     skui::Key::kBack     },
201cb93a386Sopenharmony_ci        { XK_Return,    skui::Key::kOK       },
202cb93a386Sopenharmony_ci        { XK_Up,        skui::Key::kUp       },
203cb93a386Sopenharmony_ci        { XK_Down,      skui::Key::kDown     },
204cb93a386Sopenharmony_ci        { XK_Left,      skui::Key::kLeft     },
205cb93a386Sopenharmony_ci        { XK_Right,     skui::Key::kRight    },
206cb93a386Sopenharmony_ci        { XK_Tab,       skui::Key::kTab      },
207cb93a386Sopenharmony_ci        { XK_Page_Up,   skui::Key::kPageUp   },
208cb93a386Sopenharmony_ci        { XK_Page_Down, skui::Key::kPageDown },
209cb93a386Sopenharmony_ci        { XK_Home,      skui::Key::kHome     },
210cb93a386Sopenharmony_ci        { XK_End,       skui::Key::kEnd      },
211cb93a386Sopenharmony_ci        { XK_Delete,    skui::Key::kDelete   },
212cb93a386Sopenharmony_ci        { XK_Escape,    skui::Key::kEscape   },
213cb93a386Sopenharmony_ci        { XK_Shift_L,   skui::Key::kShift    },
214cb93a386Sopenharmony_ci        { XK_Shift_R,   skui::Key::kShift    },
215cb93a386Sopenharmony_ci        { XK_Control_L, skui::Key::kCtrl     },
216cb93a386Sopenharmony_ci        { XK_Control_R, skui::Key::kCtrl     },
217cb93a386Sopenharmony_ci        { XK_Alt_L,     skui::Key::kOption   },
218cb93a386Sopenharmony_ci        { XK_Alt_R,     skui::Key::kOption   },
219cb93a386Sopenharmony_ci        { 'a',          skui::Key::kA        },
220cb93a386Sopenharmony_ci        { 'c',          skui::Key::kC        },
221cb93a386Sopenharmony_ci        { 'v',          skui::Key::kV        },
222cb93a386Sopenharmony_ci        { 'x',          skui::Key::kX        },
223cb93a386Sopenharmony_ci        { 'y',          skui::Key::kY        },
224cb93a386Sopenharmony_ci        { 'z',          skui::Key::kZ        },
225cb93a386Sopenharmony_ci    };
226cb93a386Sopenharmony_ci    for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
227cb93a386Sopenharmony_ci        if (gPair[i].fXK == keysym) {
228cb93a386Sopenharmony_ci            return gPair[i].fKey;
229cb93a386Sopenharmony_ci        }
230cb93a386Sopenharmony_ci    }
231cb93a386Sopenharmony_ci    return skui::Key::kNONE;
232cb93a386Sopenharmony_ci}
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_cistatic skui::ModifierKey get_modifiers(const XEvent& event) {
235cb93a386Sopenharmony_ci    static const struct {
236cb93a386Sopenharmony_ci        unsigned    fXMask;
237cb93a386Sopenharmony_ci        skui::ModifierKey  fSkMask;
238cb93a386Sopenharmony_ci    } gModifiers[] = {
239cb93a386Sopenharmony_ci        { ShiftMask,   skui::ModifierKey::kShift },
240cb93a386Sopenharmony_ci        { ControlMask, skui::ModifierKey::kControl },
241cb93a386Sopenharmony_ci        { Mod1Mask,    skui::ModifierKey::kOption },
242cb93a386Sopenharmony_ci    };
243cb93a386Sopenharmony_ci
244cb93a386Sopenharmony_ci    skui::ModifierKey modifiers = skui::ModifierKey::kNone;
245cb93a386Sopenharmony_ci    for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
246cb93a386Sopenharmony_ci        if (event.xkey.state & gModifiers[i].fXMask) {
247cb93a386Sopenharmony_ci            modifiers |= gModifiers[i].fSkMask;
248cb93a386Sopenharmony_ci        }
249cb93a386Sopenharmony_ci    }
250cb93a386Sopenharmony_ci    return modifiers;
251cb93a386Sopenharmony_ci}
252cb93a386Sopenharmony_ci
253cb93a386Sopenharmony_cibool Window_unix::handleEvent(const XEvent& event) {
254cb93a386Sopenharmony_ci    switch (event.type) {
255cb93a386Sopenharmony_ci        case MapNotify:
256cb93a386Sopenharmony_ci            if (!fGC) {
257cb93a386Sopenharmony_ci                fGC = XCreateGC(fDisplay, fWindow, 0, nullptr);
258cb93a386Sopenharmony_ci            }
259cb93a386Sopenharmony_ci            break;
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci        case ClientMessage:
262cb93a386Sopenharmony_ci            if ((Atom)event.xclient.data.l[0] == fWmDeleteMessage &&
263cb93a386Sopenharmony_ci                gWindowMap.count() == 1) {
264cb93a386Sopenharmony_ci                return true;
265cb93a386Sopenharmony_ci            }
266cb93a386Sopenharmony_ci            break;
267cb93a386Sopenharmony_ci
268cb93a386Sopenharmony_ci        case ButtonPress:
269cb93a386Sopenharmony_ci            switch (event.xbutton.button) {
270cb93a386Sopenharmony_ci                case Button1:
271cb93a386Sopenharmony_ci                    this->onMouse(event.xbutton.x, event.xbutton.y,
272cb93a386Sopenharmony_ci                                  skui::InputState::kDown, get_modifiers(event));
273cb93a386Sopenharmony_ci                    break;
274cb93a386Sopenharmony_ci                case Button4:
275cb93a386Sopenharmony_ci                    this->onMouseWheel(1.0f, get_modifiers(event));
276cb93a386Sopenharmony_ci                    break;
277cb93a386Sopenharmony_ci                case Button5:
278cb93a386Sopenharmony_ci                    this->onMouseWheel(-1.0f, get_modifiers(event));
279cb93a386Sopenharmony_ci                    break;
280cb93a386Sopenharmony_ci            }
281cb93a386Sopenharmony_ci            break;
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci        case ButtonRelease:
284cb93a386Sopenharmony_ci            if (event.xbutton.button == Button1) {
285cb93a386Sopenharmony_ci                this->onMouse(event.xbutton.x, event.xbutton.y,
286cb93a386Sopenharmony_ci                              skui::InputState::kUp, get_modifiers(event));
287cb93a386Sopenharmony_ci            }
288cb93a386Sopenharmony_ci            break;
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ci        case MotionNotify:
291cb93a386Sopenharmony_ci            this->onMouse(event.xmotion.x, event.xmotion.y,
292cb93a386Sopenharmony_ci                          skui::InputState::kMove, get_modifiers(event));
293cb93a386Sopenharmony_ci            break;
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci        case KeyPress: {
296cb93a386Sopenharmony_ci            int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
297cb93a386Sopenharmony_ci            KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode, 0, shiftLevel);
298cb93a386Sopenharmony_ci            skui::Key key = get_key(keysym);
299cb93a386Sopenharmony_ci            if (key != skui::Key::kNONE) {
300cb93a386Sopenharmony_ci                if (!this->onKey(key, skui::InputState::kDown, get_modifiers(event))) {
301cb93a386Sopenharmony_ci                    if (keysym == XK_Escape) {
302cb93a386Sopenharmony_ci                        return true;
303cb93a386Sopenharmony_ci                    }
304cb93a386Sopenharmony_ci                }
305cb93a386Sopenharmony_ci            }
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci            long uni = keysym2ucs(keysym);
308cb93a386Sopenharmony_ci            if (uni != -1) {
309cb93a386Sopenharmony_ci                (void) this->onChar((SkUnichar) uni, get_modifiers(event));
310cb93a386Sopenharmony_ci            }
311cb93a386Sopenharmony_ci        } break;
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_ci        case KeyRelease: {
314cb93a386Sopenharmony_ci            int shiftLevel = (event.xkey.state & ShiftMask) ? 1 : 0;
315cb93a386Sopenharmony_ci            KeySym keysym = XkbKeycodeToKeysym(fDisplay, event.xkey.keycode,
316cb93a386Sopenharmony_ci                                               0, shiftLevel);
317cb93a386Sopenharmony_ci            skui::Key key = get_key(keysym);
318cb93a386Sopenharmony_ci            (void) this->onKey(key, skui::InputState::kUp,
319cb93a386Sopenharmony_ci                               get_modifiers(event));
320cb93a386Sopenharmony_ci        } break;
321cb93a386Sopenharmony_ci
322cb93a386Sopenharmony_ci        case SelectionClear: {
323cb93a386Sopenharmony_ci            // Lost selection ownership
324cb93a386Sopenharmony_ci            fClipboardText.clear();
325cb93a386Sopenharmony_ci        } break;
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_ci        case SelectionRequest: {
328cb93a386Sopenharmony_ci            Atom UTF8      = XInternAtom(fDisplay, "UTF8_STRING", 0),
329cb93a386Sopenharmony_ci                 CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0);
330cb93a386Sopenharmony_ci
331cb93a386Sopenharmony_ci            const XSelectionRequestEvent* xsr = &event.xselectionrequest;
332cb93a386Sopenharmony_ci
333cb93a386Sopenharmony_ci            XSelectionEvent xsel = {};
334cb93a386Sopenharmony_ci            xsel.type      = SelectionNotify;
335cb93a386Sopenharmony_ci            xsel.requestor = xsr->requestor;
336cb93a386Sopenharmony_ci            xsel.selection = xsr->selection;
337cb93a386Sopenharmony_ci            xsel.target    = xsr->target;
338cb93a386Sopenharmony_ci            xsel.property  = xsr->property;
339cb93a386Sopenharmony_ci            xsel.time      = xsr->time;
340cb93a386Sopenharmony_ci
341cb93a386Sopenharmony_ci            if (xsr->selection != CLIPBOARD) {
342cb93a386Sopenharmony_ci                // A request for a different kind of selection. This shouldn't happen.
343cb93a386Sopenharmony_ci                break;
344cb93a386Sopenharmony_ci            }
345cb93a386Sopenharmony_ci
346cb93a386Sopenharmony_ci            if (fClipboardText.empty() || xsr->target != UTF8 || xsr->property == None) {
347cb93a386Sopenharmony_ci                // We can't fulfill this request. Deny it.
348cb93a386Sopenharmony_ci                xsel.property = None;
349cb93a386Sopenharmony_ci                XSendEvent(fDisplay, xsr->requestor, True, NoEventMask, (XEvent*)&xsel);
350cb93a386Sopenharmony_ci            } else {
351cb93a386Sopenharmony_ci                // We can fulfill this request! Update the contents of the CLIPBOARD property,
352cb93a386Sopenharmony_ci                // and let the requestor know.
353cb93a386Sopenharmony_ci                XChangeProperty(fDisplay, xsr->requestor, xsr->property, UTF8, /*format=*/8,
354cb93a386Sopenharmony_ci                                PropModeReplace, (unsigned char*)fClipboardText.data(),
355cb93a386Sopenharmony_ci                                fClipboardText.length());
356cb93a386Sopenharmony_ci                XSendEvent(fDisplay, xsr->requestor, True, NoEventMask, (XEvent*)&xsel);
357cb93a386Sopenharmony_ci            }
358cb93a386Sopenharmony_ci        } break;
359cb93a386Sopenharmony_ci
360cb93a386Sopenharmony_ci        default:
361cb93a386Sopenharmony_ci            // these events should be handled in the main event loop
362cb93a386Sopenharmony_ci            SkASSERT(event.type != Expose && event.type != ConfigureNotify);
363cb93a386Sopenharmony_ci            break;
364cb93a386Sopenharmony_ci    }
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ci    return false;
367cb93a386Sopenharmony_ci}
368cb93a386Sopenharmony_ci
369cb93a386Sopenharmony_civoid Window_unix::setTitle(const char* title) {
370cb93a386Sopenharmony_ci    XTextProperty textproperty;
371cb93a386Sopenharmony_ci    if (!XStringListToTextProperty(const_cast<char**>(&title), 1, &textproperty)) {
372cb93a386Sopenharmony_ci        return;
373cb93a386Sopenharmony_ci    }
374cb93a386Sopenharmony_ci    XSetWMName(fDisplay, fWindow, &textproperty);
375cb93a386Sopenharmony_ci    XFree(textproperty.value);
376cb93a386Sopenharmony_ci}
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_civoid Window_unix::show() {
379cb93a386Sopenharmony_ci    XMapWindow(fDisplay, fWindow);
380cb93a386Sopenharmony_ci}
381cb93a386Sopenharmony_ci
382cb93a386Sopenharmony_cibool Window_unix::attach(BackendType attachType) {
383cb93a386Sopenharmony_ci    fBackend = attachType;
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_ci    this->initWindow(fDisplay);
386cb93a386Sopenharmony_ci
387cb93a386Sopenharmony_ci    window_context_factory::XlibWindowInfo winInfo;
388cb93a386Sopenharmony_ci    winInfo.fDisplay = fDisplay;
389cb93a386Sopenharmony_ci    winInfo.fWindow = fWindow;
390cb93a386Sopenharmony_ci    winInfo.fFBConfig = fFBConfig;
391cb93a386Sopenharmony_ci    winInfo.fVisualInfo = fVisualInfo;
392cb93a386Sopenharmony_ci
393cb93a386Sopenharmony_ci    XWindowAttributes attrs;
394cb93a386Sopenharmony_ci    if (XGetWindowAttributes(fDisplay, fWindow, &attrs)) {
395cb93a386Sopenharmony_ci        winInfo.fWidth = attrs.width;
396cb93a386Sopenharmony_ci        winInfo.fHeight = attrs.height;
397cb93a386Sopenharmony_ci    } else {
398cb93a386Sopenharmony_ci        winInfo.fWidth = winInfo.fHeight = 0;
399cb93a386Sopenharmony_ci    }
400cb93a386Sopenharmony_ci
401cb93a386Sopenharmony_ci    switch (attachType) {
402cb93a386Sopenharmony_ci#ifdef SK_DAWN
403cb93a386Sopenharmony_ci        case kDawn_BackendType:
404cb93a386Sopenharmony_ci            fWindowContext =
405cb93a386Sopenharmony_ci                    window_context_factory::MakeDawnVulkanForXlib(winInfo, fRequestedDisplayParams);
406cb93a386Sopenharmony_ci            break;
407cb93a386Sopenharmony_ci#endif
408cb93a386Sopenharmony_ci#ifdef SK_VULKAN
409cb93a386Sopenharmony_ci        case kVulkan_BackendType:
410cb93a386Sopenharmony_ci            fWindowContext =
411cb93a386Sopenharmony_ci                    window_context_factory::MakeVulkanForXlib(winInfo, fRequestedDisplayParams);
412cb93a386Sopenharmony_ci            break;
413cb93a386Sopenharmony_ci#endif
414cb93a386Sopenharmony_ci#ifdef SK_GL
415cb93a386Sopenharmony_ci        case kNativeGL_BackendType:
416cb93a386Sopenharmony_ci            fWindowContext =
417cb93a386Sopenharmony_ci                    window_context_factory::MakeGLForXlib(winInfo, fRequestedDisplayParams);
418cb93a386Sopenharmony_ci            break;
419cb93a386Sopenharmony_ci#endif
420cb93a386Sopenharmony_ci        case kRaster_BackendType:
421cb93a386Sopenharmony_ci            fWindowContext =
422cb93a386Sopenharmony_ci                    window_context_factory::MakeRasterForXlib(winInfo, fRequestedDisplayParams);
423cb93a386Sopenharmony_ci            break;
424cb93a386Sopenharmony_ci    }
425cb93a386Sopenharmony_ci    this->onBackendCreated();
426cb93a386Sopenharmony_ci
427cb93a386Sopenharmony_ci    return (SkToBool(fWindowContext));
428cb93a386Sopenharmony_ci}
429cb93a386Sopenharmony_ci
430cb93a386Sopenharmony_civoid Window_unix::onInval() {
431cb93a386Sopenharmony_ci    XEvent event;
432cb93a386Sopenharmony_ci    event.type = Expose;
433cb93a386Sopenharmony_ci    event.xexpose.send_event = True;
434cb93a386Sopenharmony_ci    event.xexpose.display = fDisplay;
435cb93a386Sopenharmony_ci    event.xexpose.window = fWindow;
436cb93a386Sopenharmony_ci    event.xexpose.x = 0;
437cb93a386Sopenharmony_ci    event.xexpose.y = 0;
438cb93a386Sopenharmony_ci    event.xexpose.width = this->width();
439cb93a386Sopenharmony_ci    event.xexpose.height = this->height();
440cb93a386Sopenharmony_ci    event.xexpose.count = 0;
441cb93a386Sopenharmony_ci
442cb93a386Sopenharmony_ci    XSendEvent(fDisplay, fWindow, False, 0, &event);
443cb93a386Sopenharmony_ci}
444cb93a386Sopenharmony_ci
445cb93a386Sopenharmony_civoid Window_unix::setRequestedDisplayParams(const DisplayParams& params, bool allowReattach) {
446cb93a386Sopenharmony_ci#if defined(SK_VULKAN)
447cb93a386Sopenharmony_ci    // Vulkan on unix crashes if we try to reinitialize the vulkan context without remaking the
448cb93a386Sopenharmony_ci    // window.
449cb93a386Sopenharmony_ci    if (fBackend == kVulkan_BackendType && allowReattach) {
450cb93a386Sopenharmony_ci        // Need to change these early, so attach() creates the window context correctly
451cb93a386Sopenharmony_ci        fRequestedDisplayParams = params;
452cb93a386Sopenharmony_ci
453cb93a386Sopenharmony_ci        this->detach();
454cb93a386Sopenharmony_ci        this->attach(fBackend);
455cb93a386Sopenharmony_ci        return;
456cb93a386Sopenharmony_ci    }
457cb93a386Sopenharmony_ci#endif
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_ci    INHERITED::setRequestedDisplayParams(params, allowReattach);
460cb93a386Sopenharmony_ci}
461cb93a386Sopenharmony_ci
462cb93a386Sopenharmony_ciconst char* Window_unix::getClipboardText() {
463cb93a386Sopenharmony_ci    Atom UTF8      = XInternAtom(fDisplay, "UTF8_STRING", 0),
464cb93a386Sopenharmony_ci         CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0),
465cb93a386Sopenharmony_ci         XSEL_DATA = XInternAtom(fDisplay, "XSEL_DATA", 0);
466cb93a386Sopenharmony_ci
467cb93a386Sopenharmony_ci    // Ask for a UTF8 copy of the CLIPBOARD...
468cb93a386Sopenharmony_ci    XEvent event;
469cb93a386Sopenharmony_ci    XConvertSelection(fDisplay, CLIPBOARD, UTF8, XSEL_DATA, fWindow, CurrentTime);
470cb93a386Sopenharmony_ci    XSync(fDisplay, 0);
471cb93a386Sopenharmony_ci    XNextEvent(fDisplay, &event);
472cb93a386Sopenharmony_ci    if (event.type == SelectionNotify &&
473cb93a386Sopenharmony_ci            event.xselection.selection == CLIPBOARD &&
474cb93a386Sopenharmony_ci            event.xselection.property != None) {
475cb93a386Sopenharmony_ci
476cb93a386Sopenharmony_ci        // We got a response
477cb93a386Sopenharmony_ci        Atom type;
478cb93a386Sopenharmony_ci        int format;
479cb93a386Sopenharmony_ci        unsigned long nitems, bytes_after;
480cb93a386Sopenharmony_ci        char* data;
481cb93a386Sopenharmony_ci
482cb93a386Sopenharmony_ci        // Fetch the CLIPBOARD property
483cb93a386Sopenharmony_ci        XSelectionEvent xsel = event.xselection;
484cb93a386Sopenharmony_ci        XGetWindowProperty(xsel.display, xsel.requestor, xsel.property, /*offset=*/0,
485cb93a386Sopenharmony_ci                           /*length=*/~0L, /*delete=*/False, AnyPropertyType, &type, &format,
486cb93a386Sopenharmony_ci                           &nitems, &bytes_after, (unsigned char**)&data);
487cb93a386Sopenharmony_ci        SkASSERT(bytes_after == 0);
488cb93a386Sopenharmony_ci        if (type == UTF8) {
489cb93a386Sopenharmony_ci            fClipboardText.assign(data, nitems);
490cb93a386Sopenharmony_ci        }
491cb93a386Sopenharmony_ci        XFree(data);
492cb93a386Sopenharmony_ci        XDeleteProperty(xsel.display, xsel.requestor, xsel.property);
493cb93a386Sopenharmony_ci    }
494cb93a386Sopenharmony_ci    return fClipboardText.c_str();
495cb93a386Sopenharmony_ci}
496cb93a386Sopenharmony_ci
497cb93a386Sopenharmony_civoid Window_unix::setClipboardText(const char* text) {
498cb93a386Sopenharmony_ci    fClipboardText.assign(text);
499cb93a386Sopenharmony_ci
500cb93a386Sopenharmony_ci    // Take ownership of the CLIPBOARD
501cb93a386Sopenharmony_ci    Atom CLIPBOARD = XInternAtom(fDisplay, "CLIPBOARD", 0);
502cb93a386Sopenharmony_ci    XSetSelectionOwner(fDisplay, CLIPBOARD, fWindow, CurrentTime);
503cb93a386Sopenharmony_ci}
504cb93a386Sopenharmony_ci
505cb93a386Sopenharmony_ci}   // namespace sk_app
506