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