/* * Copyright (C) 2016 Kovid Goyal * * Distributed under terms of the GPL3 license. */ #include "state.h" #include "fonts.h" #include "monotonic.h" #include #include "glfw-wrapper.h" extern bool cocoa_make_window_resizable(void *w, bool); extern void cocoa_focus_window(void *w); extern long cocoa_window_number(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_system_beep(void); 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; if (global_state.callback_os_window->close_request == NO_CLOSE_REQUESTED) { global_state.callback_os_window->close_request = CONFIRMABLE_CLOSE_REQUESTED; global_state.has_pending_closes = true; request_tick_callback(); } glfwSetWindowShouldClose(window, false); global_state.callback_os_window = NULL; } 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 inline int key_to_modifier(int key) { switch(key) { case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: return GLFW_MOD_SHIFT; case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: return GLFW_MOD_CONTROL; case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: return GLFW_MOD_ALT; case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: return GLFW_MOD_SUPER; default: return -1; } } static void key_callback(GLFWwindow *w, GLFWkeyevent *ev) { if (!set_callback_window(w)) return; mods_at_last_key_or_button_event = ev->mods; int key_modifier = key_to_modifier(ev->key); if (key_modifier != -1) { if (ev->action == GLFW_RELEASE) { mods_at_last_key_or_button_event &= ~key_modifier; } else { mods_at_last_key_or_button_event |= key_modifier; } } 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, int mods) { 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, mods); 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; #define RETURN(x) { global_state.callback_os_window = NULL; return x; } 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, (Py_ssize_t)sz); request_tick_callback(); RETURN(0); #undef RETURN } static void application_close_requested_callback(int flags) { if (flags) { global_state.quit_request = IMPERATIVE_CLOSE_REQUESTED; global_state.has_pending_closes = true; request_tick_callback(); } else { if (global_state.quit_request == NO_CLOSE_REQUESTED) { global_state.has_pending_closes = true; global_state.quit_request = CONFIRMABLE_CLOSE_REQUESTED; request_tick_callback(); } } } // }}} 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) { *xscale = 1; *yscale = 1; 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, yscale; 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 GLFWwindow *apple_preserve_common_context = NULL; 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 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); glfwSetApplicationCloseCallback(application_close_requested_callback); #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; GLFWwindow *temp_window = NULL; #ifdef __APPLE__ if (!apple_preserve_common_context) { apple_preserve_common_context = glfwCreateWindow(100, 200, "kitty", NULL, common_context); } if (!common_context) common_context = apple_preserve_common_context; #endif if (!global_state.is_wayland) { // On Wayland windows dont get a content scale until they receive an enterEvent anyway // which won't 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 } } // 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); 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", ""); } static void ring_audio_bell(void) { static monotonic_t last_bell_at = -1; monotonic_t now = monotonic(); if (last_bell_at >= 0 && now - last_bell_at <= ms_to_monotonic_t(100ll)) return; last_bell_at = now; #ifdef __APPLE__ cocoa_system_beep(); #else play_canberra_sound("bell", "kitty bell"); #endif } static PyObject* ring_bell(PYNOARG) { ring_audio_bell(); 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(); 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(); } 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; } 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 OSWindow* find_os_window(PyObject *os_wid) { 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 w; } return NULL; } static PyObject* x11_window_id(PyObject UNUSED *self, PyObject *os_wid) { OSWindow *w = find_os_window(os_wid); if (!w) { PyErr_SetString(PyExc_ValueError, "No OSWindow with the specified id found"); return NULL; } if (!glfwGetX11Window) { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetX11Window"); return NULL; } return Py_BuildValue("l", (long)glfwGetX11Window(w->handle)); } static PyObject* cocoa_window_id(PyObject UNUSED *self, PyObject *os_wid) { OSWindow *w = find_os_window(os_wid); if (!w) { PyErr_SetString(PyExc_ValueError, "No OSWindow with the specified id found"); return NULL; } if (!glfwGetCocoaWindow) { PyErr_SetString(PyExc_RuntimeError, "Failed to load glfwGetCocoaWindow"); return NULL; } #ifdef __APPLE__ return Py_BuildValue("l", (long)cocoa_window_number(glfwGetCocoaWindow(w->handle))); #else PyErr_SetString(PyExc_RuntimeError, "cocoa_window_id() is only supported on Mac"); return NULL; #endif } 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 ((Py_ssize_t)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) { // Some Wayland compositors are too fragile to handle multiple // render frame requests, see https://github.com/kovidgoyal/kitty/issues/2329 if (w->render_state != RENDER_FRAME_REQUESTED) { 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) { #ifdef __APPLE__ if (apple_preserve_common_context) glfwDestroyWindow(apple_preserve_common_context); apple_preserve_common_context = NULL; #endif 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 METHODB(cocoa_window_id, METH_O), {"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_CONTEXT_DEBUG); 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 } // }}} // }}}