summaryrefslogtreecommitdiffstats
path: root/src/glfw_impl.zig
blob: 9b7937445940f77c3787c4be4934643c7aa87dd9 (plain)
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
const c = @import("c.zig");
const builtin = @import("builtin");
pub const ClientApi = enum {
    Unknown,
    OpenGL,
    Vulkan,
};

// Data
var g_Window: ?*c.GLFWwindow = null;
var g_ClientApi: ClientApi = .Unknown;
var g_Time: f32 = 0.0;
var g_MouseJustPressed = [_]bool{ false } ** 5;
var g_MouseCursors = [_]?*c.GLFWcursor{ null } ** @enumToInt(c.ImGuiMouseCursor_COUNT);
var g_WantUpdateMonitors = true;

// Chain GLFW callbacks for main viewport:
// our callbacks will call the user's previously installed callbacks, if any.
var g_PrevUserCallbackMousebutton: ?c.GLFWmousebuttonfun = null;
var g_PrevUserCallbackScroll: ?c.GLFWscrollfun = null;
var g_PrevUserCallbackKey: ?c.GLFWkeyfun = null;
var g_PrevUserCallbackChar: ?c.GLFWcharfun = null;

pub fn Init(window: *c.GLFWwindow, install_callbacks: bool, client_api: ClientApi) void {
  g_Window = window;
  g_Time = 0.0;

  // Setup back-end capabilities flags
  const io = c.igGetIO();
  io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasMouseCursors); // We can honor GetMouseCursor() values (optional)
  io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_HasSetMousePos); // We can honor io.WantSetMousePos requests (optional, rarely used)
  io.*.BackendFlags |= @enumToInt(c.ImGuiBackendFlags_PlatformHasViewports); // We can create multi-viewports on the Platform side (optional)
  if (@hasField(c, "GLFW_HAS_GLFW_HOVERED") and builtin.os == builtin.Os.windows) {
    io.*.BackendFlags |= @enumToInt(ImGuiBackendFlags_HasMouseHoveredViewport); // We can set io.MouseHoveredViewport correctly (optional, not easy)
  }
  io.*.BackendPlatformName = c"imgui_impl_glfw.zig";

  // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Tab)] = c.GLFW_KEY_TAB;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_LeftArrow)] = c.GLFW_KEY_LEFT;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_RightArrow)] = c.GLFW_KEY_RIGHT;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_UpArrow)] = c.GLFW_KEY_UP;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_DownArrow)] = c.GLFW_KEY_DOWN;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_PageUp)] = c.GLFW_KEY_PAGE_UP;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_PageDown)] = c.GLFW_KEY_PAGE_DOWN;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Home)] = c.GLFW_KEY_HOME;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_End)] = c.GLFW_KEY_END;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Insert)] = c.GLFW_KEY_INSERT;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Delete)] = c.GLFW_KEY_DELETE;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Backspace)] = c.GLFW_KEY_BACKSPACE;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Space)] = c.GLFW_KEY_SPACE;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Enter)] = c.GLFW_KEY_ENTER;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Escape)] = c.GLFW_KEY_ESCAPE;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_KeyPadEnter)] = c.GLFW_KEY_KP_ENTER;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_A)] = c.GLFW_KEY_A;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_C)] = c.GLFW_KEY_C;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_V)] = c.GLFW_KEY_V;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_X)] = c.GLFW_KEY_X;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Y)] = c.GLFW_KEY_Y;
  io.*.KeyMap[@enumToInt(c.ImGuiKey_Z)] = c.GLFW_KEY_Z;

  // @TODO: Clipboard
  // io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
  // io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
  // io.ClipboardUserData = g_Window;

  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Arrow)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR);
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_TextInput)] = c.glfwCreateStandardCursor(c.GLFW_IBEAM_CURSOR);
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeAll)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNS)] = c.glfwCreateStandardCursor(c.GLFW_VRESIZE_CURSOR);
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeEW)] = c.glfwCreateStandardCursor(c.GLFW_HRESIZE_CURSOR);
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNESW)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_ResizeNWSE)] = c.glfwCreateStandardCursor(c.GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this.
  g_MouseCursors[@enumToInt(c.ImGuiMouseCursor_Hand)] = c.glfwCreateStandardCursor(c.GLFW_HAND_CURSOR);

  // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
  g_PrevUserCallbackMousebutton = null;
  g_PrevUserCallbackScroll = null;
  g_PrevUserCallbackKey = null;
  g_PrevUserCallbackChar = null;
  if (install_callbacks) {
    g_PrevUserCallbackMousebutton = c.glfwSetMouseButtonCallback(window, Callback_MouseButton);
    g_PrevUserCallbackScroll = c.glfwSetScrollCallback(window, Callback_Scroll);
    g_PrevUserCallbackKey = c.glfwSetKeyCallback(window, Callback_Key);
    g_PrevUserCallbackChar = c.glfwSetCharCallback(window, Callback_Char);
  }

  // Our mouse update function expect PlatformHandle to be filled for the main viewport
  const main_viewport = c.igGetMainViewport();
  main_viewport.*.PlatformHandle = g_Window;
  if (builtin.os == builtin.Os.windows) {
    main_viewport.*.PlatformHandleRaw = c.glfwGetWin32Window(g_Window);
  }

  // @TODO: Platform Interface (Viewport)
  // if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable != 0)
  //    ImGui_ImplGlfw_InitPlatformInterface();

  g_ClientApi = client_api;
}

pub fn Shutdown() void {
  // @TODO: Platform Interface (Viewport)
  // ImGui_ImplGlfw_ShutdownPlatformInterface();

  for (g_MouseCursors) |*cursor| {
    c.glfwDestroyCursor(cursor.*);
    cursor.* = null;
  }
  g_ClientApi = .Unknown;
}

pub fn NewFrame() void {
  const io = c.igGetIO();
  // @TODO: assert font atlas

  var w : c_int = undefined;
  var h : c_int = undefined;
  var display_w : c_int = undefined;
  var display_h : c_int = undefined;
  c.glfwGetWindowSize(g_Window, &w, &h);
  c.glfwGetFramebufferSize(g_Window, &display_w, &display_h);
  io.*.DisplaySize = c.ImVec2{ .x = @intToFloat(f32, w), .y = @intToFloat(f32, h) };
}

// GLFW Callbacks
extern fn Callback_MouseButton(window: ?*c.GLFWwindow, button: c_int, action: c_int, mods: c_int) void {
  // @TODO: delegate up

  if (button < 0) 
    return;

  const button_u = @intCast(usize, button);
  if (action == c.GLFW_PRESS and button_u < g_MouseJustPressed.len)
    g_MouseJustPressed[button_u] = true;
}

extern fn Callback_Scroll(window: ?*c.GLFWwindow, dx: f64, dy: f64) void {
  // @TODO: delegate up

  const io = c.igGetIO();
  io.*.MouseWheelH += @floatCast(f32, dx);
  io.*.MouseWheel  += @floatCast(f32, dy);
}

extern fn Callback_Key(window: ?*c.GLFWwindow, key: c_int, scancode: c_int, action: c_int, modifiers: c_int) void {
  // @TODO: delegate up

  if (key < 0)
    unreachable;

  const key_u = @intCast(usize, key);

  const io = c.igGetIO();
  if (action == c.GLFW_PRESS)
    io.*.KeysDown[key_u] = true;
  if (action == c.GLFW_RELEASE)
    io.*.KeysDown[key_u] = false;

  // Modifiers are not reliable across systems
  io.*.KeyCtrl = io.*.KeysDown[c.GLFW_KEY_LEFT_CONTROL] or io.*.KeysDown[c.GLFW_KEY_RIGHT_CONTROL];
  io.*.KeyShift = io.*.KeysDown[c.GLFW_KEY_LEFT_SHIFT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SHIFT];
  io.*.KeyAlt = io.*.KeysDown[c.GLFW_KEY_LEFT_ALT] or io.*.KeysDown[c.GLFW_KEY_RIGHT_ALT];
  io.*.KeySuper = io.*.KeysDown[c.GLFW_KEY_LEFT_SUPER] or io.*.KeysDown[c.GLFW_KEY_RIGHT_SUPER];
}

extern fn Callback_Char(window: ?*c.GLFWwindow, char: c_uint) void {
  // @TODO: delegate up

  const io = c.igGetIO();
  c.ImGuiIO_AddInputCharacter(io, char);
}