yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
16.4 KiB548 linesraw
1#ifdef SLANG_ENABLE_XLIB
2
3#include "../window.h"
4
5#include <X11/Xlib.h>
6#include <X11/Xresource.h>
7#include <X11/Xutil.h>
8
9#ifdef None
10#undef None
11#endif
12
13#include "core/slang-basic.h"
14
15using namespace Slang;
16
17namespace platform
18{
19typedef ::Window X11WindowHandle;
20class X11PlatformWindow;
21
22void initKeyCodeTranslationTable(Display* display);
23void freeKeyCodeTranslationTable();
24KeyCode translateKeyCode(int keyCode);
25int getKeyChar(KeyCode keyCode, int keyState);
26
27const int kKeyStateTableSize = 256;
28
29enum class KeyState
30{
31    Released,
32    Pressed,
33    Hold
34};
35
36enum class KeyEvent
37{
38    Press,
39    Release
40};
41
42enum class MouseEvent
43{
44    Move,
45    Down,
46    Up,
47    Scroll
48};
49
50class X11AppContext
51{
52public:
53    static bool isTerminated;
54    static RefPtr<Window> mainWindow;
55    static OrderedDictionary<X11WindowHandle, X11PlatformWindow*> windows;
56    static X11WindowHandle mainWindowHandle;
57    static Display* xdisplay;
58    static KeyState keyStates[kKeyStateTableSize];
59    static X11PlatformWindow* currentMouseEventWindow;
60};
61
62bool X11AppContext::isTerminated = false;
63RefPtr<Window> X11AppContext::mainWindow;
64OrderedDictionary<X11WindowHandle, X11PlatformWindow*> X11AppContext::windows;
65X11WindowHandle X11AppContext::mainWindowHandle;
66Display* X11AppContext::xdisplay = nullptr;
67KeyState X11AppContext::keyStates[kKeyStateTableSize] = {};
68X11PlatformWindow* X11AppContext::currentMouseEventWindow = nullptr;
69
70void Application::init() {}
71
72static void doEventsImpl(bool waitForEvents);
73
74void Application::doEvents()
75{
76    doEventsImpl(false);
77}
78
79void Application::quit()
80{
81    X11AppContext::isTerminated = true;
82}
83
84void Application::dispose()
85{
86    X11AppContext::mainWindow = nullptr;
87    X11AppContext::windows = decltype(X11AppContext::windows)();
88    freeKeyCodeTranslationTable();
89}
90
91void Application::run(Window* mainWindow, bool waitForEvents)
92{
93    if (mainWindow)
94    {
95        X11AppContext::mainWindow = mainWindow;
96        X11AppContext::mainWindowHandle =
97            (X11WindowHandle)mainWindow->getNativeHandle().handleValues[1];
98        mainWindow->show();
99        while (!X11AppContext::isTerminated)
100        {
101            doEventsImpl(waitForEvents);
102            if (!X11AppContext::isTerminated)
103                mainWindow->events.mainLoop();
104        }
105    }
106}
107
108class X11PlatformWindow : public Window
109{
110public:
111    X11WindowHandle handle;
112    bool visible = false;
113    int currentWidth = 0;
114    int currentHeight = 0;
115    bool fixedSized = false;
116    X11PlatformWindow(const WindowDesc& desc)
117    {
118        currentWidth = desc.width;
119        currentHeight = desc.height;
120
121        int blackColor =
122            BlackPixel(X11AppContext::xdisplay, DefaultScreen(X11AppContext::xdisplay));
123        int whiteColor =
124            WhitePixel(X11AppContext::xdisplay, DefaultScreen(X11AppContext::xdisplay));
125        handle = XCreateSimpleWindow(
126            X11AppContext::xdisplay,
127            DefaultRootWindow(X11AppContext::xdisplay),
128            0,
129            0,
130            desc.width,
131            desc.height,
132            0,
133            blackColor,
134            blackColor);
135        X11AppContext::windows[handle] = this;
136        Atom wmDelete = XInternAtom(X11AppContext::xdisplay, "WM_DELETE_WINDOW", True);
137        XSetWMProtocols(X11AppContext::xdisplay, handle, &wmDelete, 1);
138        XSelectInput(
139            X11AppContext::xdisplay,
140            handle,
141            StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
142                ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask);
143
144        if (desc.style == WindowStyle::FixedSize)
145        {
146            fixedSized = true;
147            setFixedSizeHint(desc.width, desc.height);
148        }
149        setText(desc.title);
150    }
151
152    ~X11PlatformWindow() { close(); }
153
154    void setFixedSizeHint(int w, int h)
155    {
156        auto sizeHints = XAllocSizeHints();
157        sizeHints->flags = PMinSize | PMaxSize;
158        sizeHints->min_width = sizeHints->max_width = w;
159        sizeHints->min_height = sizeHints->max_height = h;
160        XSetWMNormalHints(X11AppContext::xdisplay, handle, sizeHints);
161        XFree(sizeHints);
162    }
163
164    virtual void setClientSize(uint32_t width, uint32_t height) override
165    {
166        if (fixedSized)
167            setFixedSizeHint(width, height);
168        XResizeWindow(X11AppContext::xdisplay, handle, width, height);
169        handleResizeEvent(width, height);
170    }
171
172    virtual Rect getClientRect() override
173    {
174        Rect rect = {};
175        if (!handle)
176            return rect;
177        X11WindowHandle winRoot = 0, winParent = 0;
178        X11WindowHandle* winChildren = nullptr;
179        unsigned int numChilren = 0;
180        XQueryTree(
181            X11AppContext::xdisplay,
182            handle,
183            &winRoot,
184            &winParent,
185            &winChildren,
186            &numChilren);
187        unsigned borderWidth, depth;
188        XGetGeometry(
189            X11AppContext::xdisplay,
190            handle,
191            &winRoot,
192            &rect.x,
193            &rect.y,
194            (uint32_t*)&rect.width,
195            (uint32_t*)&rect.height,
196            &borderWidth,
197            &depth);
198        return rect;
199    }
200
201    virtual void centerScreen() override
202    {
203        auto currentRect = getClientRect();
204        XWindowAttributes attributes;
205        XGetWindowAttributes(X11AppContext::xdisplay, handle, &attributes);
206        int screenWidth = WidthOfScreen(attributes.screen);
207        int screenHeight = HeightOfScreen(attributes.screen);
208        int x = (screenWidth - currentRect.width) / 2;
209        int y = (screenHeight - currentRect.height) / 2;
210        XMoveWindow(X11AppContext::xdisplay, handle, x, y);
211    }
212    virtual void close() override
213    {
214        if (handle)
215        {
216            X11AppContext::windows.remove(handle);
217            XDestroyWindow(X11AppContext::xdisplay, handle);
218            handle = 0;
219        }
220    }
221    virtual bool getFocused() override
222    {
223        if (!handle)
224            return false;
225        int revertTo;
226        X11WindowHandle focusedWindow;
227        XGetInputFocus(X11AppContext::xdisplay, &focusedWindow, &revertTo);
228        return focusedWindow == handle;
229    }
230    virtual WindowHandle getNativeHandle() override
231    {
232        WindowHandle rs;
233        rs.type = WindowHandle::Type::XLibHandle;
234        rs.handleValues[0] = (intptr_t)X11AppContext::xdisplay;
235        rs.handleValues[1] = (intptr_t)handle;
236        return rs;
237    }
238    virtual void setText(String text) override
239    {
240        if (!handle)
241            return;
242        XStoreName(X11AppContext::xdisplay, handle, text.getBuffer());
243        XClassHint* hint = XAllocClassHint();
244        hint->res_class = (char*)"Slang platform window";
245        hint->res_name = (char*)"Slang platform window";
246        XSetClassHint(X11AppContext::xdisplay, handle, hint);
247        XFree(hint);
248    }
249    virtual bool getVisible() override { return visible; }
250    virtual void show() override
251    {
252        XMapWindow(X11AppContext::xdisplay, handle);
253        visible = true;
254    }
255    virtual void hide() override
256    {
257        if (!handle)
258            return;
259        XUnmapWindow(X11AppContext::xdisplay, handle);
260        visible = false;
261    }
262    virtual int getCurrentDpi() override
263    {
264        char* resourceString = XResourceManagerString(X11AppContext::xdisplay);
265        XrmDatabase db;
266        XrmValue value;
267        char* type = NULL;
268        double dpi = 96.0;
269        db = XrmGetStringDatabase(resourceString);
270        if (resourceString)
271        {
272            if (XrmGetResource(db, "Xft.dpi", "String", &type, &value))
273            {
274                if (value.addr)
275                {
276                    dpi = atof(value.addr);
277                }
278            }
279        }
280        return (int)dpi;
281    }
282    void handleResizeEvent(int w, int h)
283    {
284        if (w != currentWidth || h != currentHeight)
285        {
286            currentWidth = w;
287            currentHeight = h;
288            events.sizeChanged();
289        }
290    }
291
292    static void addButtonState(ButtonState::Enum& state, ButtonState::Enum newState)
293    {
294        state = ButtonState::Enum((int)state | (int)newState);
295    }
296
297    ButtonState::Enum getButtonState(int state)
298    {
299        ButtonState::Enum buttonState = ButtonState::Enum::None;
300        if (state & ShiftMask)
301            addButtonState(buttonState, ButtonState::Enum::Shift);
302        if (state & ControlMask)
303            addButtonState(buttonState, ButtonState::Enum::Control);
304        if (state & Mod1Mask)
305            addButtonState(buttonState, ButtonState::Enum::Alt);
306        if (state & Button1Mask)
307            addButtonState(buttonState, ButtonState::Enum::LeftButton);
308        if (state & Button2Mask)
309            addButtonState(buttonState, ButtonState::Enum::MiddleButton);
310        if (state & Button3Mask)
311            addButtonState(buttonState, ButtonState::Enum::RightButton);
312        return buttonState;
313    }
314
315    void handleKeyEvent(KeyEvent eventType, KeyCode keyCode, int keyChar, int state)
316    {
317        KeyEventArgs e;
318        e.buttons = getButtonState(state);
319        e.cancelEvent = false;
320        e.key = keyCode;
321        e.keyChar = keyChar;
322        if (eventType == KeyEvent::Press)
323        {
324            events.keyDown(e);
325            if (keyChar)
326                events.keyPress(e);
327        }
328        else
329        {
330            events.keyUp(e);
331        }
332    }
333
334    void handleMouseEvent(
335        MouseEvent eventType,
336        int x,
337        int y,
338        int delta,
339        int button,
340        int state,
341        unsigned long time)
342    {
343        auto buttonState = getButtonState(state);
344        if (button == Button1)
345            addButtonState(buttonState, ButtonState::Enum::LeftButton);
346        else if (button == Button2)
347            addButtonState(buttonState, ButtonState::Enum::MiddleButton);
348        else if (button == Button3)
349            addButtonState(buttonState, ButtonState::Enum::RightButton);
350        MouseEventArgs e;
351        e.buttons = buttonState;
352        e.delta = delta;
353        e.x = x;
354        e.y = y;
355
356        switch (eventType)
357        {
358        case MouseEvent::Down:
359            events.mouseDown(e);
360            break;
361        case MouseEvent::Up:
362            events.mouseUp(e);
363            break;
364        case MouseEvent::Move:
365            events.mouseMove(e);
366            break;
367        case MouseEvent::Scroll:
368            events.mouseWheel(e);
369            break;
370        default:
371            break;
372        }
373    }
374
375    void handleCloseEvent()
376    {
377        hide();
378        if (X11AppContext::mainWindowHandle == handle)
379            Application::quit();
380    }
381
382    void handleExposeEvent() {}
383
384    void handleFocus(bool focus) {}
385};
386
387Window* Application::createWindow(const WindowDesc& desc)
388{
389    if (!X11AppContext::xdisplay)
390    {
391        XInitThreads();
392        XrmInitialize();
393        X11AppContext::xdisplay = XOpenDisplay(nullptr);
394        initKeyCodeTranslationTable(X11AppContext::xdisplay);
395        if (!X11AppContext::xdisplay)
396            printf("Failed to open XDisplay.\n");
397    }
398    return new X11PlatformWindow(desc);
399}
400
401void doEventsImpl(bool waitForEvents)
402{
403    auto xdisplay = X11AppContext::xdisplay;
404    if (!X11AppContext::xdisplay)
405        return;
406
407    static bool supressInvokeTasks = false;
408    X11PlatformWindow* sysWindow = nullptr;
409    KeyCode vKeyCode = KeyCode::None;
410    int iKeyCode = 0;
411    while (XPending(xdisplay))
412    {
413        XEvent nextEvent;
414        XNextEvent(xdisplay, &nextEvent);
415        switch (nextEvent.type)
416        {
417        case KeyPress:
418            vKeyCode = translateKeyCode(nextEvent.xkey.keycode);
419            iKeyCode = (int)vKeyCode;
420            if (iKeyCode < kKeyStateTableSize)
421            {
422                if (X11AppContext::keyStates[iKeyCode] == KeyState::Released)
423                    X11AppContext::keyStates[iKeyCode] = KeyState::Pressed;
424                else if (X11AppContext::keyStates[iKeyCode] == KeyState::Pressed)
425                    X11AppContext::keyStates[iKeyCode] = KeyState::Hold;
426            }
427            if (X11AppContext::windows.tryGetValue(nextEvent.xkey.window, sysWindow))
428            {
429                wchar_t keyChar = getKeyChar(vKeyCode, nextEvent.xkey.state);
430                sysWindow->handleKeyEvent(KeyEvent::Press, vKeyCode, keyChar, nextEvent.xkey.state);
431            }
432            break;
433        case KeyRelease:
434            vKeyCode = translateKeyCode(nextEvent.xkey.keycode);
435            iKeyCode = (int)vKeyCode;
436            if (iKeyCode < kKeyStateTableSize)
437            {
438                X11AppContext::keyStates[iKeyCode] = KeyState::Released;
439            }
440            if (X11AppContext::windows.tryGetValue(nextEvent.xkey.window, sysWindow))
441            {
442                sysWindow->handleKeyEvent(KeyEvent::Release, vKeyCode, 0, nextEvent.xkey.state);
443            }
444            break;
445        case MotionNotify:
446            if (X11AppContext::windows.tryGetValue(nextEvent.xmotion.window, sysWindow))
447            {
448                X11AppContext::currentMouseEventWindow = sysWindow;
449                sysWindow->handleMouseEvent(
450                    MouseEvent::Move,
451                    nextEvent.xmotion.x,
452                    nextEvent.xmotion.y,
453                    0,
454                    0,
455                    nextEvent.xmotion.state,
456                    nextEvent.xmotion.time);
457            }
458            break;
459        case ButtonPress:
460            if (X11AppContext::windows.tryGetValue(nextEvent.xbutton.window, sysWindow))
461            {
462                X11AppContext::currentMouseEventWindow = sysWindow;
463                if (nextEvent.xbutton.button <= Button3)
464                    sysWindow->handleMouseEvent(
465                        MouseEvent::Down,
466                        nextEvent.xbutton.x,
467                        nextEvent.xbutton.y,
468                        0,
469                        nextEvent.xbutton.button,
470                        nextEvent.xbutton.state,
471                        nextEvent.xbutton.time);
472                else if (nextEvent.xbutton.button == Button4)
473                    sysWindow->handleMouseEvent(
474                        MouseEvent::Scroll,
475                        nextEvent.xbutton.x,
476                        nextEvent.xbutton.y,
477                        120,
478                        nextEvent.xbutton.button,
479                        nextEvent.xbutton.state,
480                        nextEvent.xbutton.time);
481                else if (nextEvent.xbutton.button == Button5)
482                    sysWindow->handleMouseEvent(
483                        MouseEvent::Scroll,
484                        nextEvent.xbutton.x,
485                        nextEvent.xbutton.y,
486                        -120,
487                        nextEvent.xbutton.button,
488                        nextEvent.xbutton.state,
489                        nextEvent.xbutton.time);
490            }
491            break;
492        case ButtonRelease:
493            if (X11AppContext::windows.tryGetValue(nextEvent.xbutton.window, sysWindow))
494            {
495                X11AppContext::currentMouseEventWindow = sysWindow;
496                sysWindow->handleMouseEvent(
497                    MouseEvent::Up,
498                    nextEvent.xbutton.x,
499                    nextEvent.xbutton.y,
500                    0,
501                    nextEvent.xbutton.button,
502                    nextEvent.xbutton.state,
503                    nextEvent.xbutton.time);
504            }
505            break;
506        case ConfigureNotify:
507            if (X11AppContext::windows.tryGetValue(nextEvent.xconfigure.window, sysWindow))
508            {
509                sysWindow->handleResizeEvent(
510                    nextEvent.xconfigure.width,
511                    nextEvent.xconfigure.height);
512            }
513            break;
514        case Expose:
515            if (X11AppContext::windows.tryGetValue(nextEvent.xexpose.window, sysWindow))
516            {
517                sysWindow->handleExposeEvent();
518            }
519            break;
520        case ClientMessage:
521            if (X11AppContext::windows.tryGetValue(nextEvent.xclient.window, sysWindow))
522            {
523                Atom wmDelete = XInternAtom(X11AppContext::xdisplay, "WM_DELETE_WINDOW", True);
524                if (nextEvent.xclient.data.l[0] == wmDelete)
525                {
526                    sysWindow->handleCloseEvent();
527                }
528            }
529            break;
530        case FocusIn:
531            if (X11AppContext::windows.tryGetValue(nextEvent.xfocus.window, sysWindow))
532            {
533                sysWindow->handleFocus(true);
534            }
535            break;
536        case FocusOut:
537            if (X11AppContext::windows.tryGetValue(nextEvent.xfocus.window, sysWindow))
538            {
539                sysWindow->handleFocus(false);
540            }
541            break;
542        }
543    }
544}
545
546} // namespace platform
547
548#endif