const std = @import("std"); const c = @import("c.zig").c; pub const X11 = struct { display: ?*c.Display = null, root: c.Window = 0, window: c.Window = 0, gl_context: c.GLXContext = null, original_focus_window: c.Window = 0, screen_width: i32 = 0, screen_height: i32 = 0, refresh_rate: i32 = 60, windowed: bool = false, wm_delete_window: c.Atom = 0, pub fn init() !X11 { const display = c.XOpenDisplay(null); if (display == null) { std.debug.print("error: failed to open display\n", .{}); return error.X11OpenDisplayFailed; } _ = c.XSetErrorHandler(x11ErrorHandler); const root = c.XDefaultRootWindow(display); var root_attrs: c.XWindowAttributes = undefined; _ = c.XGetWindowAttributes(display, root, &root_attrs); const sc = c.XRRGetScreenInfo(display, root); const rr = c.XRRConfigCurrentRate(sc); c.XRRFreeScreenConfigInfo(sc); std.debug.print("screen: {d}x{d} @ {d}hz\n", .{ root_attrs.width, root_attrs.height, rr }); return .{ .display = display, .root = root, .screen_width = root_attrs.width, .screen_height = root_attrs.height, .refresh_rate = if (rr > 0) rr else 60, }; } pub fn checkGlx(self: *X11) !void { var glx_major: i32 = undefined; var glx_minor: i32 = undefined; if (c.glXQueryVersion(self.display, &glx_major, &glx_minor) == 0 or (glx_major == 1 and glx_minor < 3) or (glx_major < 1)) { std.debug.print("error: invalid glx version\n", .{}); return error.InvalidGlxVersion; } std.debug.print("glx version: {d}.{d}\n", .{ glx_major, glx_minor }); } pub fn createWindow(self: *X11, windowed: bool) !void { self.windowed = windowed; const attrs = [_]c_int{ c.GLX_RGBA, c.GLX_DEPTH_SIZE, 24, c.GLX_DOUBLEBUFFER, 0 }; const vi = c.glXChooseVisual(self.display, 0, @constCast(&attrs)); if (vi == null) { std.debug.print("error: no appropriate visual found\n", .{}); return error.NoAppropriateVisual; } defer _ = c.XFree(vi); std.debug.print("visual id: 0x{x}\n", .{vi.*.visualid}); var swa: c.XSetWindowAttributes = undefined; swa.colormap = c.XCreateColormap(self.display, self.root, vi.*.visual, c.AllocNone); swa.event_mask = c.ButtonPressMask | c.ButtonReleaseMask | c.KeyPressMask | c.KeyReleaseMask | c.PointerMotionMask | c.ExposureMask | c.ClientMessage; var mask: c_ulong = c.CWColormap | c.CWEventMask; if (!windowed) { swa.override_redirect = 1; swa.save_under = 1; mask |= c.CWOverrideRedirect | c.CWSaveUnder; } self.window = c.XCreateWindow( self.display, self.root, 0, 0, @intCast(self.screen_width), @intCast(self.screen_height), 0, vi.*.depth, c.InputOutput, vi.*.visual, mask, &swa, ); _ = c.XStoreName(self.display, self.window, "boomer"); var class_hint = c.XClassHint{ .res_name = @constCast("boomer"), .res_class = @constCast("Boomer"), }; _ = c.XSetClassHint(self.display, self.window, &class_hint); if (windowed) { self.wm_delete_window = c.XInternAtom(self.display, "WM_DELETE_WINDOW", c.False); _ = c.XSetWMProtocols(self.display, self.window, &self.wm_delete_window, 1); } _ = c.XMapWindow(self.display, self.window); self.gl_context = c.glXCreateContext(self.display, vi, null, c.GL_TRUE); _ = c.glXMakeCurrent(self.display, self.window, self.gl_context); var revert: i32 = 0; _ = c.XGetInputFocus(self.display, &self.original_focus_window, &revert); } pub fn grabFocus(self: *X11) void { if (!self.windowed) _ = c.XSetInputFocus(self.display, self.window, c.RevertToParent, c.CurrentTime); } pub fn restoreFocus(self: *X11) void { _ = c.XSetInputFocus(self.display, self.original_focus_window, c.RevertToParent, c.CurrentTime); _ = c.XSync(self.display, c.False); } pub fn getWindowSize(self: *X11, w: *i32, h: *i32) void { var wa: c.XWindowAttributes = undefined; _ = c.XGetWindowAttributes(self.display, self.window, &wa); w.* = wa.width; h.* = wa.height; } pub fn deinit(self: *X11) void { if (self.gl_context != null) { _ = c.glXMakeCurrent(self.display, 0, null); c.glXDestroyContext(self.display, self.gl_context); } if (self.window != 0) _ = c.XDestroyWindow(self.display, self.window); if (self.display != null) _ = c.XCloseDisplay(self.display); } }; export fn x11ErrorHandler(d: ?*c.Display, ev: ?*c.XErrorEvent) callconv(.c) c_int { var buf: [256]u8 = undefined; _ = c.XGetErrorText(d, ev.?.error_code, &buf, @intCast(buf.len)); const len = std.mem.indexOfScalar(u8, &buf, 0) orelse buf.len; std.debug.print("error: x11: {s}\n", .{buf[0..len]}); return 0; }