1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
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;
}
|