Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/util/x11/X11Window.cpp
1693 views
1
//
2
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
3
// Use of this source code is governed by a BSD-style license that can be
4
// found in the LICENSE file.
5
//
6
7
// X11Window.cpp: Implementation of OSWindow for X11
8
9
#include "util/x11/X11Window.h"
10
11
#include "common/debug.h"
12
#include "util/Timer.h"
13
#include "util/test_utils.h"
14
15
namespace
16
{
17
18
Bool WaitForMapNotify(Display *dpy, XEvent *event, XPointer window)
19
{
20
return event->type == MapNotify && event->xmap.window == reinterpret_cast<Window>(window);
21
}
22
23
static Key X11CodeToKey(Display *display, unsigned int scancode)
24
{
25
int temp;
26
KeySym *keySymbols;
27
keySymbols = XGetKeyboardMapping(display, scancode, 1, &temp);
28
29
KeySym keySymbol = keySymbols[0];
30
XFree(keySymbols);
31
32
switch (keySymbol)
33
{
34
case XK_Shift_L:
35
return KEY_LSHIFT;
36
case XK_Shift_R:
37
return KEY_RSHIFT;
38
case XK_Alt_L:
39
return KEY_LALT;
40
case XK_Alt_R:
41
return KEY_RALT;
42
case XK_Control_L:
43
return KEY_LCONTROL;
44
case XK_Control_R:
45
return KEY_RCONTROL;
46
case XK_Super_L:
47
return KEY_LSYSTEM;
48
case XK_Super_R:
49
return KEY_RSYSTEM;
50
case XK_Menu:
51
return KEY_MENU;
52
53
case XK_semicolon:
54
return KEY_SEMICOLON;
55
case XK_slash:
56
return KEY_SLASH;
57
case XK_equal:
58
return KEY_EQUAL;
59
case XK_minus:
60
return KEY_DASH;
61
case XK_bracketleft:
62
return KEY_LBRACKET;
63
case XK_bracketright:
64
return KEY_RBRACKET;
65
case XK_comma:
66
return KEY_COMMA;
67
case XK_period:
68
return KEY_PERIOD;
69
case XK_backslash:
70
return KEY_BACKSLASH;
71
case XK_asciitilde:
72
return KEY_TILDE;
73
case XK_Escape:
74
return KEY_ESCAPE;
75
case XK_space:
76
return KEY_SPACE;
77
case XK_Return:
78
return KEY_RETURN;
79
case XK_BackSpace:
80
return KEY_BACK;
81
case XK_Tab:
82
return KEY_TAB;
83
case XK_Page_Up:
84
return KEY_PAGEUP;
85
case XK_Page_Down:
86
return KEY_PAGEDOWN;
87
case XK_End:
88
return KEY_END;
89
case XK_Home:
90
return KEY_HOME;
91
case XK_Insert:
92
return KEY_INSERT;
93
case XK_Delete:
94
return KEY_DELETE;
95
case XK_KP_Add:
96
return KEY_ADD;
97
case XK_KP_Subtract:
98
return KEY_SUBTRACT;
99
case XK_KP_Multiply:
100
return KEY_MULTIPLY;
101
case XK_KP_Divide:
102
return KEY_DIVIDE;
103
case XK_Pause:
104
return KEY_PAUSE;
105
106
case XK_F1:
107
return KEY_F1;
108
case XK_F2:
109
return KEY_F2;
110
case XK_F3:
111
return KEY_F3;
112
case XK_F4:
113
return KEY_F4;
114
case XK_F5:
115
return KEY_F5;
116
case XK_F6:
117
return KEY_F6;
118
case XK_F7:
119
return KEY_F7;
120
case XK_F8:
121
return KEY_F8;
122
case XK_F9:
123
return KEY_F9;
124
case XK_F10:
125
return KEY_F10;
126
case XK_F11:
127
return KEY_F11;
128
case XK_F12:
129
return KEY_F12;
130
case XK_F13:
131
return KEY_F13;
132
case XK_F14:
133
return KEY_F14;
134
case XK_F15:
135
return KEY_F15;
136
137
case XK_Left:
138
return KEY_LEFT;
139
case XK_Right:
140
return KEY_RIGHT;
141
case XK_Down:
142
return KEY_DOWN;
143
case XK_Up:
144
return KEY_UP;
145
146
case XK_KP_Insert:
147
return KEY_NUMPAD0;
148
case XK_KP_End:
149
return KEY_NUMPAD1;
150
case XK_KP_Down:
151
return KEY_NUMPAD2;
152
case XK_KP_Page_Down:
153
return KEY_NUMPAD3;
154
case XK_KP_Left:
155
return KEY_NUMPAD4;
156
case XK_KP_5:
157
return KEY_NUMPAD5;
158
case XK_KP_Right:
159
return KEY_NUMPAD6;
160
case XK_KP_Home:
161
return KEY_NUMPAD7;
162
case XK_KP_Up:
163
return KEY_NUMPAD8;
164
case XK_KP_Page_Up:
165
return KEY_NUMPAD9;
166
167
case XK_a:
168
return KEY_A;
169
case XK_b:
170
return KEY_B;
171
case XK_c:
172
return KEY_C;
173
case XK_d:
174
return KEY_D;
175
case XK_e:
176
return KEY_E;
177
case XK_f:
178
return KEY_F;
179
case XK_g:
180
return KEY_G;
181
case XK_h:
182
return KEY_H;
183
case XK_i:
184
return KEY_I;
185
case XK_j:
186
return KEY_J;
187
case XK_k:
188
return KEY_K;
189
case XK_l:
190
return KEY_L;
191
case XK_m:
192
return KEY_M;
193
case XK_n:
194
return KEY_N;
195
case XK_o:
196
return KEY_O;
197
case XK_p:
198
return KEY_P;
199
case XK_q:
200
return KEY_Q;
201
case XK_r:
202
return KEY_R;
203
case XK_s:
204
return KEY_S;
205
case XK_t:
206
return KEY_T;
207
case XK_u:
208
return KEY_U;
209
case XK_v:
210
return KEY_V;
211
case XK_w:
212
return KEY_W;
213
case XK_x:
214
return KEY_X;
215
case XK_y:
216
return KEY_Y;
217
case XK_z:
218
return KEY_Z;
219
220
case XK_1:
221
return KEY_NUM1;
222
case XK_2:
223
return KEY_NUM2;
224
case XK_3:
225
return KEY_NUM3;
226
case XK_4:
227
return KEY_NUM4;
228
case XK_5:
229
return KEY_NUM5;
230
case XK_6:
231
return KEY_NUM6;
232
case XK_7:
233
return KEY_NUM7;
234
case XK_8:
235
return KEY_NUM8;
236
case XK_9:
237
return KEY_NUM9;
238
case XK_0:
239
return KEY_NUM0;
240
}
241
242
return Key(0);
243
}
244
245
static void AddX11KeyStateToEvent(Event *event, unsigned int state)
246
{
247
event->Key.Shift = state & ShiftMask;
248
event->Key.Control = state & ControlMask;
249
event->Key.Alt = state & Mod1Mask;
250
event->Key.System = state & Mod4Mask;
251
}
252
253
} // namespace
254
255
X11Window::X11Window()
256
: WM_DELETE_WINDOW(None),
257
WM_PROTOCOLS(None),
258
TEST_EVENT(None),
259
mDisplay(nullptr),
260
mWindow(0),
261
mRequestedVisualId(-1),
262
mVisible(false)
263
{}
264
265
X11Window::X11Window(int visualId)
266
: WM_DELETE_WINDOW(None),
267
WM_PROTOCOLS(None),
268
TEST_EVENT(None),
269
mDisplay(nullptr),
270
mWindow(0),
271
mRequestedVisualId(visualId),
272
mVisible(false)
273
{}
274
275
X11Window::~X11Window()
276
{
277
destroy();
278
}
279
280
bool X11Window::initializeImpl(const std::string &name, int width, int height)
281
{
282
destroy();
283
284
mDisplay = XOpenDisplay(nullptr);
285
if (!mDisplay)
286
{
287
return false;
288
}
289
290
{
291
int screen = DefaultScreen(mDisplay);
292
Window root = RootWindow(mDisplay, screen);
293
294
Visual *visual;
295
if (mRequestedVisualId == -1)
296
{
297
visual = DefaultVisual(mDisplay, screen);
298
}
299
else
300
{
301
XVisualInfo visualTemplate;
302
visualTemplate.visualid = mRequestedVisualId;
303
304
int numVisuals = 0;
305
XVisualInfo *visuals =
306
XGetVisualInfo(mDisplay, VisualIDMask, &visualTemplate, &numVisuals);
307
if (numVisuals <= 0)
308
{
309
return false;
310
}
311
ASSERT(numVisuals == 1);
312
313
visual = visuals[0].visual;
314
XFree(visuals);
315
}
316
317
int depth = DefaultDepth(mDisplay, screen);
318
Colormap colormap = XCreateColormap(mDisplay, root, visual, AllocNone);
319
320
XSetWindowAttributes attributes;
321
unsigned long attributeMask = CWBorderPixel | CWColormap | CWEventMask;
322
323
attributes.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask |
324
ButtonReleaseMask | FocusChangeMask | EnterWindowMask |
325
LeaveWindowMask | KeyPressMask | KeyReleaseMask;
326
attributes.border_pixel = 0;
327
attributes.colormap = colormap;
328
329
mWindow = XCreateWindow(mDisplay, root, 0, 0, width, height, 0, depth, InputOutput, visual,
330
attributeMask, &attributes);
331
XFreeColormap(mDisplay, colormap);
332
}
333
334
if (!mWindow)
335
{
336
destroy();
337
return false;
338
}
339
340
// Tell the window manager to notify us when the user wants to close the
341
// window so we can do it ourselves.
342
WM_DELETE_WINDOW = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
343
WM_PROTOCOLS = XInternAtom(mDisplay, "WM_PROTOCOLS", False);
344
if (WM_DELETE_WINDOW == None || WM_PROTOCOLS == None)
345
{
346
destroy();
347
return false;
348
}
349
350
if (XSetWMProtocols(mDisplay, mWindow, &WM_DELETE_WINDOW, 1) == 0)
351
{
352
destroy();
353
return false;
354
}
355
356
// Create an atom to identify our test event
357
TEST_EVENT = XInternAtom(mDisplay, "ANGLE_TEST_EVENT", False);
358
if (TEST_EVENT == None)
359
{
360
destroy();
361
return false;
362
}
363
364
XFlush(mDisplay);
365
366
mX = 0;
367
mY = 0;
368
mWidth = width;
369
mHeight = height;
370
371
return true;
372
}
373
374
void X11Window::disableErrorMessageDialog() {}
375
376
void X11Window::destroy()
377
{
378
if (mWindow)
379
{
380
XDestroyWindow(mDisplay, mWindow);
381
mWindow = 0;
382
}
383
if (mDisplay)
384
{
385
XCloseDisplay(mDisplay);
386
mDisplay = nullptr;
387
}
388
WM_DELETE_WINDOW = None;
389
WM_PROTOCOLS = None;
390
}
391
392
void X11Window::resetNativeWindow() {}
393
394
EGLNativeWindowType X11Window::getNativeWindow() const
395
{
396
return mWindow;
397
}
398
399
EGLNativeDisplayType X11Window::getNativeDisplay() const
400
{
401
return reinterpret_cast<EGLNativeDisplayType>(mDisplay);
402
}
403
404
void X11Window::messageLoop()
405
{
406
int eventCount = XPending(mDisplay);
407
while (eventCount--)
408
{
409
XEvent event;
410
XNextEvent(mDisplay, &event);
411
processEvent(event);
412
}
413
}
414
415
void X11Window::setMousePosition(int x, int y)
416
{
417
XWarpPointer(mDisplay, None, mWindow, 0, 0, 0, 0, x, y);
418
}
419
420
bool X11Window::setOrientation(int width, int height)
421
{
422
UNIMPLEMENTED();
423
return false;
424
}
425
426
bool X11Window::setPosition(int x, int y)
427
{
428
XMoveWindow(mDisplay, mWindow, x, y);
429
XFlush(mDisplay);
430
return true;
431
}
432
433
bool X11Window::resize(int width, int height)
434
{
435
XResizeWindow(mDisplay, mWindow, width, height);
436
XFlush(mDisplay);
437
438
Timer timer;
439
timer.start();
440
441
// Wait until the window has actually been resized so that the code calling resize
442
// can assume the window has been resized.
443
const double kResizeWaitDelay = 0.2;
444
while ((mHeight != height || mWidth != width) && timer.getElapsedTime() < kResizeWaitDelay)
445
{
446
messageLoop();
447
angle::Sleep(10);
448
}
449
450
return true;
451
}
452
453
void X11Window::setVisible(bool isVisible)
454
{
455
if (mVisible == isVisible)
456
{
457
return;
458
}
459
460
if (isVisible)
461
{
462
XMapWindow(mDisplay, mWindow);
463
464
// Wait until we get an event saying this window is mapped so that the
465
// code calling setVisible can assume the window is visible.
466
// This is important when creating a framebuffer as the framebuffer content
467
// is undefined when the window is not visible.
468
XEvent placeholderEvent;
469
XIfEvent(mDisplay, &placeholderEvent, WaitForMapNotify,
470
reinterpret_cast<XPointer>(mWindow));
471
}
472
else
473
{
474
XUnmapWindow(mDisplay, mWindow);
475
XFlush(mDisplay);
476
}
477
mVisible = isVisible;
478
}
479
480
void X11Window::signalTestEvent()
481
{
482
XEvent event;
483
event.type = ClientMessage;
484
event.xclient.message_type = TEST_EVENT;
485
// Format needs to be valid or a BadValue is generated
486
event.xclient.format = 32;
487
488
// Hijack StructureNotifyMask as we know we will be listening for it.
489
XSendEvent(mDisplay, mWindow, False, StructureNotifyMask, &event);
490
491
// For test events, the tests want to check that it really did arrive, and they don't wait
492
// long. XSync here makes sure the event is sent by the time the messageLoop() is called.
493
XSync(mDisplay, false);
494
}
495
496
void X11Window::processEvent(const XEvent &xEvent)
497
{
498
// TODO(cwallez) text events
499
switch (xEvent.type)
500
{
501
case ButtonPress:
502
{
503
Event event;
504
MouseButton button = MOUSEBUTTON_UNKNOWN;
505
int wheelY = 0;
506
507
// The mouse wheel updates are sent via button events.
508
switch (xEvent.xbutton.button)
509
{
510
case Button4:
511
wheelY = 1;
512
break;
513
case Button5:
514
wheelY = -1;
515
break;
516
case 6:
517
break;
518
case 7:
519
break;
520
521
case Button1:
522
button = MOUSEBUTTON_LEFT;
523
break;
524
case Button2:
525
button = MOUSEBUTTON_MIDDLE;
526
break;
527
case Button3:
528
button = MOUSEBUTTON_RIGHT;
529
break;
530
case 8:
531
button = MOUSEBUTTON_BUTTON4;
532
break;
533
case 9:
534
button = MOUSEBUTTON_BUTTON5;
535
break;
536
537
default:
538
break;
539
}
540
541
if (wheelY != 0)
542
{
543
event.Type = Event::EVENT_MOUSE_WHEEL_MOVED;
544
event.MouseWheel.Delta = wheelY;
545
pushEvent(event);
546
}
547
548
if (button != MOUSEBUTTON_UNKNOWN)
549
{
550
event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
551
event.MouseButton.Button = button;
552
event.MouseButton.X = xEvent.xbutton.x;
553
event.MouseButton.Y = xEvent.xbutton.y;
554
pushEvent(event);
555
}
556
}
557
break;
558
559
case ButtonRelease:
560
{
561
Event event;
562
MouseButton button = MOUSEBUTTON_UNKNOWN;
563
564
switch (xEvent.xbutton.button)
565
{
566
case Button1:
567
button = MOUSEBUTTON_LEFT;
568
break;
569
case Button2:
570
button = MOUSEBUTTON_MIDDLE;
571
break;
572
case Button3:
573
button = MOUSEBUTTON_RIGHT;
574
break;
575
case 8:
576
button = MOUSEBUTTON_BUTTON4;
577
break;
578
case 9:
579
button = MOUSEBUTTON_BUTTON5;
580
break;
581
582
default:
583
break;
584
}
585
586
if (button != MOUSEBUTTON_UNKNOWN)
587
{
588
event.Type = Event::EVENT_MOUSE_BUTTON_RELEASED;
589
event.MouseButton.Button = button;
590
event.MouseButton.X = xEvent.xbutton.x;
591
event.MouseButton.Y = xEvent.xbutton.y;
592
pushEvent(event);
593
}
594
}
595
break;
596
597
case KeyPress:
598
{
599
Event event;
600
event.Type = Event::EVENT_KEY_PRESSED;
601
event.Key.Code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
602
AddX11KeyStateToEvent(&event, xEvent.xkey.state);
603
pushEvent(event);
604
}
605
break;
606
607
case KeyRelease:
608
{
609
Event event;
610
event.Type = Event::EVENT_KEY_RELEASED;
611
event.Key.Code = X11CodeToKey(mDisplay, xEvent.xkey.keycode);
612
AddX11KeyStateToEvent(&event, xEvent.xkey.state);
613
pushEvent(event);
614
}
615
break;
616
617
case EnterNotify:
618
{
619
Event event;
620
event.Type = Event::EVENT_MOUSE_ENTERED;
621
pushEvent(event);
622
}
623
break;
624
625
case LeaveNotify:
626
{
627
Event event;
628
event.Type = Event::EVENT_MOUSE_LEFT;
629
pushEvent(event);
630
}
631
break;
632
633
case MotionNotify:
634
{
635
Event event;
636
event.Type = Event::EVENT_MOUSE_MOVED;
637
event.MouseMove.X = xEvent.xmotion.x;
638
event.MouseMove.Y = xEvent.xmotion.y;
639
pushEvent(event);
640
}
641
break;
642
643
case ConfigureNotify:
644
{
645
if (xEvent.xconfigure.width != mWidth || xEvent.xconfigure.height != mHeight)
646
{
647
Event event;
648
event.Type = Event::EVENT_RESIZED;
649
event.Size.Width = xEvent.xconfigure.width;
650
event.Size.Height = xEvent.xconfigure.height;
651
pushEvent(event);
652
}
653
if (xEvent.xconfigure.x != mX || xEvent.xconfigure.y != mY)
654
{
655
// Sometimes, the window manager reparents our window (for example
656
// when resizing) then the X and Y coordinates will be with respect to
657
// the new parent and not what the user wants to know. Use
658
// XTranslateCoordinates to get the coordinates on the screen.
659
int screen = DefaultScreen(mDisplay);
660
Window root = RootWindow(mDisplay, screen);
661
662
int x, y;
663
Window child;
664
XTranslateCoordinates(mDisplay, mWindow, root, 0, 0, &x, &y, &child);
665
666
if (x != mX || y != mY)
667
{
668
Event event;
669
event.Type = Event::EVENT_MOVED;
670
event.Move.X = x;
671
event.Move.Y = y;
672
pushEvent(event);
673
}
674
}
675
}
676
break;
677
678
case FocusIn:
679
if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
680
{
681
Event event;
682
event.Type = Event::EVENT_GAINED_FOCUS;
683
pushEvent(event);
684
}
685
break;
686
687
case FocusOut:
688
if (xEvent.xfocus.mode == NotifyNormal || xEvent.xfocus.mode == NotifyWhileGrabbed)
689
{
690
Event event;
691
event.Type = Event::EVENT_LOST_FOCUS;
692
pushEvent(event);
693
}
694
break;
695
696
case DestroyNotify:
697
// We already received WM_DELETE_WINDOW
698
break;
699
700
case ClientMessage:
701
if (xEvent.xclient.message_type == WM_PROTOCOLS &&
702
static_cast<Atom>(xEvent.xclient.data.l[0]) == WM_DELETE_WINDOW)
703
{
704
Event event;
705
event.Type = Event::EVENT_CLOSED;
706
pushEvent(event);
707
}
708
else if (xEvent.xclient.message_type == TEST_EVENT)
709
{
710
Event event;
711
event.Type = Event::EVENT_TEST;
712
pushEvent(event);
713
}
714
break;
715
}
716
}
717
718
// static
719
OSWindow *OSWindow::New()
720
{
721
return new X11Window();
722
}
723
724