Project
Loading...
Searching...
No Matches
GPUDisplayFrontendX11.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
15// Now the other headers
19#include "GPULogging.h"
20#include <cstdio>
21#include <cstdlib>
22#include <cstring>
23#include <stdexcept>
24#include <chrono>
25
26#ifdef GPUCA_BUILD_EVENT_DISPLAY_VULKAN
27#include <vulkan/vulkan.h>
28#include <vulkan/vulkan_xlib.h>
29#endif
30
31typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int32_t*);
32
33using namespace o2::gpu;
34
40
41int32_t GPUDisplayFrontendX11::GetKey(int32_t key)
42{
43 if (key == 65453) {
44 return ('-');
45 }
46 if (key == 65451) {
47 return ('+');
48 }
49 if (key == 65505 || key == 65506) {
50 return (KEY_SHIFT);
51 }
52 if (key == 65513 || key == 65511) {
53 return (KEY_ALT);
54 }
55 if (key == 65027) {
56 return (KEY_RALT);
57 }
58 if (key == 65507) {
59 return (KEY_CTRL);
60 }
61 if (key == 65508) {
62 return (KEY_RCTRL);
63 }
64 if (key == 65362) {
65 return (KEY_UP);
66 }
67 if (key == 65364) {
68 return (KEY_DOWN);
69 }
70 if (key == 65361) {
71 return (KEY_LEFT);
72 }
73 if (key == 65363) {
74 return (KEY_RIGHT);
75 }
76 if (key == 65365) {
77 return (KEY_PAGEUP);
78 }
79 if (key == 65366) {
80 return (KEY_PAGEDOWN);
81 }
82 if (key == 65307) {
83 return (KEY_ESCAPE);
84 }
85 if (key == 65293) {
86 return (KEY_ENTER);
87 }
88 if (key == 65367) {
89 return (KEY_END);
90 }
91 if (key == 65360) {
92 return (KEY_HOME);
93 }
94 if (key == 65379) {
95 return (KEY_INSERT);
96 }
97 if (key == 65470) {
98 return (KEY_F1);
99 }
100 if (key == 65471) {
101 return (KEY_F2);
102 }
103 if (key == 65472) {
104 return (KEY_F3);
105 }
106 if (key == 65473) {
107 return (KEY_F4);
108 }
109 if (key == 65474) {
110 return (KEY_F5);
111 }
112 if (key == 65475) {
113 return (KEY_F6);
114 }
115 if (key == 65476) {
116 return (KEY_F7);
117 }
118 if (key == 65477) {
119 return (KEY_F8);
120 }
121 if (key == 65478) {
122 return (KEY_F9);
123 }
124 if (key == 65479) {
125 return (KEY_F10);
126 }
127 if (key == 65480) {
128 return (KEY_F11);
129 }
130 if (key == 65481) {
131 return (KEY_F12);
132 }
133 if (key == 32) {
134 return (KEY_SPACE);
135 }
136 if (key > 255) {
137 return (0);
138 }
139 return 0;
140}
141
142void GPUDisplayFrontendX11::GetKey(XEvent& event, int32_t& keyOut, int32_t& keyPressOut)
143{
144 char tmpString[9];
145 KeySym sym;
146 if (XLookupString(&event.xkey, tmpString, 8, &sym, nullptr) == 0) {
147 tmpString[0] = 0;
148 }
149 int32_t specialKey = GetKey(sym);
150 int32_t localeKey = (uint8_t)tmpString[0];
151 // GPUInfo("Key: keycode %d -> sym %d (%c) key %d (%c) special %d (%c)", (int32_t)event.xkey.keycode, (int32_t)sym, (char)sym, (int32_t)localeKey, (char)localeKey, (int32_t)specialKey, (char)specialKey);
152
153 if (specialKey) {
154 keyOut = keyPressOut = specialKey;
155 } else {
156 keyOut = keyPressOut = localeKey;
157 if (keyPressOut >= 'a' && keyPressOut <= 'z') {
158 keyPressOut += 'A' - 'a';
159 }
160 }
161}
162
163void GPUDisplayFrontendX11::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton)
164{
165#ifndef GPUCA_DISPLAY_OPENGL_CORE
166 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
167 if (!fromBotton) {
168 y = mDisplayHeight - y;
169 }
170 glColor4f(r, g, b, a);
171 glRasterPos2f(x, y);
172 if (!glIsList(mFontBase)) {
173 GPUError("print string: Bad display list.");
174 exit(1);
175 } else if (s && strlen(s)) {
176 glPushAttrib(GL_LIST_BIT);
177 glListBase(mFontBase);
178 glCallLists(strlen(s), GL_UNSIGNED_BYTE, (GLubyte*)s);
179 glPopAttrib();
180 }
181 }
182#endif
183}
184
185int32_t GPUDisplayFrontendX11::FrontendMain()
186{
187 XSetWindowAttributes windowAttributes;
188 XVisualInfo* visualInfo = nullptr;
189 XEvent event;
190 Colormap colorMap;
191 GLXContext glxContext = nullptr;
192 int32_t errorBase;
193 int32_t eventBase;
194
195 // Open a connection to the X server
196 mDisplay = XOpenDisplay(nullptr);
197
198 if (mDisplay == nullptr) {
199 GPUError("could not open display");
200 return (-1);
201 }
202
203 // Make sure OpenGL's GLX extension supported
204 if (!glXQueryExtension(mDisplay, &errorBase, &eventBase)) {
205 GPUError("X server has no OpenGL GLX extension");
206 return (-1);
207 }
208
209 const char* glxExt = glXQueryExtensionsString(mDisplay, DefaultScreen(mDisplay));
210 if (strstr(glxExt, "GLX_EXT_swap_control") == nullptr) {
211 GPUError("No vsync support!");
212 vsync_supported = false;
213 } else {
214 vsync_supported = true;
215 }
216
217 // Require MSAA, double buffering, and Depth buffer
218 int32_t attribs[] = {GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, GLX_DOUBLEBUFFER, True,
219 // GLX_SAMPLE_BUFFERS , 1, //Disable MSAA here, we do it by rendering to offscreenbuffer
220 // GLX_SAMPLES , MSAA_SAMPLES,
221 None};
222
223 GLXFBConfig fbconfig = nullptr;
224 int32_t fbcount;
225 GLXFBConfig* fbc = glXChooseFBConfig(mDisplay, DefaultScreen(mDisplay), attribs, &fbcount);
226 if (fbc == nullptr || fbcount == 0) {
227 GPUError("Failed to get MSAA GLXFBConfig");
228 return (-1);
229 }
230 fbconfig = fbc[0];
231 XFree(fbc);
232 visualInfo = glXGetVisualFromFBConfig(mDisplay, fbconfig);
233
234 if (visualInfo == nullptr) {
235 GPUError("no RGB visual with depth buffer");
236 return (-1);
237 }
238
239 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
240 // Create an OpenGL rendering context
241 glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB");
242 if (glXCreateContextAttribsARB) {
243 int32_t context_attribs[] = {
244 GLX_CONTEXT_MAJOR_VERSION_ARB, GL_MIN_VERSION_MAJOR,
245 GLX_CONTEXT_MINOR_VERSION_ARB, GL_MIN_VERSION_MINOR,
246 GLX_CONTEXT_PROFILE_MASK_ARB, mBackend->CoreProfile() ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB : GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
247 None};
248 glxContext = glXCreateContextAttribsARB(mDisplay, fbconfig, nullptr, GL_TRUE, context_attribs);
249 } else {
250 glxContext = glXCreateContext(mDisplay, visualInfo, nullptr, GL_TRUE);
251 }
252 if (glxContext == nullptr) {
253 GPUError("could not create rendering context");
254 return (-1);
255 }
256 }
257
258 Window win = RootWindow(mDisplay, visualInfo->screen);
259 colorMap = XCreateColormap(mDisplay, win, visualInfo->visual, AllocNone);
260 windowAttributes.colormap = colorMap;
261 windowAttributes.border_pixel = 0;
262 windowAttributes.event_mask = ExposureMask | VisibilityChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | SubstructureNotifyMask | FocusChangeMask;
263
264 // Create an X window with the selected visual
265 mWindow = XCreateWindow(mDisplay, win, 50, 50, INIT_WIDTH, INIT_HEIGHT, // Position / Width and height of window
266 0, visualInfo->depth, InputOutput, visualInfo->visual, CWBorderPixel | CWColormap | CWEventMask, &windowAttributes);
267 XSetStandardProperties(mDisplay, mWindow, DISPLAY_WINDOW_NAME, DISPLAY_WINDOW_NAME, None, nullptr, 0, nullptr);
268 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
269 glXMakeCurrent(mDisplay, mWindow, glxContext);
270 }
271 XMapWindow(mDisplay, mWindow);
272
273 // Maximize window
274 ToggleMaximized(true);
275
276 // Receive signal when window closed
277 Atom WM_DELETE_WINDOW = XInternAtom(mDisplay, "WM_DELETE_WINDOW", False);
278 XSetWMProtocols(mDisplay, mWindow, &WM_DELETE_WINDOW, 1);
279#ifndef GPUCA_DISPLAY_OPENGL_CORE
280 XFontStruct* font_info = nullptr;
281 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
282 // Prepare fonts
283 mFontBase = glGenLists(256);
284 if (!glIsList(mFontBase)) {
285 GPUError("Out of display lists.");
286 return (-1);
287 }
288 const char* f = "fixed";
289 font_info = XLoadQueryFont(mDisplay, f);
290 if (!font_info) {
291 GPUError("XLoadQueryFont failed.");
292 return (-1);
293 } else {
294 int32_t first = font_info->min_char_or_byte2;
295 int32_t last = font_info->max_char_or_byte2;
296 glXUseXFont(font_info->fid, first, last - first + 1, mFontBase + first);
297 }
298 }
299 mCanDrawText = 1;
300 if (drawTextFontSize() == 0) {
301 drawTextFontSize() = 12;
302 }
303#endif
304
305 if (mBackend->ExtInit()) {
306 fprintf(stderr, "Error initializing backend\n");
307 return (-1);
308 }
309
310 XMapWindow(mDisplay, mWindow);
311 XFlush(mDisplay);
312 int32_t x11_fd = ConnectionNumber(mDisplay);
313
314 // Enable vsync
315 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL && vsync_supported) {
316 mGlXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalEXT");
317 if (mGlXSwapIntervalEXT == nullptr) {
318 GPUError("Cannot enable vsync");
319 return (-1);
320 }
321 mGlXSwapIntervalEXT(mDisplay, glXGetCurrentDrawable(), 1);
322 }
323
324 if (InitDisplay()) {
325 return (1);
326 }
327
328 pthread_mutex_lock(&mSemLockExit);
329 mDisplayRunning = true;
330 pthread_mutex_unlock(&mSemLockExit);
331
332 std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
333 while (1) {
334 int32_t num_ready_fds;
335 struct timeval tv;
336 fd_set in_fds;
337 int32_t waitCount = 0;
338 do {
339 FD_ZERO(&in_fds);
340 FD_SET(x11_fd, &in_fds);
341 tv.tv_usec = 10000;
342 tv.tv_sec = 0;
343 std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
344 bool allowMax = mMaxFPSRate && std::chrono::duration_cast<std::chrono::duration<double>>(t2 - t1).count() < 0.01;
345 t1 = t2;
346 num_ready_fds = allowMax || XPending(mDisplay) || select(x11_fd + 1, &in_fds, nullptr, nullptr, &tv);
347 if (num_ready_fds < 0) {
348 GPUError("Error (num_ready_fds)");
349 }
350 if (mDisplayControl == 2) {
351 break;
352 }
353 if (mSendKey) {
354 mNeedUpdate = 1;
355 }
356 if (waitCount++ != 100) {
357 mNeedUpdate = 1;
358 }
359 } while (!(num_ready_fds || mNeedUpdate));
360 mNeedUpdate = 0;
361
362 do {
363 if (mDisplayControl == 2) {
364 break;
365 }
367 if (!XPending(mDisplay)) {
368 event.type = Expose;
369 } else {
370 XNextEvent(mDisplay, &event);
371 }
372 switch (event.type) {
373 case ButtonPress: {
374 if (event.xbutton.button == 4) {
375 mMouseWheel += 100;
376 } else if (event.xbutton.button == 5) {
377 mMouseWheel -= 100;
378 } else {
379 if (event.xbutton.button == 1) {
380 mMouseDn = true;
381 } else {
382 mMouseDnR = true;
383 }
384 mMouseDnX = event.xmotion.x;
385 mMouseDnY = event.xmotion.y;
386 }
387 break;
388 }
389
390 case ButtonRelease: {
391 if (event.xbutton.button != 4 && event.xbutton.button != 5) {
392 if (event.xbutton.button == 1) {
393 mMouseDn = false;
394 } else {
395 mMouseDnR = false;
396 }
397 }
398 break;
399 }
400
401 case KeyPress: {
402 int32_t handleKey = 0, keyPress = 0;
403 GetKey(event, handleKey, keyPress);
404 mKeysShift[keyPress] = mKeys[KEY_SHIFT];
405 mKeys[keyPress] = true;
406 HandleKey(handleKey);
407 break;
408 }
409
410 case KeyRelease: {
411 int32_t handleKey = 0, keyPress = 0;
412 GetKey(event, handleKey, keyPress);
413 mKeys[keyPress] = false;
414 mKeysShift[keyPress] = false;
415 break;
416 }
417
418 case MotionNotify: {
419 mMouseMvX = event.xmotion.x;
420 mMouseMvY = event.xmotion.y;
421 break;
422 }
423
424 case Expose: {
425 break;
426 }
427
428 case ConfigureNotify: {
429 ResizeScene(event.xconfigure.width, event.xconfigure.height);
430 break;
431 }
432
433 case ClientMessage: {
434 if (event.xclient.message_type == XInternAtom(mDisplay, "_NET_WM_STATE", False)) {
435 XFlush(mDisplay);
436 } else {
437 mDisplayControl = 2;
438 }
439 break;
440 }
441 }
442 } while (XPending(mDisplay)); // Loop to compress events
443 if (mDisplayControl == 2) {
444 break;
445 }
446
447 DrawGLScene();
448 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
449 glXSwapBuffers(mDisplay, mWindow);
450 }
451 }
452
453#ifndef GPUCA_DISPLAY_OPENGL_CORE
454 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
455 glDeleteLists(mFontBase, 256);
456 XUnloadFont(mDisplay, font_info->fid);
457 }
458#endif
459 ExitDisplay();
460 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
461 glXDestroyContext(mDisplay, glxContext);
462 }
463 XFree(visualInfo);
464 XDestroyWindow(mDisplay, mWindow);
465 XCloseDisplay(mDisplay);
466
467 pthread_mutex_lock(&mSemLockExit);
468 mDisplayRunning = false;
469 pthread_mutex_unlock(&mSemLockExit);
470
471 return (0);
472}
473
475{
476 pthread_mutex_lock(&mSemLockExit);
477 if (mDisplayRunning) {
478 mDisplayControl = 2;
479 }
480 pthread_mutex_unlock(&mSemLockExit);
481 while (mDisplayRunning) {
482 usleep(10000);
483 }
484}
485
487{
488 XEvent xev;
489 memset(&xev, 0, sizeof(xev));
490 xev.type = ClientMessage;
491 xev.xclient.window = mWindow;
492 xev.xclient.message_type = XInternAtom(mDisplay, "_NET_WM_STATE", False);
493 xev.xclient.format = 32;
494 xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE
495 xev.xclient.data.l[1] = XInternAtom(mDisplay, "_NET_WM_STATE_FULLSCREEN", True);
496 xev.xclient.data.l[2] = 0;
497 XSendEvent(mDisplay, DefaultRootWindow(mDisplay), False, SubstructureNotifyMask, &xev);
498}
499
501{
502 XEvent xev;
503 memset(&xev, 0, sizeof(xev));
504 xev.type = ClientMessage;
505 xev.xclient.window = mWindow;
506 xev.xclient.message_type = XInternAtom(mDisplay, "_NET_WM_STATE", False);
507 xev.xclient.format = 32;
508 xev.xclient.data.l[0] = set ? 1 : 2; //_NET_WM_STATE_ADD
509 xev.xclient.data.l[1] = XInternAtom(mDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
510 xev.xclient.data.l[2] = XInternAtom(mDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", False);
511 XSendEvent(mDisplay, DefaultRootWindow(mDisplay), False, SubstructureNotifyMask, &xev);
512}
513
515{
516 if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL && vsync_supported) {
517 mGlXSwapIntervalEXT(mDisplay, glXGetCurrentDrawable(), (int32_t)enable);
518 }
519}
520
522{
523 static pthread_t hThread;
524 if (pthread_create(&hThread, nullptr, FrontendThreadWrapper, this)) {
525 GPUError("Coult not Create frontend Thread...");
526 return (1);
527 }
528 return (0);
529}
530
532{
533 Window root_return;
534 int32_t x_return, y_return;
535 uint32_t width_return, height_return, border_width_return, depth_return;
536 if (XGetGeometry(mDisplay, mWindow, &root_return, &x_return, &y_return, &width_return, &height_return, &border_width_return, &depth_return) == 0) {
537 throw std::runtime_error("Cannot query X11 window geometry");
538 }
539 width = width_return;
540 height = height_return;
541}
542
543int32_t GPUDisplayFrontendX11::getVulkanSurface(void* instance, void* surface)
544{
545#ifdef GPUCA_BUILD_EVENT_DISPLAY_VULKAN
546 VkXlibSurfaceCreateInfoKHR info{};
547 info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
548 info.flags = 0;
549 info.dpy = mDisplay;
550 info.window = mWindow;
551 return vkCreateXlibSurfaceKHR(*(VkInstance*)instance, &info, nullptr, (VkSurfaceKHR*)surface) != VK_SUCCESS;
552#else
553 return 1;
554#endif
555}
556
558{
559 static const char* exts[] = {"VK_KHR_surface", "VK_KHR_xlib_surface"};
560 p = exts;
561 return 2;
562}
int32_t GetKey(int32_t key)
GLXContext(* glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const int32_t *)
StringRef key
void getSize(int32_t &width, int32_t &height) override
int32_t getVulkanSurface(void *instance, void *surface) override
void OpenGLPrint(const char *s, float x, float y, float r, float g, float b, float a, bool fromBotton=true) override
uint32_t getReqVulkanExtensions(const char **&p) override
void SetVSync(bool enable) override
static constexpr int32_t KEY_F9
static constexpr int32_t KEY_F2
static void * FrontendThreadWrapper(void *)
static constexpr int32_t KEY_ALT
static constexpr int32_t KEY_F12
static constexpr int32_t KEY_F7
static constexpr int32_t KEY_END
static constexpr int32_t KEY_PAGEDOWN
static constexpr int32_t KEY_RALT
static constexpr int32_t KEY_F10
static constexpr int32_t KEY_F4
static constexpr int32_t KEY_LEFT
static constexpr int32_t KEY_ENTER
static constexpr int32_t KEY_F6
static constexpr int32_t KEY_CTRL
static constexpr int32_t KEY_F1
void ResizeScene(int32_t width, int32_t height)
static constexpr const char * DISPLAY_WINDOW_NAME
static constexpr int32_t INIT_WIDTH
static constexpr int32_t GL_MIN_VERSION_MAJOR
static constexpr int32_t KEY_SHIFT
static constexpr int32_t KEY_F3
static constexpr int32_t KEY_INSERT
static constexpr int32_t KEY_F11
static constexpr int32_t KEY_SPACE
static constexpr int32_t KEY_F5
static constexpr int32_t KEY_PAGEUP
static constexpr int32_t KEY_HOME
static constexpr int32_t GL_MIN_VERSION_MINOR
int32_t InitDisplay(bool initFailure=false)
static constexpr int32_t KEY_UP
static constexpr int32_t KEY_DOWN
static constexpr int32_t KEY_F8
static constexpr int32_t INIT_HEIGHT
static constexpr int32_t KEY_RIGHT
static constexpr int32_t KEY_RCTRL
static constexpr int32_t KEY_ESCAPE
struct _cl_event * event
Definition glcorearb.h:2982
#define GL_TRUE
Definition glcorearb.h:95
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
khronos_uint8_t GLubyte
Definition glcorearb.h:90
GLint GLsizei GLsizei height
Definition glcorearb.h:270
GLint GLsizei width
Definition glcorearb.h:270
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint y
Definition glcorearb.h:270
#define GL_UNSIGNED_BYTE
Definition glcorearb.h:206
GLboolean GLboolean g
Definition glcorearb.h:1233
GLboolean enable
Definition glcorearb.h:3991
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
Definition glcorearb.h:5034
uint8_t itsSharedClusterMap uint8_t
std::vector< InputSpec > select(char const *matcher="")
Marks an empty item in the context.