// TODO: Add all the error handling to the glx part #include "openwindow.h" #include #include #include #include int initOpenGL() { if (getOpenGLProcs() == 0) return -1; glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); return 0; } #define OPENWINDOW_OPENGL_VERSSION_MAJOR 3 #define OPENWINDOW_OPENGL_VERSSION_MINOR 0 #ifndef _WIN32 #include typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); void windowDefaultEventHandler(Window * w, XEvent xev) { switch (xev.type) { case KeyPress: case KeyRelease: XQueryKeymap(w->x.display, w->io.current); break; } } void windowEventHandler(Window * w, XEvent event) { windowDefaultEventHandler(w, event); } bool checkGLXVersion(Window window) { int glx_major, glx_minor; if (!glXQueryVersion(window.x.display, &glx_major, &glx_minor)) { return false; } return (glx_major == 1 && glx_minor >= 3) || ( glx_major > 1); } GLXFBConfig getBestFBConfig(Window window, GLXFBConfig * configs, size_t count) { int best = 0, best_samp = -1; for (size_t i = 0; i < count; ++i) { XVisualInfo * vi = glXGetVisualFromFBConfig(window.x.display, configs[i]); if (!vi) continue; int samp_buf, samples; glXGetFBConfigAttrib(window.x.display, configs[i], GLX_SAMPLE_BUFFERS, &samp_buf); glXGetFBConfigAttrib(window.x.display, configs[i], GLX_SAMPLES , &samples); if (samp_buf && samples > best_samp) best = i, best_samp = samples; XFree(vi); } return configs[best]; } int initGLX(Window * window) { if (!checkGLXVersion(*window)) return -1; int 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, //GLX_SAMPLE_BUFFERS , 1, //GLX_SAMPLES , 4, None }; int fbcount; GLXFBConfig * configs = glXChooseFBConfig(window->x.display, DefaultScreen(window->x.display), attribs, &fbcount); if (!configs || !fbcount) { fprintf(stderr, "Failed to retrieve a framebuffer config\n"); return -1; } window->glx.config = getBestFBConfig(*window, configs, fbcount); XFree(configs); glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MAJOR, GLX_CONTEXT_MINOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MINOR, GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None }; window->glx.context = glXCreateContextAttribsARB(window->x.display, window->glx.config, 0, True, context_attribs); return 0; } int init(Window * window, const char * name, size_t width, size_t height) { window->x.display = XOpenDisplay(NULL); if (window->x.display == NULL) { fprintf(stderr, "Failed to open xdisplay!\n"); return -1; } if (initGLX(window)) return -1; XVisualInfo * vi = glXGetVisualFromFBConfig(window->x.display, window->glx.config); XSetWindowAttributes wa; wa.border_pixel = 0; wa.colormap = window->x.cmap = XCreateColormap(window->x.display, RootWindow(window->x.display, vi->screen), vi->visual, AllocNone); window->x.eventMask = wa.event_mask = StructureNotifyMask | ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask; X11Window root = RootWindow(window->x.display, vi->screen); window->x.window = XCreateWindow( window->x.display, root, 0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWBorderPixel|CWColormap|CWEventMask, &wa); if (window->x.window == None) { fprintf(stderr, "Failed to create x11 window!\n"); return -1; } XFree(vi); XMapWindow(window->x.display, window->x.window); XStoreName(window->x.display, window->x.window, name); XSync(window->x.display, False); glXMakeCurrent(window->x.display, window->x.window, window->glx.context); //TODO: Check for errors PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((const GLubyte *)"glXSwapIntervalEXT"); if (glXSwapIntervalEXT) glXSwapIntervalEXT(window->x.display, window->x.window, 0); return 0; } Window * openWindow(const char * name, size_t width, size_t height) { Window * w = calloc(1, sizeof(Window)); w->fps = WINDOW_DEFAULT_FPS; w->dt.t1 = calloc(1, sizeof(struct timespec)); w->dt.t2 = calloc(1, sizeof(struct timespec)); if (init(w, name, width, height) < 0) { fprintf(stderr, "Failed to initialize window: X11 error!\n"); exit(1); } if (initOpenGL() < 0) { fprintf(stderr, "Failed to initialize window: GLAD/OPENGL error!\n"); exit(1); } return w; } void windowSetFps(Window * window, uint32_t fps) { window->fps = fps; } double windowGetDeltaTime(Window *window) { return window->dt.dt; } void windowDraw(Window * window) { glXSwapBuffers(window->x.display, window->x.window); usleep(((float)1 / (window->fps + 1)) * 1e6); clock_gettime(CLOCK_MONOTONIC, window->dt.t2); window->dt.dt = (double) (window->dt.t2->tv_sec - window->dt.t1->tv_sec) + (window->dt.t2->tv_nsec - window->dt.t1->tv_nsec) / 1e9; clock_gettime(CLOCK_MONOTONIC, window->dt.t1); } void resetIOState(Window * window) { memcpy(window->io.last, window->io.current, WINDOW_KEY_COUNT); } void windowHandleEvents(Window * window) { XEvent xev = {0}; resetIOState(window); while (XPending(window->x.display)) { if (XCheckWindowEvent(window->x.display, window->x.window, window->x.eventMask, &xev)) { windowEventHandler(window, xev); } } } void closeWindow(Window * window) { glXMakeCurrent(window->x.display, None, None); glXDestroyContext(window->x.display, window->glx.context); XFreeColormap(window->x.display, window->x.cmap); XDestroyWindow(window->x.display, window->x.window); XCloseDisplay(window->x.display); free(window->dt.t1); free(window->dt.t2); free(window); } bool windowKeyPressed(Window * window, char * key) { KeySym keysym = XStringToKeysym(key); if (keysym == NoSymbol) return false; KeyCode keycode = XKeysymToKeycode(window->x.display, keysym); return !(window->io.last[keycode >> 3] & (1 << (keycode & 7))) && (window->io.current[keycode >> 3] & (1 << (keycode & 7))); } bool windowKeyHeld(Window * window, char * key) { KeySym keysym = XStringToKeysym(key); if (keysym == NoSymbol) return false; KeyCode keycode = XKeysymToKeycode(window->x.display, keysym); return (window->io.last[keycode >> 3] & (1 << (keycode & 7))) && (window->io.current[keycode >> 3] & (1 << (keycode & 7))); } bool windowKeyReleased(Window * window, char * key) { KeySym keysym = XStringToKeysym(key); if (keysym == NoSymbol) return false; KeyCode keycode = XKeysymToKeycode(window->x.display, keysym); return (window->io.last[keycode >> 3] & (1 << (keycode & 7))) && !(window->io.current[keycode >> 3] & (1 << (keycode & 7))); } bool windowKeyShift(Window * window) { KeyCode sl = XKeysymToKeycode(window->x.display, XK_Shift_L); KeyCode sr = XKeysymToKeycode(window->x.display, XK_Shift_R); return (window->io.last[sl >> 3] & (1 << (sl & 7))) || (window->io.current[sr >> 3] & (1 << (sr & 7))); } bool windowKeyCtrl(Window * window) { KeyCode cl = XKeysymToKeycode(window->x.display, XK_Control_L); KeyCode cr = XKeysymToKeycode(window->x.display, XK_Control_R); return (window->io.last[cl >> 3] & (1 << (cl & 7))) || (window->io.current[cr >> 3] & (1 << (cr & 7))); } bool windowKeyAlt(Window * window) { KeyCode al = XKeysymToKeycode(window->x.display, XK_Alt_L); KeyCode ar = XKeysymToKeycode(window->x.display, XK_Alt_R); return (window->io.last[al >> 3] & (1 << (al & 7))) || (window->io.current[ar >> 3] & (1 << (ar & 7))); } #else #include #include //TODO: Change this every time we open a window #define WND_CLASS_NAME "OpenWindowWndClass" void windowDefaultEventHandler(Window * window, unsigned int event, WPARAM wParam, LPARAM lParam) { (void) wParam; (void) lParam; switch (event) { case WM_KEYDOWN: case WM_KEYUP: GetKeyboardState(window->io.current); break; case WM_TIMER: InvalidateRect(window->wgl.window, NULL, FALSE); break; } } LRESULT CALLBACK windowEventHandler(HWND hwnd, unsigned int event, WPARAM wParam, LPARAM lParam) { Window * window = (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (window == NULL) goto uninit; windowDefaultEventHandler(window, event, wParam, lParam); uninit: return (DefWindowProc(hwnd, event, wParam, lParam)); } int init(Window * window, const char * title, size_t width, size_t height) { window->wgl.instance = GetModuleHandle(NULL); WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_CLASSDC; wcex.lpfnWndProc = windowEventHandler; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = window->wgl.instance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = 0; wcex.lpszMenuName = NULL; wcex.lpszClassName = WND_CLASS_NAME; wcex.hIconSm = NULL; RegisterClassEx(&wcex); window->wgl.window = CreateWindow( WND_CLASS_NAME, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, window->wgl.instance, NULL ); if (window->wgl.window == NULL) { fprintf(stderr, "Failed to create window!\n"); return -1; } ShowWindow(window->wgl.window, SW_SHOW); SetWindowLongPtr(window->wgl.window, GWLP_USERDATA, (LONG_PTR)window); window->wgl.display = GetDC(window->wgl.window); PIXELFORMATDESCRIPTOR pfd = {0}; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cRedBits = 0; pfd.cRedShift = 0; pfd.cGreenBits = 0; pfd.cGreenShift = 0; pfd.cBlueBits = 0; pfd.cBlueShift = 0; pfd.cAlphaBits = 0; pfd.cAlphaShift = 0; pfd.cAccumBits = 0; pfd.cAccumRedBits = 0; pfd.cAccumGreenBits = 0; pfd.cAccumBlueBits = 0; pfd.cAccumAlphaBits = 0; pfd.cDepthBits = 24; pfd.cStencilBits = 8; pfd.cAuxBuffers = 0; pfd.iLayerType = PFD_MAIN_PLANE; pfd.bReserved = 0; pfd.dwLayerMask = 0; pfd.dwVisibleMask = 0; pfd.dwDamageMask = 0; int format = ChoosePixelFormat(window->wgl.display, &pfd); SetPixelFormat(window->wgl.display, format, &pfd); HGLRC tempContext = wglCreateContext(window->wgl.display); if (tempContext == NULL) return -1; if (!wglMakeCurrent(window->wgl.display, tempContext)) return -1; PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void *)wglGetProcAddress("wglCreateContextAttribsARB"); if (wglCreateContextAttribsARB == NULL) return -1; int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MAJOR, WGL_CONTEXT_MINOR_VERSION_ARB, OPENWINDOW_OPENGL_VERSSION_MINOR, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 }; window->wgl.context = wglCreateContextAttribsARB(window->wgl.display, 0, attribs); if (window->wgl.context == NULL) { fprintf(stderr, "Failed to create wgl context!\n"); return -1; } wglMakeCurrent(window->wgl.display, NULL); wglDeleteContext(tempContext); wglMakeCurrent(window->wgl.display, window->wgl.context); SetTimer(window->wgl.window, 1, (1 / window->fps) * 1000, NULL); //TODO: Check for errors PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (void *)wglGetProcAddress("wglSwapIntervalEXT"); if (wglSwapIntervalEXT) wglSwapIntervalEXT(0); return 0; } Window * openWindow(const char * name, size_t width, size_t height) { Window * w = calloc(1, sizeof(Window)); w->fps = WINDOW_DEFAULT_FPS; if (init(w, name, width, height) < 0) { fprintf(stderr, "Failed to initialize wgl!\n"); exit(1); } if (initOpenGL() < 0) { fprintf(stderr, "Failed to initialize window: GLAD/OPENGL error!\n"); exit(1); } return w; } void windowSetFps(Window * window, uint32_t fps) { window->fps = fps; } double windowGetDeltaTime(Window * window) { return window->dt.dt; } void windowDraw(Window * window) { SwapBuffers(window->wgl.display); Sleep((uint32_t)(((double)1 / (window->fps + 1)) * 1e3)); QueryPerformanceFrequency(&window->dt.freq); QueryPerformanceCounter(&window->dt.t2); window->dt.dt = (double) (window->dt.t2.QuadPart - window->dt.t1.QuadPart) / window->dt.freq.QuadPart; QueryPerformanceCounter(&window->dt.t1); } void resetIOState(Window * window) { memcpy(window->io.last, window->io.current, WINDOW_KEY_COUNT); } void windowHandleEvents(Window * window) { (void) window; resetIOState(window); MSG msg = {0}; GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } void closeWindow(Window * window) { wglMakeCurrent(NULL, NULL); wglDeleteContext(window->wgl.context); ReleaseDC(window->wgl.window, window->wgl.display); DestroyWindow(window->wgl.window); UnregisterClass(WND_CLASS_NAME, window->wgl.instance); free(window); } bool windowKeyPressed(Window * window, int key) { return !(window->io.last[key] & 0x80) && (window->io.current[key] & 0x80); } bool windowKeyHeld(Window * window, int key) { return (window->io.last[key] & 0x80) && (window->io.current[key] & 0x80); } bool windowKeyReleased(Window * window, int key) { return (window->io.last[key] & 0x80) && !(window->io.current[key] & 0x80); } bool windowKeyShift(Window * window) { return window->io.current[VK_SHIFT] & 0x80; } bool windowKeyCtrl(Window * window) { return window->io.current[VK_CONTROL] & 0x80; } bool windowKeyAlt(Window * window) { return window->io.current[VK_MENU] & 0x80; } #endif // _WIN32 int getOpenGLProcs(void) { return gladLoadGL(); }