1 // Copyright 2016-2021 Doug Moen
2 // Licensed under the Apache License, version 2.0
3 // See accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0
4 
5 #include "version.h"
6 #include <libcurv/version.h>
7 extern "C" {
8 #ifndef _WIN32
9     #include <sys/utsname.h>
10 #endif
11 #include <stdlib.h>
12 }
13 #include <libcurv/viewer/glfw.h>
14 
15 #define USE_X11 0
16 #if USE_X11
17 #include <X11/Xlib.h>
18 #include <GL/glx.h>
19 #endif
20 
21 void
print_gpu(std::ostream & out)22 print_gpu(std::ostream& out)
23 {
24     // GPU information: see `glxinfo` on Linux, but I don't want hundreds of
25     // lines of gibberish. I want: GPU model, driver version, OpenGL version.
26     // If X11, then 'direct rendering':' from glxinfo.
27 
28     static std::ostream* gout;
29 
30     out.flush(); // In case of crash while querying GPU.
31 
32     // This is a nice sanity test of Curv's ability to access the GPU.
33     // If this doesn't work, there's no need to debug anything deep in the
34     // Viewer code.
35 
36     gout = &out;
37     glfwSetErrorCallback([](int err, const char* msg)->void {
38         *gout << "GPU: "<<msg<<"\n";
39     });
40     if(!glfwInit()) {
41         return;
42     }
43 
44     glfwWindowHint(GLFW_VISIBLE, GL_FALSE);
45     GLFWwindow* window = glfwCreateWindow(100, 100, "version", NULL, NULL);
46     if (window == nullptr) {
47         return;
48     }
49     glfwMakeContextCurrent(window);
50     if (!curv::viewer::opengl_init()) {
51         *gout << "GPU: can't load OpenGL library\n";
52         return;
53     }
54 
55     const char* vendor = (const char*) glGetString(GL_VENDOR);
56     if (vendor == nullptr) vendor = "???";
57     const char* renderer = (const char*) glGetString(GL_RENDERER);
58     if (renderer == nullptr) renderer = "???";
59     const char* version = (const char*) glGetString(GL_VERSION);
60     if (version == nullptr) version = "???";
61 
62     out << "GPU: " << vendor << ", " << renderer << "\n";
63     out << "OpenGL: " << version << "\n";
64     out.flush();
65 
66 #if USE_X11
67     char* display_name = getenv("DISPLAY");
68     if (display_name != nullptr) {
69         // X11 specific information, from glxinfo
70         // To get correct information, I need to use the same display and ctx
71         // that GLFW is using. This doesn't work.
72         Display* display = XOpenDisplay(display_name);
73         if (display == nullptr)
74             return;
75         GLXContext ctx = 0;
76         out << "Direct Rendering: ";
77         if (glXIsDirect(display, ctx)) {
78             out << "Yes\n";
79         }
80         else if (getenv("LIBGL_ALWAYS_INDIRECT")) {
81             out << "No (LIBGL_ALWAYS_INDIRECT set)\n";
82         }
83         else {
84             out << "No (If you want to find out why, try setting "
85                    "LIBGL_DEBUG=verbose)\n";
86         }
87         XCloseDisplay(display);
88     }
89 #endif
90 
91     // In principle, this should free resources and leave no storage leaks.
92     glfwDestroyWindow(window);
93     glfwTerminate();
94 }
95 
96 void
print_os(std::ostream & out)97 print_os(std::ostream& out)
98 {
99     // TODO: print "OS" distro name and version.
100     // On Linux (os.sysname=="Linux"), use /etc/os-release,
101     //   see <https://www.freedesktop.org/software/systemd/man/os-release.html>
102     // On macOS (os.sysname=="Darwin"), use
103     //   /System/Library/CoreServices/SystemVersion.plist
104 
105 #ifdef _WIN32
106     // TODO Get version on Windows, see also
107     // https://docs.microsoft.com/en-US/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
108     out << "Kernel: Windows\n";
109 #else
110     struct utsname os;
111     uname(&os);
112     out << "Kernel: " << os.sysname << " " << os.release
113         << " " << os.machine << "\n";
114 #endif
115 }
116 
117 void
print_cpu(std::ostream &)118 print_cpu(std::ostream&)
119 {
120     // CPU information: see `lscpu` on Linux.
121 
122     // TODO: cpu -- see `lscpu` on Linux. name, model, # of cores
123     // TODO: vm -- see `lscpu` on Linux
124     // TODO: The amount of RAM.
125     // see https://github.com/dylanaraps/neofetch
126 }
127 
128 void
print_compiler(std::ostream & out)129 print_compiler(std::ostream& out)
130 {
131     #if defined __clang_version__
132         out << "Compiler: clang " << __clang_version__ << "\n";
133     #elif defined __GNUC__
134         out << "Compiler: gcc " << __VERSION__ << "\n";
135     #else
136         (void) out;
137     #endif
138 }
139 
140 // Print version information useful for bug reports.
141 // Inspiration: openscad --info,
142 // and neofetch, which displays terse and readable system information:
143 //    see <https://github.com/dylanaraps/neofetch>
144 void
print_version(std::ostream & out)145 print_version(std::ostream& out)
146 {
147     out << "Curv: " << CURV_VERSION << "\n";
148     print_compiler(out);
149     print_os(out);
150     print_cpu(out);
151     print_gpu(out); // Last info printed, due to crashiness.
152 }
153