kitty/kitty/glfw.c
2020-03-19 14:13:23 +05:30

1559 lines
52 KiB
C

/*
* Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "state.h"
#include "fonts.h"
#include "monotonic.h"
#include <structmember.h>
#include "glfw-wrapper.h"
extern bool cocoa_make_window_resizable(void *w, bool);
extern void cocoa_focus_window(void *w);
extern void cocoa_create_global_menu(void);
extern void cocoa_hide_window_title(void *w);
extern void cocoa_hide_titlebar(void *w);
extern void cocoa_set_activation_policy(bool);
extern void cocoa_set_titlebar_color(void *w, color_type color);
extern bool cocoa_alt_option_key_pressed(unsigned long);
extern size_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz);
extern monotonic_t cocoa_cursor_blink_interval(void);
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
#endif
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
static void set_os_window_dpi(OSWindow *w);
void
request_tick_callback(void) {
glfwPostEmptyEvent();
}
static int min_width = 100, min_height = 100;
void
update_os_window_viewport(OSWindow *window, bool notify_boss) {
int w, h, fw, fh;
glfwGetFramebufferSize(window->handle, &fw, &fh);
glfwGetWindowSize(window->handle, &w, &h);
double xdpi = window->logical_dpi_x, ydpi = window->logical_dpi_y;
set_os_window_dpi(window);
if (fw == window->viewport_width && fh == window->viewport_height && w == window->window_width && h == window->window_height && xdpi == window->logical_dpi_x && ydpi == window->logical_dpi_y) {
return; // no change, ignore
}
if (w <= 0 || h <= 0 || fw / w > 5 || fh / h > 5 || fw < min_width || fh < min_height || fw < w || fh < h) {
log_error("Invalid geometry ignored: framebuffer: %dx%d window: %dx%d\n", fw, fh, w, h);
if (!window->viewport_updated_at_least_once) {
window->viewport_width = min_width; window->viewport_height = min_height;
window->window_width = min_width; window->window_height = min_height;
window->viewport_x_ratio = 1; window->viewport_y_ratio = 1;
window->viewport_size_dirty = true;
if (notify_boss) {
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, Py_False);
}
}
return;
}
window->viewport_updated_at_least_once = true;
window->viewport_width = fw; window->viewport_height = fh;
double xr = window->viewport_x_ratio, yr = window->viewport_y_ratio;
window->viewport_x_ratio = w > 0 ? (double)window->viewport_width / (double)w : xr;
window->viewport_y_ratio = h > 0 ? (double)window->viewport_height / (double)h : yr;
bool dpi_changed = (xr != 0.0 && xr != window->viewport_x_ratio) || (yr != 0.0 && yr != window->viewport_y_ratio) || (xdpi != window->logical_dpi_x) || (ydpi != window->logical_dpi_y);
window->viewport_size_dirty = true;
window->viewport_width = MAX(window->viewport_width, min_width);
window->viewport_height = MAX(window->viewport_height, min_height);
window->window_width = MAX(w, min_width);
window->window_height = MAX(h, min_height);
if (notify_boss) {
call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False);
}
}
void
log_event(const char *format, ...) {
if (format)
{
va_list vl;
fprintf(stderr, "[%.4f] ", monotonic_t_to_s_double(glfwGetTime()));
va_start(vl, format);
vfprintf(stderr, format, vl);
va_end(vl);
fprintf(stderr, "\n");
}
}
// callbacks {{{
void
update_os_window_references() {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (w->handle) glfwSetWindowUserPointer(w->handle, w);
}
}
static inline bool
set_callback_window(GLFWwindow *w) {
global_state.callback_os_window = glfwGetWindowUserPointer(w);
if (global_state.callback_os_window) return true;
for (size_t i = 0; i < global_state.num_os_windows; i++) {
if ((GLFWwindow*)(global_state.os_windows[i].handle) == w) {
global_state.callback_os_window = global_state.os_windows + i;
return true;
}
}
return false;
}
static inline bool
is_window_ready_for_callbacks(void) {
OSWindow *w = global_state.callback_os_window;
if (w->num_tabs == 0) return false;
Tab *t = w->tabs + w->active_tab;
if (t->num_windows == 0) return false;
return true;
}
#define WINDOW_CALLBACK(name, fmt, ...) call_boss(name, "K" fmt, global_state.callback_os_window->id, __VA_ARGS__)
static inline void
show_mouse_cursor(GLFWwindow *w) {
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
}
void
blank_os_window(OSWindow *w) {
color_type color = OPT(background);
if (w->num_tabs > 0) {
Tab *t = w->tabs + w->active_tab;
if (t->num_windows == 1) {
Window *w = t->windows + t->active_window;
Screen *s = w->render_data.screen;
if (s) {
color = colorprofile_to_color(s->color_profile, s->color_profile->overridden.default_bg, s->color_profile->configured.default_bg);
}
}
}
blank_canvas(w->is_semi_transparent ? w->background_opacity : 1.0f, color);
}
static void
window_close_callback(GLFWwindow* window) {
if (!set_callback_window(window)) return;
global_state.has_pending_closes = true;
request_tick_callback();
global_state.callback_os_window = NULL;
}
#ifdef __APPLE__
static void
application_quit_canary_close_requested(GLFWwindow *window UNUSED) {
global_state.has_pending_closes = true;
request_tick_callback();
}
#endif
static void
window_occlusion_callback(GLFWwindow *window, bool occluded UNUSED) {
if (!set_callback_window(window)) return;
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
window_iconify_callback(GLFWwindow *window, int iconified UNUSED) {
if (!set_callback_window(window)) return;
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
live_resize_callback(GLFWwindow *w, bool started) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->live_resize.from_os_notification = true;
global_state.callback_os_window->live_resize.in_progress = true;
global_state.has_pending_resizes = true;
if (!started) {
global_state.callback_os_window->live_resize.os_says_resize_complete = true;
request_tick_callback();
}
global_state.callback_os_window = NULL;
}
static void
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
if (!set_callback_window(w)) return;
if (width >= min_width && height >= min_height) {
OSWindow *window = global_state.callback_os_window;
global_state.has_pending_resizes = true;
window->live_resize.in_progress = true;
window->live_resize.last_resize_event_at = monotonic();
window->live_resize.width = MAX(0, width); window->live_resize.height = MAX(0, height);
window->live_resize.num_of_resize_events++;
make_os_window_context_current(window);
update_surface_size(width, height, window->offscreen_texture_id);
request_tick_callback();
} else log_error("Ignoring resize request for tiny size: %dx%d", width, height);
global_state.callback_os_window = NULL;
}
static void
dpi_change_callback(GLFWwindow *w, float x_scale UNUSED, float y_scale UNUSED) {
if (!set_callback_window(w)) return;
// Ensure update_os_window_viewport() is called in the near future, it will
// take care of DPI changes.
OSWindow *window = global_state.callback_os_window;
window->live_resize.in_progress = true; global_state.has_pending_resizes = true;
window->live_resize.last_resize_event_at = monotonic();
global_state.callback_os_window = NULL;
request_tick_callback();
}
static void
refresh_callback(GLFWwindow *w) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->is_damaged = true;
global_state.callback_os_window = NULL;
request_tick_callback();
}
static int mods_at_last_key_or_button_event = 0;
static void
key_callback(GLFWwindow *w, GLFWkeyevent *ev) {
if (!set_callback_window(w)) return;
mods_at_last_key_or_button_event = ev->mods;
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST) {
global_state.callback_os_window->is_key_pressed[ev->key] = ev->action == GLFW_RELEASE ? false : true;
}
if (is_window_ready_for_callbacks()) on_key_input(ev);
global_state.callback_os_window = NULL;
request_tick_callback();
}
static void
cursor_enter_callback(GLFWwindow *w, int entered) {
if (!set_callback_window(w)) return;
if (entered) {
show_mouse_cursor(w);
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) enter_event();
request_tick_callback();
}
global_state.callback_os_window = NULL;
}
static void
mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
mods_at_last_key_or_button_event = mods;
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (button >= 0 && (unsigned int)button < arraysz(global_state.callback_os_window->mouse_button_pressed)) {
global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
if (is_window_ready_for_callbacks()) mouse_event(button, mods, action);
}
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
cursor_pos_callback(GLFWwindow *w, double x, double y) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
global_state.callback_os_window->cursor_blink_zero_time = now;
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
if (is_window_ready_for_callbacks()) mouse_event(-1, mods_at_last_key_or_button_event, -1);
request_tick_callback();
global_state.callback_os_window = NULL;
}
static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags);
request_tick_callback();
global_state.callback_os_window = NULL;
}
static id_type focus_counter = 0;
static void
window_focus_callback(GLFWwindow *w, int focused) {
global_state.active_drag_in_window = 0;
if (!set_callback_window(w)) return;
global_state.callback_os_window->is_focused = focused ? true : false;
if (focused) {
show_mouse_cursor(w);
focus_in_event();
global_state.callback_os_window->last_focused_counter = ++focus_counter;
}
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
global_state.callback_os_window->cursor_blink_zero_time = now;
if (is_window_ready_for_callbacks()) {
WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False);
glfwUpdateIMEState(global_state.callback_os_window->handle, 1, focused, 0, 0, 0);
}
request_tick_callback();
global_state.callback_os_window = NULL;
}
static int
drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
if (!set_callback_window(w)) return 0;
if (!data) {
if (strcmp(mime, "text/uri-list") == 0) return 3;
if (strcmp(mime, "text/plain;charset=utf-8") == 0) return 2;
if (strcmp(mime, "text/plain") == 0) return 1;
return 0;
}
WINDOW_CALLBACK(on_drop, "sy#", mime, data, (int)sz);
request_tick_callback();
/* PyObject *s = PyTuple_New(count); */
/* if (s) { */
/* for (int i = 0; i < count; i++) PyTuple_SET_ITEM(s, i, PyUnicode_FromString(strings[i])); */
/* WINDOW_CALLBACK(on_drop, "O", s); */
/* Py_CLEAR(s); */
/* request_tick_callback(); */
/* } */
global_state.callback_os_window = NULL;
return 0;
}
// }}}
void
set_mouse_cursor(MouseShape type) {
if (global_state.callback_os_window) {
GLFWwindow *w = (GLFWwindow*)global_state.callback_os_window->handle;
switch(type) {
case HAND:
glfwSetCursor(w, click_cursor);
break;
case ARROW:
glfwSetCursor(w, arrow_cursor);
break;
default:
glfwSetCursor(w, standard_cursor);
break;
}
}
}
static GLFWimage logo = {0};
static PyObject*
set_default_window_icon(PyObject UNUSED *self, PyObject *args) {
Py_ssize_t sz;
const char *logo_data;
if(!PyArg_ParseTuple(args, "s#ii", &(logo_data), &sz, &(logo.width), &(logo.height))) return NULL;
sz = (MAX(logo.width * logo.height, sz));
logo.pixels = malloc(sz);
if (logo.pixels) memcpy(logo.pixels, logo_data, sz);
Py_RETURN_NONE;
}
void
make_os_window_context_current(OSWindow *w) {
GLFWwindow *current_context = glfwGetCurrentContext();
if (w->handle != current_context) {
glfwMakeContextCurrent(w->handle);
}
}
static inline void
get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) {
if (w) glfwGetWindowContentScale(w, xscale, yscale);
else {
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
if (monitor) glfwGetMonitorContentScale(monitor, xscale, yscale);
}
// check for zero, negative, NaN or excessive values of xscale/yscale
if (*xscale <= 0.0001 || *xscale != *xscale || *xscale >= 24) *xscale = 1.0;
if (*yscale <= 0.0001 || *yscale != *yscale || *yscale >= 24) *yscale = 1.0;
#ifdef __APPLE__
const double factor = 72.0;
#else
const double factor = 96.0;
#endif
*xdpi = *xscale * factor;
*ydpi = *yscale * factor;
}
static inline void
get_window_dpi(GLFWwindow *w, double *x, double *y) {
float xscale = 1, yscale = 1;
get_window_content_scale(w, &xscale, &yscale, x, y);
}
static void
set_os_window_dpi(OSWindow *w) {
get_window_dpi(w->handle, &w->logical_dpi_x, &w->logical_dpi_y);
}
static inline bool
do_toggle_fullscreen(OSWindow *w) {
int width, height, x, y;
glfwGetWindowSize(w->handle, &width, &height);
glfwGetWindowPos(w->handle, &x, &y);
if (glfwToggleFullscreen(w->handle, 0)) {
w->before_fullscreen.is_set = true;
w->before_fullscreen.w = width; w->before_fullscreen.h = height; w->before_fullscreen.x = x; w->before_fullscreen.y = y;
return true;
}
if (w->before_fullscreen.is_set) {
glfwSetWindowSize(w->handle, w->before_fullscreen.w, w->before_fullscreen.h);
glfwSetWindowPos(w->handle, w->before_fullscreen.x, w->before_fullscreen.y);
}
return false;
}
static bool
toggle_fullscreen_for_os_window(OSWindow *w) {
if (w && w->handle) {
#ifdef __APPLE__
if (!OPT(macos_traditional_fullscreen)) return glfwToggleFullscreen(w->handle, 1);
#endif
return do_toggle_fullscreen(w);
}
return false;
}
static bool
toggle_maximized_for_os_window(OSWindow *w) {
bool maximized = false;
if (w && w->handle) {
if (glfwGetWindowAttrib(w->handle, GLFW_MAXIMIZED)) {
glfwRestoreWindow(w->handle);
} else {
glfwMaximizeWindow(w->handle);
maximized = true;
}
}
return maximized;
}
#ifdef __APPLE__
static int
filter_option(int key UNUSED, int mods, unsigned int native_key UNUSED, unsigned long flags) {
if ((mods == GLFW_MOD_ALT) || (mods == (GLFW_MOD_ALT | GLFW_MOD_SHIFT))) {
if (OPT(macos_option_as_alt) == 3) return 1;
if (cocoa_alt_option_key_pressed(flags)) return 1;
}
return 0;
}
static GLFWwindow *application_quit_canary = NULL;
static bool
on_application_reopen(int has_visible_windows) {
if (has_visible_windows) return true;
set_cocoa_pending_action(NEW_OS_WINDOW, NULL);
return false;
}
static bool
intercept_cocoa_fullscreen(GLFWwindow *w) {
if (!OPT(macos_traditional_fullscreen) || !set_callback_window(w)) return false;
toggle_fullscreen_for_os_window(global_state.callback_os_window);
global_state.callback_os_window = NULL;
return true;
}
#endif
void
set_titlebar_color(OSWindow *w, color_type color) {
if (w->handle && (!w->last_titlebar_color || (w->last_titlebar_color & 0xffffff) != (color & 0xffffff))) {
w->last_titlebar_color = (1 << 24) | (color & 0xffffff);
#ifdef __APPLE__
cocoa_set_titlebar_color(glfwGetCocoaWindow(w->handle), color);
#endif
}
}
static inline PyObject*
native_window_handle(GLFWwindow *w) {
#ifdef __APPLE__
void *ans = glfwGetCocoaWindow(w);
return PyLong_FromVoidPtr(ans);
#endif
if (glfwGetX11Window) return PyLong_FromLong((long)glfwGetX11Window(w));
return Py_None;
}
static PyObject*
create_os_window(PyObject UNUSED *self, PyObject *args) {
int x = -1, y = -1;
char *title, *wm_class_class, *wm_class_name;
PyObject *load_programs = NULL, *get_window_size, *pre_show_callback;
if (!PyArg_ParseTuple(args, "OOsss|Oii", &get_window_size, &pre_show_callback, &title, &wm_class_name, &wm_class_class, &load_programs, &x, &y)) return NULL;
static bool is_first_window = true;
if (is_first_window) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_REQUIRED_VERSION_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_REQUIRED_VERSION_MINOR);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
// We don't use depth and stencil buffers
glfwWindowHint(GLFW_DEPTH_BITS, 0);
glfwWindowHint(GLFW_STENCIL_BITS, 0);
if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false);
#ifdef __APPLE__
cocoa_set_activation_policy(OPT(macos_hide_from_tasks));
glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, true);
glfwSetApplicationShouldHandleReopen(on_application_reopen);
glfwSetApplicationWillFinishLaunching(cocoa_create_global_menu);
#endif
}
#ifndef __APPLE__
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, wm_class_name);
glfwWindowHintString(GLFW_X11_CLASS_NAME, wm_class_class);
glfwWindowHintString(GLFW_WAYLAND_APP_ID, wm_class_class);
#endif
if (global_state.num_os_windows >= MAX_CHILDREN) {
PyErr_SetString(PyExc_ValueError, "Too many windows");
return NULL;
}
bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) || OPT(dynamic_background_opacity);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
// We use a temp window to avoid the need to set the window size after
// creation, which causes a resize event and all the associated processing.
// The temp window is used to get the DPI.
glfwWindowHint(GLFW_VISIBLE, false);
GLFWwindow *common_context = global_state.num_os_windows ? global_state.os_windows[0].handle : NULL;
#ifdef __APPLE__
if (is_first_window && !application_quit_canary) {
application_quit_canary = glfwCreateWindow(100, 200, "quit_canary", NULL, NULL);
glfwSetWindowCloseCallback(application_quit_canary, application_quit_canary_close_requested);
}
if (!common_context) common_context = application_quit_canary;
#endif
GLFWwindow *temp_window = NULL;
if (!global_state.is_wayland) {
// On Wayland windows dont get a content scale until they receive an enterEvent anyway
// which wont happen until the event loop ticks, so using a temp window is useless.
temp_window = glfwCreateWindow(640, 480, "temp", NULL, common_context);
if (temp_window == NULL) { fatal("Failed to create GLFW temp window! This usually happens because of old/broken OpenGL drivers. kitty requires working OpenGL 3.3 drivers."); }
}
float xscale, yscale;
double xdpi, ydpi;
get_window_content_scale(temp_window, &xscale, &yscale, &xdpi, &ydpi);
FONTS_DATA_HANDLE fonts_data = load_fonts_data(global_state.font_sz_in_pts, xdpi, ydpi);
PyObject *ret = PyObject_CallFunction(get_window_size, "IIddff", fonts_data->cell_width, fonts_data->cell_height, fonts_data->logical_dpi_x, fonts_data->logical_dpi_y, xscale, yscale);
if (ret == NULL) return NULL;
int width = PyLong_AsLong(PyTuple_GET_ITEM(ret, 0)), height = PyLong_AsLong(PyTuple_GET_ITEM(ret, 1));
Py_CLEAR(ret);
// The GLFW Wayland backend cannot create and show windows separately so we
// cannot call the pre_show_callback. See
// https://github.com/glfw/glfw/issues/1268 It doesn't matter since there
// is no startup notification in Wayland anyway. It amazes me that anyone
// uses Wayland as anything other than a butt for jokes.
if (global_state.is_wayland) glfwWindowHint(GLFW_VISIBLE, true);
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window ? temp_window : common_context);
if (temp_window) { glfwDestroyWindow(temp_window); temp_window = NULL; }
if (glfw_window == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
glfwMakeContextCurrent(glfw_window);
if (is_first_window) {
gl_init();
}
bool is_semi_transparent = glfwGetWindowAttrib(glfw_window, GLFW_TRANSPARENT_FRAMEBUFFER);
// blank the window once so that there is no initial flash of color
// changing, in case the background color is not black
blank_canvas(is_semi_transparent ? OPT(background_opacity) : 1.0f, OPT(background));
#ifndef __APPLE__
if (is_first_window) glfwSwapInterval(OPT(sync_to_monitor) && !global_state.is_wayland ? 1 : 0);
#endif
glfwSwapBuffers(glfw_window);
if (!global_state.is_wayland) {
PyObject *pret = PyObject_CallFunction(pre_show_callback, "N", native_window_handle(glfw_window));
if (pret == NULL) return NULL;
Py_DECREF(pret);
if (x != -1 && y != -1) glfwSetWindowPos(glfw_window, x, y);
glfwShowWindow(glfw_window);
}
if (is_first_window) {
PyObject *ret = PyObject_CallFunction(load_programs, "O", is_semi_transparent ? Py_True : Py_False);
if (ret == NULL) return NULL;
Py_DECREF(ret);
#define CC(dest, shape) {\
if (!dest##_cursor) { \
dest##_cursor = glfwCreateStandardCursor(GLFW_##shape##_CURSOR); \
if (dest##_cursor == NULL) { log_error("Failed to create the %s mouse cursor, using default cursor.", #shape); } \
}}
CC(standard, IBEAM); CC(click, HAND); CC(arrow, ARROW);
#undef CC
if (OPT(click_interval) < 0) OPT(click_interval) = glfwGetDoubleClickInterval(glfw_window);
if (OPT(cursor_blink_interval) < 0) {
OPT(cursor_blink_interval) = ms_to_monotonic_t(500ll);
#ifdef __APPLE__
monotonic_t cbi = cocoa_cursor_blink_interval();
if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2;
#endif
}
is_first_window = false;
}
OSWindow *w = add_os_window();
w->handle = glfw_window;
update_os_window_references();
for (size_t i = 0; i < global_state.num_os_windows; i++) {
// On some platforms (macOS) newly created windows don't get the initial focus in event
OSWindow *q = global_state.os_windows + i;
q->is_focused = q == w ? true : false;
}
w->logical_dpi_x = xdpi; w->logical_dpi_y = ydpi;
w->fonts_data = fonts_data;
w->shown_once = true;
w->last_focused_counter = ++focus_counter;
if (OPT(resize_in_steps)) os_window_update_size_increments(w);
#ifdef __APPLE__
if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option);
glfwSetCocoaToggleFullscreenIntercept(glfw_window, intercept_cocoa_fullscreen);
#endif
send_prerendered_sprites_for_window(w);
if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo);
glfwSetCursor(glfw_window, standard_cursor);
update_os_window_viewport(w, false);
// missing pos callback
// missing size callback
glfwSetWindowCloseCallback(glfw_window, window_close_callback);
glfwSetWindowRefreshCallback(glfw_window, refresh_callback);
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
glfwSetWindowOcclusionCallback(glfw_window, window_occlusion_callback);
glfwSetWindowIconifyCallback(glfw_window, window_iconify_callback);
// missing maximize/restore callback
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
glfwSetLiveResizeCallback(glfw_window, live_resize_callback);
glfwSetWindowContentScaleCallback(glfw_window, dpi_change_callback);
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
glfwSetCursorEnterCallback(glfw_window, cursor_enter_callback);
glfwSetScrollCallback(glfw_window, scroll_callback);
glfwSetKeyboardCallback(glfw_window, key_callback);
glfwSetDropCallback(glfw_window, drop_callback);
#ifdef __APPLE__
if (glfwGetCocoaWindow) {
if (OPT(hide_window_decorations) & 2) {
cocoa_hide_titlebar(glfwGetCocoaWindow(glfw_window));
} else if (!(OPT(macos_show_window_title_in) & WINDOW)) {
cocoa_hide_window_title(glfwGetCocoaWindow(glfw_window));
}
cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable));
} else log_error("Failed to load glfwGetCocoaWindow");
#endif
monotonic_t now = monotonic();
w->is_focused = true;
w->cursor_blink_zero_time = now;
w->last_mouse_activity_at = now;
w->is_semi_transparent = is_semi_transparent;
if (want_semi_transparent && !w->is_semi_transparent) {
static bool warned = false;
if (!warned) {
log_error("Failed to enable transparency. This happens when your desktop environment does not support compositing.");
warned = true;
}
}
return PyLong_FromUnsignedLongLong(w->id);
}
void
os_window_update_size_increments(OSWindow *window) {
if (window->handle && window->fonts_data) glfwSetWindowSizeIncrements(
window->handle, window->fonts_data->cell_width, window->fonts_data->cell_height);
}
#ifdef __APPLE__
static inline bool
window_in_same_cocoa_workspace(void *w, size_t *source_workspaces, size_t source_workspace_count) {
static size_t workspaces[64];
size_t workspace_count = cocoa_get_workspace_ids(w, workspaces, arraysz(workspaces));
for (size_t i = 0; i < workspace_count; i++) {
for (size_t s = 0; s < source_workspace_count; s++) {
if (source_workspaces[s] == workspaces[i]) return true;
}
}
return false;
}
static inline void
cocoa_focus_last_window(id_type source_window_id, size_t *source_workspaces, size_t source_workspace_count) {
id_type highest_focus_number = 0;
OSWindow *window_to_focus = NULL;
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (
w->id != source_window_id && w->handle && w->shown_once &&
w->last_focused_counter >= highest_focus_number &&
(!source_workspace_count || window_in_same_cocoa_workspace(glfwGetCocoaWindow(w->handle), source_workspaces, source_workspace_count))
) {
highest_focus_number = w->last_focused_counter;
window_to_focus = w;
}
}
if (window_to_focus) {
glfwFocusWindow(window_to_focus->handle);
}
}
#endif
void
destroy_os_window(OSWindow *w) {
#ifdef __APPLE__
static size_t source_workspaces[64];
size_t source_workspace_count = 0;
#endif
if (w->handle) {
#ifdef __APPLE__
source_workspace_count = cocoa_get_workspace_ids(glfwGetCocoaWindow(w->handle), source_workspaces, arraysz(source_workspaces));
#endif
// Ensure mouse cursor is visible and reset to default shape, needed on macOS
show_mouse_cursor(w->handle);
glfwSetCursor(w->handle, NULL);
glfwDestroyWindow(w->handle);
}
w->handle = NULL;
#ifdef __APPLE__
// On macOS when closing a window, any other existing windows belonging to the same application do not
// automatically get focus, so we do it manually.
cocoa_focus_last_window(w->id, source_workspaces, source_workspace_count);
#endif
}
void
focus_os_window(OSWindow *w, bool also_raise) {
if (w->handle) {
#ifdef __APPLE__
if (!also_raise) cocoa_focus_window(glfwGetCocoaWindow(w->handle));
else glfwFocusWindow(w->handle);
#else
(void)also_raise;
glfwFocusWindow(w->handle);
#endif
}
}
#ifdef __APPLE__
bool
application_quit_requested() {
return !application_quit_canary || glfwWindowShouldClose(application_quit_canary);
}
void
request_application_quit() {
if (application_quit_canary) {
global_state.has_pending_closes = true;
glfwSetWindowShouldClose(application_quit_canary, true);
}
}
#endif
// Global functions {{{
static void
error_callback(int error, const char* description) {
log_error("[glfw error %d]: %s", error, description);
}
#ifndef __APPLE__
static void
dbus_user_notification_activated(uint32_t notification_id, const char* action) {
unsigned long nid = notification_id;
call_boss(dbus_notification_callback, "Oks", Py_True, nid, action);
}
#endif
static PyObject*
glfw_init(PyObject UNUSED *self, PyObject *args) {
const char* path;
int debug_keyboard = 0;
if (!PyArg_ParseTuple(args, "s|p", &path, &debug_keyboard)) return NULL;
const char* err = load_glfw(path);
if (err) { PyErr_SetString(PyExc_RuntimeError, err); return NULL; }
glfwSetErrorCallback(error_callback);
glfwInitHint(GLFW_DEBUG_KEYBOARD, debug_keyboard);
// Joysticks cause slow startup on some linux systems, see
// https://github.com/kovidgoyal/kitty/issues/830
glfwInitHint(GLFW_ENABLE_JOYSTICKS, 0);
OPT(debug_keyboard) = debug_keyboard != 0;
#ifdef __APPLE__
glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, 0);
glfwInitHint(GLFW_COCOA_MENUBAR, 0);
#else
if (glfwDBusSetUserNotificationHandler) {
glfwDBusSetUserNotificationHandler(dbus_user_notification_activated);
}
#endif
PyObject *ans = glfwInit(monotonic_start_time) ? Py_True: Py_False;
if (ans == Py_True) {
OSWindow w = {0};
set_os_window_dpi(&w);
global_state.default_dpi.x = w.logical_dpi_x;
global_state.default_dpi.y = w.logical_dpi_y;
}
Py_INCREF(ans);
return ans;
}
static PyObject*
glfw_terminate(PYNOARG) {
glfwTerminate();
Py_RETURN_NONE;
}
static PyObject*
get_physical_dpi(GLFWmonitor *m) {
int width = 0, height = 0;
glfwGetMonitorPhysicalSize(m, &width, &height);
if (width == 0 || height == 0) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor size"); return NULL; }
const GLFWvidmode *vm = glfwGetVideoMode(m);
if (vm == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get video mode for monitor"); return NULL; }
float dpix = (float)(vm->width / (width / 25.4));
float dpiy = (float)(vm->height / (height / 25.4));
return Py_BuildValue("ff", dpix, dpiy);
}
static PyObject*
glfw_get_physical_dpi(PYNOARG) {
GLFWmonitor *m = glfwGetPrimaryMonitor();
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
return get_physical_dpi(m);
}
static PyObject*
glfw_get_key_name(PyObject UNUSED *self, PyObject *args) {
int key, native_key;
if (!PyArg_ParseTuple(args, "ii", &key, &native_key)) return NULL;
if (!glfwGetKeyName) {
return PyUnicode_FromFormat("key: %d native_key: %d", key, native_key);
}
return Py_BuildValue("z", glfwGetKeyName(key, native_key));
}
static PyObject*
glfw_window_hint(PyObject UNUSED *self, PyObject *args) {
int key, val;
if (!PyArg_ParseTuple(args, "ii", &key, &val)) return NULL;
glfwWindowHint(key, val);
Py_RETURN_NONE;
}
// }}}
static PyObject*
get_clipboard_string(PYNOARG) {
OSWindow *w = current_os_window();
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
return Py_BuildValue("s", "");
}
void
ring_audio_bell(OSWindow *w UNUSED) {
static monotonic_t last_bell_at = -1;
monotonic_t now = monotonic();
if (now - last_bell_at <= ms_to_monotonic_t(100ll)) return;
last_bell_at = now;
#ifdef __APPLE__
if (w->handle) {
glfwWindowBell(w->handle);
}
#else
play_canberra_sound("bell", "kitty bell");
#endif
}
static PyObject*
ring_bell(PYNOARG) {
OSWindow *w = current_os_window();
ring_audio_bell(w);
Py_RETURN_NONE;
}
static PyObject*
get_content_scale_for_window(PYNOARG) {
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
float xscale, yscale;
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
return Py_BuildValue("ff", xscale, yscale);
}
static PyObject*
set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
char *title;
Py_ssize_t sz;
if(!PyArg_ParseTuple(args, "s#", &title, &sz)) return NULL;
OSWindow *w = current_os_window();
if (w) glfwSetClipboardString(w->handle, title);
Py_RETURN_NONE;
}
static PyObject*
toggle_fullscreen(PYNOARG) {
OSWindow *w = current_os_window();
if (!w) Py_RETURN_NONE;
if (toggle_fullscreen_for_os_window(w)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}
static PyObject*
toggle_maximized(PYNOARG) {
OSWindow *w = current_os_window();
if (!w) Py_RETURN_NONE;
if (toggle_maximized_for_os_window(w)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE;
}
static PyObject*
change_os_window_state(PyObject *self UNUSED, PyObject *args) {
char *state;
if (!PyArg_ParseTuple(args, "s", &state)) return NULL;
OSWindow *w = current_os_window();
if (!w || !w->handle) Py_RETURN_NONE;
if (strcmp(state, "maximized") == 0) glfwMaximizeWindow(w->handle);
else if (strcmp(state, "minimized") == 0) glfwIconifyWindow(w->handle);
else { PyErr_SetString(PyExc_ValueError, "Unknown window state"); return NULL; }
Py_RETURN_NONE;
}
void
request_window_attention(id_type kitty_window_id, bool audio_bell) {
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
if (w) {
if (audio_bell) ring_audio_bell(w);
if (OPT(window_alert_on_bell)) glfwRequestWindowAttention(w->handle);
glfwPostEmptyEvent();
}
}
void
set_os_window_title(OSWindow *w, const char *title) {
glfwSetWindowTitle(w->handle, title);
}
void
hide_mouse(OSWindow *w) {
glfwSetInputMode(w->handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}
bool
is_mouse_hidden(OSWindow *w) {
return w->handle && glfwGetInputMode(w->handle, GLFW_CURSOR) == GLFW_CURSOR_HIDDEN;
}
void
swap_window_buffers(OSWindow *os_window) {
glfwSwapBuffers(os_window->handle);
}
void
wakeup_main_loop() {
glfwPostEmptyEvent();
}
void
mark_os_window_for_close(OSWindow* w, bool yes) {
global_state.has_pending_closes = true;
glfwSetWindowShouldClose(w->handle, yes);
}
bool
should_os_window_be_rendered(OSWindow* w) {
return (
glfwGetWindowAttrib(w->handle, GLFW_ICONIFIED) ||
!glfwGetWindowAttrib(w->handle, GLFW_VISIBLE) ||
glfwGetWindowAttrib(w->handle, GLFW_OCCLUDED)
) ? false : true;
}
bool
should_os_window_close(OSWindow* w) {
return glfwWindowShouldClose(w->handle) ? true : false;
}
static PyObject*
primary_monitor_size(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
return Py_BuildValue("ii", mode->width, mode->height);
}
static PyObject*
primary_monitor_content_scale(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
float xscale = 1.0, yscale = 1.0;
if (monitor) glfwGetMonitorContentScale(monitor, &xscale, &yscale);
return Py_BuildValue("ff", xscale, yscale);
}
static PyObject*
x11_display(PYNOARG) {
if (glfwGetX11Display) {
return PyLong_FromVoidPtr(glfwGetX11Display());
} else log_error("Failed to load glfwGetX11Display");
Py_RETURN_NONE;
}
static PyObject*
x11_window_id(PyObject UNUSED *self, PyObject *os_wid) {
if (glfwGetX11Window) {
id_type os_window_id = PyLong_AsUnsignedLongLong(os_wid);
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
if (w->id == os_window_id) return Py_BuildValue("l", (long)glfwGetX11Window(w->handle));
}
}
else { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; }
PyErr_SetString(PyExc_ValueError, "No OSWindow with the specified id found");
return NULL;
}
static PyObject*
get_primary_selection(PYNOARG) {
if (glfwGetPrimarySelectionString) {
OSWindow *w = current_os_window();
if (w) return Py_BuildValue("y", glfwGetPrimarySelectionString(w->handle));
} else log_error("Failed to load glfwGetPrimarySelectionString");
Py_RETURN_NONE;
}
static PyObject*
set_primary_selection(PyObject UNUSED *self, PyObject *args) {
char *text;
Py_ssize_t sz;
if (!PyArg_ParseTuple(args, "s#", &text, &sz)) return NULL;
if (glfwSetPrimarySelectionString) {
OSWindow *w = current_os_window();
if (w) glfwSetPrimarySelectionString(w->handle, text);
}
else log_error("Failed to load glfwSetPrimarySelectionString");
Py_RETURN_NONE;
}
static PyObject*
set_smallest_allowed_resize(PyObject *self UNUSED, PyObject *args) {
if (!PyArg_ParseTuple(args, "ii", &min_width, &min_height)) return NULL;
Py_RETURN_NONE;
}
static PyObject*
set_custom_cursor(PyObject *self UNUSED, PyObject *args) {
int shape;
int x=0, y=0;
Py_ssize_t sz;
PyObject *images;
if (!PyArg_ParseTuple(args, "iO!|ii", &shape, &PyTuple_Type, &images, &x, &y)) return NULL;
static GLFWimage gimages[16] = {{0}};
size_t count = MIN((size_t)PyTuple_GET_SIZE(images), arraysz(gimages));
for (size_t i = 0; i < count; i++) {
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(images, i), "s#ii", &gimages[i].pixels, &sz, &gimages[i].width, &gimages[i].height)) return NULL;
if (gimages[i].width * gimages[i].height * 4 != sz) {
PyErr_SetString(PyExc_ValueError, "The image data size does not match its width and height");
return NULL;
}
}
#define CASE(which, dest) {\
case which: \
dest = glfwCreateCursor(gimages, x, y, count); \
if (dest == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to create custom cursor"); return NULL; } \
break; \
}
switch(shape) {
CASE(GLFW_IBEAM_CURSOR, standard_cursor);
CASE(GLFW_HAND_CURSOR, click_cursor);
CASE(GLFW_ARROW_CURSOR, arrow_cursor);
default:
PyErr_SetString(PyExc_ValueError, "Unknown cursor shape");
return NULL;
}
Py_RETURN_NONE;
}
#ifdef __APPLE__
void
get_cocoa_key_equivalent(int key, int mods, char *cocoa_key, size_t key_sz, int *cocoa_mods) {
glfwGetCocoaKeyEquivalent(key, mods, cocoa_key, key_sz, cocoa_mods);
}
static void
cocoa_frame_request_callback(GLFWwindow *window) {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
if (global_state.os_windows[i].handle == window) {
global_state.os_windows[i].render_state = RENDER_FRAME_READY;
global_state.os_windows[i].last_render_frame_received_at = monotonic();
request_tick_callback();
break;
}
}
}
void
request_frame_render(OSWindow *w) {
glfwCocoaRequestRenderFrame(w->handle, cocoa_frame_request_callback);
w->render_state = RENDER_FRAME_REQUESTED;
}
#else
static void
wayland_frame_request_callback(id_type os_window_id) {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
if (global_state.os_windows[i].id == os_window_id) {
global_state.os_windows[i].render_state = RENDER_FRAME_READY;
global_state.os_windows[i].last_render_frame_received_at = monotonic();
request_tick_callback();
break;
}
}
}
void
request_frame_render(OSWindow *w) {
glfwRequestWaylandFrameEvent(w->handle, w->id, wayland_frame_request_callback);
w->render_state = RENDER_FRAME_REQUESTED;
}
void
dbus_notification_created_callback(unsigned long long notification_id, uint32_t new_notification_id, void* data UNUSED) {
unsigned long new_id = new_notification_id;
call_boss(dbus_notification_callback, "OKk", Py_False, notification_id, new_id);
}
static PyObject*
dbus_send_notification(PyObject *self UNUSED, PyObject *args) {
char *app_name, *icon, *summary, *body, *action_name;
int timeout = -1;
if (!PyArg_ParseTuple(args, "sssss|i", &app_name, &icon, &summary, &body, &action_name, &timeout)) return NULL;
if (!glfwDBusUserNotify) {
PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwDBusUserNotify, did you call glfw_init?");
return NULL;
}
unsigned long long notification_id = glfwDBusUserNotify(app_name, icon, summary, body, action_name, timeout, dbus_notification_created_callback, NULL);
return PyLong_FromUnsignedLongLong(notification_id);
}
#endif
id_type
add_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {
return glfwAddTimer(interval, repeats, callback, callback_data, free_callback);
}
void
update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled) {
glfwUpdateTimer(timer_id, interval, enabled);
}
void
remove_main_loop_timer(id_type timer_id) {
glfwRemoveTimer(timer_id);
}
void
run_main_loop(tick_callback_fun cb, void* cb_data) {
glfwRunMainLoop(cb, cb_data);
}
void
stop_main_loop(void) {
glfwStopMainLoop();
}
// Boilerplate {{{
static PyMethodDef module_methods[] = {
METHODB(set_custom_cursor, METH_VARARGS),
METHODB(set_smallest_allowed_resize, METH_VARARGS),
METHODB(create_os_window, METH_VARARGS),
METHODB(set_default_window_icon, METH_VARARGS),
METHODB(get_clipboard_string, METH_NOARGS),
METHODB(get_content_scale_for_window, METH_NOARGS),
METHODB(ring_bell, METH_NOARGS),
METHODB(set_clipboard_string, METH_VARARGS),
METHODB(toggle_fullscreen, METH_NOARGS),
METHODB(toggle_maximized, METH_NOARGS),
METHODB(change_os_window_state, METH_VARARGS),
METHODB(glfw_window_hint, METH_VARARGS),
METHODB(get_primary_selection, METH_NOARGS),
METHODB(x11_display, METH_NOARGS),
METHODB(x11_window_id, METH_O),
METHODB(set_primary_selection, METH_VARARGS),
#ifndef __APPLE__
METHODB(dbus_send_notification, METH_VARARGS),
#endif
{"glfw_init", (PyCFunction)glfw_init, METH_VARARGS, ""},
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
{"glfw_get_key_name", (PyCFunction)glfw_get_key_name, METH_VARARGS, ""},
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""},
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};
void cleanup_glfw(void) {
if (logo.pixels) free(logo.pixels);
logo.pixels = NULL;
}
// constants {{{
bool
init_glfw(PyObject *m) {
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
if (Py_AtExit(cleanup_glfw) != 0) {
PyErr_SetString(PyExc_RuntimeError, "Failed to register the glfw exit handler");
return false;
}
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
ADDC(GLFW_RELEASE);
ADDC(GLFW_PRESS);
ADDC(GLFW_REPEAT);
ADDC(true); ADDC(false);
ADDC(GLFW_IBEAM_CURSOR); ADDC(GLFW_HAND_CURSOR); ADDC(GLFW_ARROW_CURSOR);
// --- Keys --------------------------------------------------------------------
// --- The unknown key ---------------------------------------------------------
ADDC(GLFW_KEY_UNKNOWN);
// --- Printable keys ----------------------------------------------------------
ADDC(GLFW_KEY_SPACE);
ADDC(GLFW_KEY_EXCLAM);
ADDC(GLFW_KEY_DOUBLE_QUOTE);
ADDC(GLFW_KEY_NUMBER_SIGN);
ADDC(GLFW_KEY_DOLLAR);
ADDC(GLFW_KEY_AMPERSAND);
ADDC(GLFW_KEY_APOSTROPHE);
ADDC(GLFW_KEY_PARENTHESIS_LEFT);
ADDC(GLFW_KEY_PARENTHESIS_RIGHT);
ADDC(GLFW_KEY_PLUS);
ADDC(GLFW_KEY_COMMA);
ADDC(GLFW_KEY_MINUS);
ADDC(GLFW_KEY_PERIOD);
ADDC(GLFW_KEY_SLASH);
ADDC(GLFW_KEY_0);
ADDC(GLFW_KEY_1);
ADDC(GLFW_KEY_2);
ADDC(GLFW_KEY_3);
ADDC(GLFW_KEY_4);
ADDC(GLFW_KEY_5);
ADDC(GLFW_KEY_6);
ADDC(GLFW_KEY_7);
ADDC(GLFW_KEY_8);
ADDC(GLFW_KEY_9);
ADDC(GLFW_KEY_COLON);
ADDC(GLFW_KEY_SEMICOLON);
ADDC(GLFW_KEY_LESS);
ADDC(GLFW_KEY_EQUAL);
ADDC(GLFW_KEY_GREATER);
ADDC(GLFW_KEY_AT);
ADDC(GLFW_KEY_A);
ADDC(GLFW_KEY_B);
ADDC(GLFW_KEY_C);
ADDC(GLFW_KEY_D);
ADDC(GLFW_KEY_E);
ADDC(GLFW_KEY_F);
ADDC(GLFW_KEY_G);
ADDC(GLFW_KEY_H);
ADDC(GLFW_KEY_I);
ADDC(GLFW_KEY_J);
ADDC(GLFW_KEY_K);
ADDC(GLFW_KEY_L);
ADDC(GLFW_KEY_M);
ADDC(GLFW_KEY_N);
ADDC(GLFW_KEY_O);
ADDC(GLFW_KEY_P);
ADDC(GLFW_KEY_Q);
ADDC(GLFW_KEY_R);
ADDC(GLFW_KEY_S);
ADDC(GLFW_KEY_T);
ADDC(GLFW_KEY_U);
ADDC(GLFW_KEY_V);
ADDC(GLFW_KEY_W);
ADDC(GLFW_KEY_X);
ADDC(GLFW_KEY_Y);
ADDC(GLFW_KEY_Z);
ADDC(GLFW_KEY_LEFT_BRACKET);
ADDC(GLFW_KEY_BACKSLASH);
ADDC(GLFW_KEY_RIGHT_BRACKET);
ADDC(GLFW_KEY_CIRCUMFLEX);
ADDC(GLFW_KEY_UNDERSCORE);
ADDC(GLFW_KEY_GRAVE_ACCENT);
ADDC(GLFW_KEY_WORLD_1);
ADDC(GLFW_KEY_WORLD_2);
ADDC(GLFW_KEY_PARAGRAPH);
ADDC(GLFW_KEY_MASCULINE);
ADDC(GLFW_KEY_A_GRAVE);
ADDC(GLFW_KEY_A_DIAERESIS);
ADDC(GLFW_KEY_A_RING);
ADDC(GLFW_KEY_AE);
ADDC(GLFW_KEY_C_CEDILLA);
ADDC(GLFW_KEY_E_GRAVE);
ADDC(GLFW_KEY_E_ACUTE);
ADDC(GLFW_KEY_I_GRAVE);
ADDC(GLFW_KEY_N_TILDE);
ADDC(GLFW_KEY_O_GRAVE);
ADDC(GLFW_KEY_O_DIAERESIS);
ADDC(GLFW_KEY_O_SLASH);
ADDC(GLFW_KEY_U_GRAVE);
ADDC(GLFW_KEY_U_DIAERESIS);
ADDC(GLFW_KEY_S_SHARP);
ADDC(GLFW_KEY_CYRILLIC_A);
ADDC(GLFW_KEY_CYRILLIC_BE);
ADDC(GLFW_KEY_CYRILLIC_VE);
ADDC(GLFW_KEY_CYRILLIC_GHE);
ADDC(GLFW_KEY_CYRILLIC_DE);
ADDC(GLFW_KEY_CYRILLIC_IE);
ADDC(GLFW_KEY_CYRILLIC_ZHE);
ADDC(GLFW_KEY_CYRILLIC_ZE);
ADDC(GLFW_KEY_CYRILLIC_I);
ADDC(GLFW_KEY_CYRILLIC_SHORT_I);
ADDC(GLFW_KEY_CYRILLIC_KA);
ADDC(GLFW_KEY_CYRILLIC_EL);
ADDC(GLFW_KEY_CYRILLIC_EM);
ADDC(GLFW_KEY_CYRILLIC_EN);
ADDC(GLFW_KEY_CYRILLIC_O);
ADDC(GLFW_KEY_CYRILLIC_PE);
ADDC(GLFW_KEY_CYRILLIC_ER);
ADDC(GLFW_KEY_CYRILLIC_ES);
ADDC(GLFW_KEY_CYRILLIC_TE);
ADDC(GLFW_KEY_CYRILLIC_U);
ADDC(GLFW_KEY_CYRILLIC_EF);
ADDC(GLFW_KEY_CYRILLIC_HA);
ADDC(GLFW_KEY_CYRILLIC_TSE);
ADDC(GLFW_KEY_CYRILLIC_CHE);
ADDC(GLFW_KEY_CYRILLIC_SHA);
ADDC(GLFW_KEY_CYRILLIC_SHCHA);
ADDC(GLFW_KEY_CYRILLIC_HARD_SIGN);
ADDC(GLFW_KEY_CYRILLIC_YERU);
ADDC(GLFW_KEY_CYRILLIC_SOFT_SIGN);
ADDC(GLFW_KEY_CYRILLIC_E);
ADDC(GLFW_KEY_CYRILLIC_YU);
ADDC(GLFW_KEY_CYRILLIC_YA);
ADDC(GLFW_KEY_CYRILLIC_IO);
ADDC(GLFW_KEY_LAST_PRINTABLE);
// --- Function keys -----------------------------------------------------------
ADDC(GLFW_KEY_ESCAPE);
ADDC(GLFW_KEY_ENTER);
ADDC(GLFW_KEY_TAB);
ADDC(GLFW_KEY_BACKSPACE);
ADDC(GLFW_KEY_INSERT);
ADDC(GLFW_KEY_DELETE);
ADDC(GLFW_KEY_RIGHT);
ADDC(GLFW_KEY_LEFT);
ADDC(GLFW_KEY_DOWN);
ADDC(GLFW_KEY_UP);
ADDC(GLFW_KEY_PAGE_UP);
ADDC(GLFW_KEY_PAGE_DOWN);
ADDC(GLFW_KEY_HOME);
ADDC(GLFW_KEY_END);
ADDC(GLFW_KEY_CAPS_LOCK);
ADDC(GLFW_KEY_SCROLL_LOCK);
ADDC(GLFW_KEY_NUM_LOCK);
ADDC(GLFW_KEY_PRINT_SCREEN);
ADDC(GLFW_KEY_PAUSE);
ADDC(GLFW_KEY_F1);
ADDC(GLFW_KEY_F2);
ADDC(GLFW_KEY_F3);
ADDC(GLFW_KEY_F4);
ADDC(GLFW_KEY_F5);
ADDC(GLFW_KEY_F6);
ADDC(GLFW_KEY_F7);
ADDC(GLFW_KEY_F8);
ADDC(GLFW_KEY_F9);
ADDC(GLFW_KEY_F10);
ADDC(GLFW_KEY_F11);
ADDC(GLFW_KEY_F12);
ADDC(GLFW_KEY_F13);
ADDC(GLFW_KEY_F14);
ADDC(GLFW_KEY_F15);
ADDC(GLFW_KEY_F16);
ADDC(GLFW_KEY_F17);
ADDC(GLFW_KEY_F18);
ADDC(GLFW_KEY_F19);
ADDC(GLFW_KEY_F20);
ADDC(GLFW_KEY_F21);
ADDC(GLFW_KEY_F22);
ADDC(GLFW_KEY_F23);
ADDC(GLFW_KEY_F24);
ADDC(GLFW_KEY_F25);
ADDC(GLFW_KEY_KP_0);
ADDC(GLFW_KEY_KP_1);
ADDC(GLFW_KEY_KP_2);
ADDC(GLFW_KEY_KP_3);
ADDC(GLFW_KEY_KP_4);
ADDC(GLFW_KEY_KP_5);
ADDC(GLFW_KEY_KP_6);
ADDC(GLFW_KEY_KP_7);
ADDC(GLFW_KEY_KP_8);
ADDC(GLFW_KEY_KP_9);
ADDC(GLFW_KEY_KP_DECIMAL);
ADDC(GLFW_KEY_KP_DIVIDE);
ADDC(GLFW_KEY_KP_MULTIPLY);
ADDC(GLFW_KEY_KP_SUBTRACT);
ADDC(GLFW_KEY_KP_ADD);
ADDC(GLFW_KEY_KP_ENTER);
ADDC(GLFW_KEY_KP_EQUAL);
ADDC(GLFW_KEY_LEFT_SHIFT);
ADDC(GLFW_KEY_LEFT_CONTROL);
ADDC(GLFW_KEY_LEFT_ALT);
ADDC(GLFW_KEY_LEFT_SUPER);
ADDC(GLFW_KEY_RIGHT_SHIFT);
ADDC(GLFW_KEY_RIGHT_CONTROL);
ADDC(GLFW_KEY_RIGHT_ALT);
ADDC(GLFW_KEY_RIGHT_SUPER);
ADDC(GLFW_KEY_MENU);
ADDC(GLFW_KEY_LAST);
// --- Modifiers ---------------------------------------------------------------
ADDC(GLFW_MOD_SHIFT);
ADDC(GLFW_MOD_CONTROL);
ADDC(GLFW_MOD_ALT);
ADDC(GLFW_MOD_SUPER);
ADDC(GLFW_MOD_KITTY);
// --- Mouse -------------------------------------------------------------------
ADDC(GLFW_MOUSE_BUTTON_1);
ADDC(GLFW_MOUSE_BUTTON_2);
ADDC(GLFW_MOUSE_BUTTON_3);
ADDC(GLFW_MOUSE_BUTTON_4);
ADDC(GLFW_MOUSE_BUTTON_5);
ADDC(GLFW_MOUSE_BUTTON_6);
ADDC(GLFW_MOUSE_BUTTON_7);
ADDC(GLFW_MOUSE_BUTTON_8);
ADDC(GLFW_MOUSE_BUTTON_LAST);
ADDC(GLFW_MOUSE_BUTTON_LEFT);
ADDC(GLFW_MOUSE_BUTTON_RIGHT);
ADDC(GLFW_MOUSE_BUTTON_MIDDLE);
// --- Joystick ----------------------------------------------------------------
ADDC(GLFW_JOYSTICK_1);
ADDC(GLFW_JOYSTICK_2);
ADDC(GLFW_JOYSTICK_3);
ADDC(GLFW_JOYSTICK_4);
ADDC(GLFW_JOYSTICK_5);
ADDC(GLFW_JOYSTICK_6);
ADDC(GLFW_JOYSTICK_7);
ADDC(GLFW_JOYSTICK_8);
ADDC(GLFW_JOYSTICK_9);
ADDC(GLFW_JOYSTICK_10);
ADDC(GLFW_JOYSTICK_11);
ADDC(GLFW_JOYSTICK_12);
ADDC(GLFW_JOYSTICK_13);
ADDC(GLFW_JOYSTICK_14);
ADDC(GLFW_JOYSTICK_15);
ADDC(GLFW_JOYSTICK_16);
ADDC(GLFW_JOYSTICK_LAST);
// --- Error codes -------------------------------------------------------------
ADDC(GLFW_NOT_INITIALIZED);
ADDC(GLFW_NO_CURRENT_CONTEXT);
ADDC(GLFW_INVALID_ENUM);
ADDC(GLFW_INVALID_VALUE);
ADDC(GLFW_OUT_OF_MEMORY);
ADDC(GLFW_API_UNAVAILABLE);
ADDC(GLFW_VERSION_UNAVAILABLE);
ADDC(GLFW_PLATFORM_ERROR);
ADDC(GLFW_FORMAT_UNAVAILABLE);
// ---
ADDC(GLFW_FOCUSED);
ADDC(GLFW_ICONIFIED);
ADDC(GLFW_RESIZABLE);
ADDC(GLFW_VISIBLE);
ADDC(GLFW_DECORATED);
ADDC(GLFW_AUTO_ICONIFY);
ADDC(GLFW_FLOATING);
// ---
ADDC(GLFW_RED_BITS);
ADDC(GLFW_GREEN_BITS);
ADDC(GLFW_BLUE_BITS);
ADDC(GLFW_ALPHA_BITS);
ADDC(GLFW_DEPTH_BITS);
ADDC(GLFW_STENCIL_BITS);
ADDC(GLFW_ACCUM_RED_BITS);
ADDC(GLFW_ACCUM_GREEN_BITS);
ADDC(GLFW_ACCUM_BLUE_BITS);
ADDC(GLFW_ACCUM_ALPHA_BITS);
ADDC(GLFW_AUX_BUFFERS);
ADDC(GLFW_STEREO);
ADDC(GLFW_SAMPLES);
ADDC(GLFW_SRGB_CAPABLE);
ADDC(GLFW_REFRESH_RATE);
ADDC(GLFW_DOUBLEBUFFER);
// ---
ADDC(GLFW_CLIENT_API);
ADDC(GLFW_CONTEXT_VERSION_MAJOR);
ADDC(GLFW_CONTEXT_VERSION_MINOR);
ADDC(GLFW_CONTEXT_REVISION);
ADDC(GLFW_CONTEXT_ROBUSTNESS);
ADDC(GLFW_OPENGL_FORWARD_COMPAT);
ADDC(GLFW_OPENGL_DEBUG_CONTEXT);
ADDC(GLFW_OPENGL_PROFILE);
// ---
ADDC(GLFW_OPENGL_API);
ADDC(GLFW_OPENGL_ES_API);
// ---
ADDC(GLFW_NO_ROBUSTNESS);
ADDC(GLFW_NO_RESET_NOTIFICATION);
ADDC(GLFW_LOSE_CONTEXT_ON_RESET);
// ---
ADDC(GLFW_OPENGL_ANY_PROFILE);
ADDC(GLFW_OPENGL_CORE_PROFILE);
ADDC(GLFW_OPENGL_COMPAT_PROFILE);
// ---
ADDC(GLFW_CURSOR);
ADDC(GLFW_STICKY_KEYS);
ADDC(GLFW_STICKY_MOUSE_BUTTONS);
// ---
ADDC(GLFW_CURSOR_NORMAL);
ADDC(GLFW_CURSOR_HIDDEN);
ADDC(GLFW_CURSOR_DISABLED);
// ---
ADDC(GLFW_CONNECTED);
ADDC(GLFW_DISCONNECTED);
return true;
#undef ADDC
}
// }}}
// }}}