Start work on multi-window support

Port the glfw.c module and various bits of code that depend on it.
This commit is contained in:
Kovid Goyal 2017-11-14 10:53:02 +05:30
parent a4fe97c88c
commit f0003f223a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 243 additions and 328 deletions

View File

@ -9,7 +9,8 @@ from .config import MINIMUM_FONT_SIZE
from .constants import cell_size, set_boss, viewport_size, wakeup
from .fast_data_types import (
GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data,
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
layout_sprite_map, toggle_fullscreen
)
from .fonts.render import prerender, resize_fonts, set_font_family
from .keys import get_key_map, get_shortcut
@ -72,8 +73,6 @@ class Boss:
cell_size.width, cell_size.height = set_font_family(opts)
self.opts, self.args = opts, args
self.glfw_window = glfw_window
glfw_window.framebuffer_size_callback = self.on_window_resize
glfw_window.window_focus_callback = self.on_focus
initialize_renderer()
self.tab_manager = TabManager(opts, args)
self.tab_manager.init(startup_session)
@ -111,7 +110,7 @@ class Boss:
self.close_window(window)
def toggle_fullscreen(self):
self.glfw_window.toggle_fullscreen()
toggle_fullscreen()
def start(self):
if not getattr(self, 'io_thread_started', False):
@ -119,6 +118,7 @@ class Boss:
self.io_thread_started = True
def on_window_resize(self, window, w, h):
# WIN: Port this
viewport_size.width, viewport_size.height = w, h
self.tab_manager.resize()
@ -206,6 +206,7 @@ class Boss:
self.dispatch_action(key_action)
def on_focus(self, window, focused):
# WIN: Port this
self.window_is_focused = focused
w = self.active_window
if w is not None:
@ -273,7 +274,7 @@ class Boss:
w.paste(text)
def paste_from_clipboard(self):
text = self.glfw_window.get_clipboard_string()
text = get_clipboard_string()
self.paste_to_active_window(text)
def paste_from_selection(self):

View File

@ -158,17 +158,18 @@ cocoa_update_title(PyObject *pytitle) {
[title release];
}
PyObject*
cocoa_make_window_resizable(PyObject UNUSED *self, PyObject *window_id) {
NSWindow *window = (NSWindow*)PyLong_AsVoidPtr(window_id);
bool
cocoa_make_window_resizable(void *w) {
NSWindow *window = (NSWindow*)w;
@try {
[window setStyleMask:
[window styleMask] | NSWindowStyleMaskResizable];
} @catch (NSException *e) {
return PyErr_Format(PyExc_ValueError, "Failed to set style mask: %s: %s", [[e name] UTF8String], [[e reason] UTF8String]);
return false;
}
Py_RETURN_NONE;
return true;
}
@ -188,7 +189,6 @@ cocoa_get_lang(PyObject UNUSED *self) {
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""}, \
{"cocoa_make_window_resizable", (PyCFunction)cocoa_make_window_resizable, METH_O, ""}, \
{"cocoa_create_global_menu", (PyCFunction)cocoa_create_global_menu, METH_NOARGS, ""}, \
{"cocoa_init", (PyCFunction)cocoa_init, METH_NOARGS, ""}, \
{NULL, NULL, 0, NULL} /* Sentinel */

View File

@ -38,7 +38,7 @@ typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MOD
typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol;
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
#define MAX_CHILDREN 256
#define MAX_CHILDREN 512
#define BLANK_CHAR 0
#define ATTRS_MASK_WITHOUT_WIDTH 0xFFC
#define WIDTH_MASK 3
@ -220,6 +220,7 @@ typedef struct {
pthread_t io_thread;
} ChildMonitor;
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
#define left_shift_line(line, at, num) \
@ -275,4 +276,4 @@ void scroll_event(double, double);
void set_special_key_combo(int glfw_key, int mods);
void on_text_input(unsigned int codepoint, int mods);
void on_key_input(int key, int scancode, int action, int mods);
void request_window_attention();
void request_window_attention(unsigned int);

View File

@ -10,6 +10,7 @@
#if defined(__APPLE__)
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3native.h>
extern bool cocoa_make_window_resizable(void *w);
#endif
#if GLFW_VERSION_MAJOR < 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR < 2)
@ -26,155 +27,178 @@
#error "glfw has too many keys, you should increase MAX_KEY_COUNT"
#endif
#define CALLBACK(name, fmt, ...) \
if ((name) != NULL) { \
PyObject *_pyret = PyObject_CallFunction((name), fmt, __VA_ARGS__); \
if (_pyret == NULL) PyErr_Print(); \
else Py_DECREF(_pyret); \
}
#define WINDOW_CALLBACK(name, fmt, ...) \
CALLBACK(the_window->name, "O" fmt, the_window, __VA_ARGS__);
typedef struct {
int x, y, w, h;
bool is_set;
} GLFWWindowGeometry;
static GLFWcursor *standard_cursor = NULL, *click_cursor = NULL, *arrow_cursor = NULL;
typedef struct {
PyObject_HEAD
#define GLFW_WINDOW(w) ((GLFWwindow*)((w)->handle))
GLFWwindow *window;
PyObject *framebuffer_size_callback, *window_focus_callback;
GLFWWindowGeometry before_fullscreen;
} WindowWrapper;
static void update_viewport(GLFWwindow *window) {
static void
update_viewport(OSWindow *window) {
int w, h;
glfwGetFramebufferSize(window, &global_state.viewport_width, &global_state.viewport_height);
glfwGetWindowSize(window, &w, &h);
global_state.viewport_x_ratio = (double)global_state.viewport_width / (double)w;
global_state.viewport_y_ratio = (double)global_state.viewport_height / (double)h;
glfwGetFramebufferSize(GLFW_WINDOW(window), &window->viewport_width, &window->viewport_height);
glfwGetWindowSize(GLFW_WINDOW(window), &w, &h);
window->viewport_x_ratio = (double)window->viewport_width / (double)w;
window->viewport_y_ratio = (double)window->viewport_height / (double)h;
window->viewport_size_dirty = true;
}
// callbacks {{{
static WindowWrapper* the_window = NULL;
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;
}
#define WINDOW_CALLBACK(name, fmt, ...) call_boss(name, "K" fmt, global_state.callback_os_window->window_id, __VA_ARGS__)
static void
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
if (!set_callback_window(w)) return;
if (width > 100 && height > 100) {
update_viewport_size(width, height);
update_viewport(w);
WINDOW_CALLBACK(framebuffer_size_callback, "ii", width, height);
update_viewport(global_state.callback_os_window);
WINDOW_CALLBACK(on_window_resize, "ii", width, height);
glfwPostEmptyEvent();
} else fprintf(stderr, "Ignoring resize request for tiny size: %dx%d\n", width, height);
global_state.callback_os_window = NULL;
}
static void
char_mods_callback(GLFWwindow UNUSED *w, unsigned int codepoint, int mods) {
global_state.cursor_blink_zero_time = monotonic();
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
on_text_input(codepoint, mods);
global_state.callback_os_window = NULL;
}
static void
key_callback(GLFWwindow UNUSED *w, int key, int scancode, int action, int mods) {
global_state.cursor_blink_zero_time = monotonic();
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->cursor_blink_zero_time = monotonic();
if (key >= 0 && key <= GLFW_KEY_LAST) {
global_state.is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
global_state.callback_os_window->is_key_pressed[key] = action == GLFW_RELEASE ? false : true;
on_key_input(key, scancode, action, mods);
}
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;
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
double now = monotonic();
global_state.last_mouse_activity_at = now;
if (button >= 0 && (unsigned int)button < sizeof(global_state.mouse_button_pressed)/sizeof(global_state.mouse_button_pressed[0])) {
global_state.mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
global_state.callback_os_window->last_mouse_activity_at = now;
if (button >= 0 && (unsigned int)button < sizeof(global_state.callback_os_window->mouse_button_pressed)/sizeof(global_state.callback_os_window->mouse_button_pressed[0])) {
global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
mouse_event(button, mods);
}
global_state.callback_os_window = NULL;
}
static void
cursor_pos_callback(GLFWwindow *w, double x, double y) {
if (!set_callback_window(w)) return;
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
double now = monotonic();
global_state.last_mouse_activity_at = now;
global_state.cursor_blink_zero_time = now;
global_state.mouse_x = x * global_state.viewport_x_ratio;
global_state.mouse_y = y * global_state.viewport_y_ratio;
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;
mouse_event(-1, 0);
global_state.callback_os_window = NULL;
}
static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset) {
if (!set_callback_window(w)) return;
if (glfwGetInputMode(w, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
double now = monotonic();
global_state.last_mouse_activity_at = now;
global_state.callback_os_window->last_mouse_activity_at = now;
scroll_event(xoffset, yoffset);
global_state.callback_os_window = NULL;
}
static void
window_focus_callback(GLFWwindow UNUSED *w, int focused) {
global_state.application_focused = focused ? true : false;
window_focus_callback(GLFWwindow *w, int focused) {
if (!set_callback_window(w)) return;
global_state.callback_os_window->is_focused = focused ? true : false;
if (focused) {
global_state.focussed_os_window = global_state.callback_os_window;
} else if (global_state.focussed_os_window == global_state.callback_os_window) global_state.focussed_os_window = NULL;
double now = monotonic();
global_state.last_mouse_activity_at = now;
global_state.cursor_blink_zero_time = now;
WINDOW_CALLBACK(window_focus_callback, "O", focused ? Py_True : Py_False);
global_state.callback_os_window->last_mouse_activity_at = now;
global_state.callback_os_window->cursor_blink_zero_time = now;
WINDOW_CALLBACK(on_focus, "O", focused ? Py_True : Py_False);
global_state.callback_os_window = NULL;
}
// }}}
void
set_mouse_cursor(MouseShape type) {
switch(type) {
case HAND:
glfwSetCursor(the_window->window, click_cursor);
break;
case ARROW:
glfwSetCursor(the_window->window, arrow_cursor);
break;
default:
glfwSetCursor(the_window->window, standard_cursor);
break;
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 PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
WindowWrapper *self;
char *title;
create_new_os_window(PyObject UNUSED *self, PyObject *args) {
int width, height;
char *title;
if (!PyArg_ParseTuple(args, "iis", &width, &height, &title)) return NULL;
if (the_window != NULL) { PyErr_SetString(PyExc_ValueError, "Only one glfw window is supported"); return NULL; }
self = (WindowWrapper *)type->tp_alloc(type, 0);
if (self != NULL) {
the_window = self;
self->window = glfwCreateWindow(width, height, title, NULL, NULL);
if (self->window == NULL) { Py_CLEAR(self); the_window = NULL; PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
update_viewport(self->window);
if (standard_cursor == NULL) {
standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
if (standard_cursor == NULL || click_cursor == NULL || arrow_cursor == NULL) {
Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; }
}
set_mouse_cursor(0);
glfwSetFramebufferSizeCallback(self->window, framebuffer_size_callback);
glfwSetCharModsCallback(self->window, char_mods_callback);
glfwSetMouseButtonCallback(self->window, mouse_button_callback);
glfwSetScrollCallback(self->window, scroll_callback);
glfwSetCursorPosCallback(self->window, cursor_pos_callback);
glfwSetKeyCallback(self->window, key_callback);
glfwSetWindowFocusCallback(self->window, window_focus_callback);
if (standard_cursor == NULL) {
standard_cursor = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
click_cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
arrow_cursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
if (standard_cursor == NULL || click_cursor == NULL || arrow_cursor == NULL) {
Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create standard mouse cursors"); return NULL; }
}
return (PyObject*)self;
if (global_state.num_os_windows >= MAX_CHILDREN) {
PyErr_SetString(PyExc_ValueError, "Too many windows");
return NULL;
}
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
if (glfw_window == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create GLFWwindow"); return NULL; }
OSWindow *w = global_state.os_windows + global_state.num_os_windows++;
w->window_id = global_state.window_counter++;
glfwSetWindowUserPointer(glfw_window, w);
w->handle = glfw_window;
glfwSetCursor(glfw_window, standard_cursor);
global_state.callback_os_window->viewport_size_dirty = true;
update_viewport(w);
glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
glfwSetCharModsCallback(glfw_window, char_mods_callback);
glfwSetMouseButtonCallback(glfw_window, mouse_button_callback);
glfwSetScrollCallback(glfw_window, scroll_callback);
glfwSetCursorPosCallback(glfw_window, cursor_pos_callback);
glfwSetKeyCallback(glfw_window, key_callback);
glfwSetWindowFocusCallback(glfw_window, window_focus_callback);
#ifdef __APPLE__
if (OPT(macos_hide_titlebar)) {
if (!cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window))) { PyErr_Print(); }
}
#endif
return PyLong_FromUnsignedLongLong(w->window_id);
}
// Global functions {{{
static void
error_callback(int error, const char* description) {
@ -270,131 +294,36 @@ glfw_init_hint_string(PyObject UNUSED *self, PyObject *args) {
// }}}
static void
dealloc(WindowWrapper* self) {
the_window = NULL;
Py_CLEAR(self->framebuffer_size_callback); Py_CLEAR(self->window_focus_callback);
if (self->window != NULL) glfwDestroyWindow(self->window);
Py_TYPE(self)->tp_free((PyObject*)self);
static PyObject*
get_clipboard_string(PyObject UNUSED *self) {
OSWindow *w = current_os_window();
if (w) return Py_BuildValue("s", glfwGetClipboardString(w->handle));
return Py_BuildValue("s", "");
}
static PyObject*
swap_buffers(WindowWrapper *self) {
glfwSwapBuffers(self->window);
Py_RETURN_NONE;
}
static PyObject*
make_context_current(WindowWrapper *self) {
glfwMakeContextCurrent(self->window);
Py_RETURN_NONE;
}
static PyObject*
window_id(WindowWrapper *self) {
return PyLong_FromVoidPtr(self->window);
}
static PyObject*
show(WindowWrapper *self) {
glfwShowWindow(self->window);
Py_RETURN_NONE;
}
static PyObject*
hide(WindowWrapper *self) {
glfwHideWindow(self->window);
Py_RETURN_NONE;
}
static PyObject*
should_close(WindowWrapper *self) {
PyObject *ans = glfwWindowShouldClose(self->window) ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyObject*
get_clipboard_string(WindowWrapper *self) {
return Py_BuildValue("s", glfwGetClipboardString(self->window));
}
static PyObject*
get_cursor_pos(WindowWrapper *self) {
double x=0, y=0;
glfwGetCursorPos(self->window, &x, &y);
return Py_BuildValue("dd", x, y);
}
static PyObject*
get_content_scale(WindowWrapper UNUSED *self) {
get_content_scale_for_window(PyObject UNUSED *self) {
#ifdef has_content_scale_query
OSWindow *w = global_state.callback_os_window ? global_state.callback_os_window : global_state.os_windows;
float xscale, yscale;
glfwGetWindowContentScale(self->window, &xscale, &yscale);
glfwGetWindowContentScale(w->handle, &xscale, &yscale);
return Py_BuildValue("ff", xscale, yscale);
#else
(void)self;
PyErr_SetString(PyExc_NotImplementedError, "glfw version is too old");
return NULL;
#endif
}
static PyObject*
set_should_close(WindowWrapper *self, PyObject *args) {
int c;
if (!PyArg_ParseTuple(args, "p", &c)) return NULL;
glfwSetWindowShouldClose(self->window, c);
Py_RETURN_NONE;
}
static PyObject*
set_pos(WindowWrapper *self, PyObject *args) {
int x, y;
if (!PyArg_ParseTuple(args, "ii", &x, &y)) return NULL;
glfwSetWindowPos(self->window, x, y);
Py_RETURN_NONE;
}
static PyObject*
is_key_pressed(WindowWrapper *self, PyObject *args) {
int c;
if (!PyArg_ParseTuple(args, "i", &c)) return NULL;
PyObject *ans = glfwGetKey(self->window, c) == GLFW_PRESS ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyObject*
set_clipboard_string(WindowWrapper *self, PyObject *args) {
set_clipboard_string(PyObject UNUSED *self, PyObject *args) {
char *title;
if(!PyArg_ParseTuple(args, "s", &title)) return NULL;
glfwSetClipboardString(self->window, title);
OSWindow *w = current_os_window();
if (w) glfwSetClipboardString(w->handle, title);
Py_RETURN_NONE;
}
static PyObject*
set_window_icon(WindowWrapper *self, PyObject *args) {
GLFWimage logo;
Py_ssize_t sz;
if(!PyArg_ParseTuple(args, "s#ii", &(logo.pixels), &sz, &(logo.width), &(logo.height))) return NULL;
glfwSetWindowIcon(self->window, 1, &logo);
Py_RETURN_NONE;
}
static PyObject*
get_framebuffer_size(WindowWrapper *self) {
int w, h;
glfwGetFramebufferSize(self->window, &w, &h);
return Py_BuildValue("ii", w, h);
}
static PyObject*
get_window_size(WindowWrapper *self) {
int w, h;
glfwGetWindowSize(self->window, &w, &h);
return Py_BuildValue("ii", w, h);
}
static GLFWmonitor*
current_monitor(GLFWwindow *window) {
// Find the monitor that has the maximum overlap with this window
@ -431,51 +360,40 @@ current_monitor(GLFWwindow *window) {
}
static PyObject*
current_monitor_dpi(WindowWrapper *self) {
GLFWmonitor *m = current_monitor(self->window);
if (m == NULL) return NULL;
return get_physical_dpi(m);
}
static PyObject*
toggle_fullscreen(WindowWrapper *self) {
toggle_fullscreen(PyObject UNUSED *self) {
GLFWmonitor *monitor;
if ((monitor = glfwGetWindowMonitor(self->window)) == NULL) {
OSWindow *w = current_os_window();
if (!w) Py_RETURN_NONE;
if ((monitor = glfwGetWindowMonitor(w->handle)) == NULL) {
// make fullscreen
monitor = current_monitor(self->window);
monitor = current_monitor(w->handle);
if (monitor == NULL) return NULL;
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
self->before_fullscreen.is_set = true;
glfwGetWindowSize(self->window, &self->before_fullscreen.w, &self->before_fullscreen.h);
glfwGetWindowPos(self->window, &self->before_fullscreen.x, &self->before_fullscreen.y);
glfwSetWindowMonitor(self->window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
w->before_fullscreen.is_set = true;
glfwGetWindowSize(w->handle, &w->before_fullscreen.w, &w->before_fullscreen.h);
glfwGetWindowPos(w->handle, &w->before_fullscreen.x, &w->before_fullscreen.y);
glfwSetWindowMonitor(w->handle, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
Py_RETURN_TRUE;
} else {
// make windowed
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
if (self->before_fullscreen.is_set) glfwSetWindowMonitor(self->window, NULL, self->before_fullscreen.x, self->before_fullscreen.y, self->before_fullscreen.w, self->before_fullscreen.h, mode->refreshRate);
else glfwSetWindowMonitor(self->window, NULL, 0, 0, 600, 400, mode->refreshRate);
if (w->before_fullscreen.is_set) glfwSetWindowMonitor(w->handle, NULL, w->before_fullscreen.x, w->before_fullscreen.y, w->before_fullscreen.w, w->before_fullscreen.h, mode->refreshRate);
else glfwSetWindowMonitor(w->handle, NULL, 0, 0, 600, 400, mode->refreshRate);
Py_RETURN_FALSE;
}
}
void
request_window_attention() {
request_window_attention(unsigned int kitty_window_id) {
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
if (w) {
#ifdef has_request_attention
glfwRequestWindowAttention(the_window->window);
glfwRequestWindowAttention(w->handle);
#endif
glfwPostEmptyEvent();
glfwPostEmptyEvent();
}
}
#ifdef __APPLE__
static PyObject*
cocoa_window_id(WindowWrapper *self) {
void *wid = glfwGetCocoaWindow(self->window);
if (wid == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get native window handle"); return NULL; }
return PyLong_FromVoidPtr(wid);
}
#endif
static PyObject*
primary_monitor_size(PyObject UNUSED *self) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
@ -497,65 +415,24 @@ primary_monitor_content_scale(PyObject UNUSED *self) {
}
// Boilerplate {{{
#define MND(name, args) {#name, (PyCFunction)name, args, ""}
static PyMethodDef methods[] = {
MND(swap_buffers, METH_NOARGS),
MND(get_clipboard_string, METH_NOARGS),
MND(get_cursor_pos, METH_NOARGS),
MND(get_content_scale, METH_NOARGS),
MND(should_close, METH_NOARGS),
MND(get_framebuffer_size, METH_NOARGS),
MND(get_window_size, METH_NOARGS),
MND(toggle_fullscreen, METH_NOARGS),
MND(current_monitor_dpi, METH_NOARGS),
#ifdef __APPLE__
MND(cocoa_window_id, METH_NOARGS),
#endif
MND(set_should_close, METH_VARARGS),
MND(is_key_pressed, METH_VARARGS),
MND(set_pos, METH_VARARGS),
MND(set_clipboard_string, METH_VARARGS),
MND(make_context_current, METH_NOARGS),
MND(window_id, METH_NOARGS),
MND(show, METH_NOARGS),
MND(hide, METH_NOARGS),
{"set_icon", (PyCFunction)set_window_icon, METH_VARARGS, ""},
{NULL} /* Sentinel */
};
static PyMemberDef members[] = {
#define CBE(name) {#name, T_OBJECT_EX, offsetof(WindowWrapper, name), 0, #name}
CBE(framebuffer_size_callback),
CBE(window_focus_callback),
{NULL}
#undef CBE
};
PyTypeObject WindowWrapper_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.GLFWWindow",
.tp_basicsize = sizeof(WindowWrapper),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "A GLFW window",
.tp_methods = methods,
.tp_members = members,
.tp_new = new,
};
static PyMethodDef module_methods[] = {
{"glfw_init", (PyCFunction)glfw_init, METH_NOARGS, ""}, \
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""}, \
{"glfw_window_hint", (PyCFunction)glfw_window_hint, METH_VARARGS, ""}, \
{"glfw_swap_interval", (PyCFunction)glfw_swap_interval, METH_VARARGS, ""}, \
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""}, \
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, 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_init_hint_string", (PyCFunction)glfw_init_hint_string, METH_VARARGS, ""}, \
{"glfw_primary_monitor_size", (PyCFunction)primary_monitor_size, METH_NOARGS, ""}, \
{"glfw_primary_monitor_content_scale", (PyCFunction)primary_monitor_content_scale, METH_NOARGS, ""}, \
METHODB(create_new_os_window, METH_VARARGS),
METHODB(get_clipboard_string, METH_NOARGS),
METHODB(get_content_scale_for_window, METH_NOARGS),
METHODB(set_clipboard_string, METH_VARARGS),
METHODB(toggle_fullscreen, METH_NOARGS),
{"glfw_init", (PyCFunction)glfw_init, METH_NOARGS, ""},
{"glfw_terminate", (PyCFunction)glfw_terminate, METH_NOARGS, ""},
{"glfw_window_hint", (PyCFunction)glfw_window_hint, METH_VARARGS, ""},
{"glfw_swap_interval", (PyCFunction)glfw_swap_interval, METH_VARARGS, ""},
{"glfw_wait_events", (PyCFunction)glfw_wait_events, METH_VARARGS, ""},
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, 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_init_hint_string", (PyCFunction)glfw_init_hint_string, 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 */
};
@ -563,9 +440,6 @@ static PyMethodDef module_methods[] = {
bool
init_glfw(PyObject *m) {
if (PyModule_AddFunctions(m, module_methods) != 0) return false;
if (PyType_Ready(&WindowWrapper_Type) < 0) return false;
if (PyModule_AddObject(m, "GLFWWindow", (PyObject *)&WindowWrapper_Type) != 0) return 0;
Py_INCREF(&WindowWrapper_Type);
#define ADDC(n) if(PyModule_AddIntConstant(m, #n, n) != 0) return false;
#ifdef GLFW_X11_WM_CLASS_NAME
ADDC(GLFW_X11_WM_CLASS_NAME)

View File

@ -34,7 +34,7 @@ set_special_key_combo(int glfw_key, int mods) {
static inline Window*
active_window() {
Tab *t = global_state.tabs + global_state.active_tab;
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
Window *w = t->windows + t->active_window;
if (w->render_data.screen) return w;
return NULL;

View File

@ -192,13 +192,10 @@ def run_app(opts, args):
viewport_size.height = 400
window = GLFWWindow(viewport_size.width, viewport_size.height, args.cls)
startup_ctx = init_startup_notification(window)
window.make_context_current()
if isosx:
from .fast_data_types import cocoa_make_window_resizable, cocoa_create_global_menu, cocoa_init
from .fast_data_types import cocoa_create_global_menu, cocoa_init
cocoa_init()
cocoa_create_global_menu()
if opts.macos_hide_titlebar:
cocoa_make_window_resizable(window.cocoa_window_id())
elif not iswayland: # no window icons on wayland
with open(logo_data_file, 'rb') as f:
window.set_icon(f.read(), 256, 256)

View File

@ -86,15 +86,15 @@ encode_mouse_event(Window *w, int button, MouseAction action, int mods) {
static inline bool
contains_mouse(Window *w) {
WindowGeometry *g = &w->geometry;
double x = global_state.mouse_x, y = global_state.mouse_y;
double x = global_state.callback_os_window->mouse_x, y = global_state.callback_os_window->mouse_y;
return (w->visible && g->left <= x && x <= g->right && g->top <= y && y <= g->bottom);
}
static inline bool
cell_for_pos(Window *w, unsigned int *x, unsigned int *y) {
WindowGeometry *g = &w->geometry;
unsigned int qx = (unsigned int)((double)(global_state.mouse_x - g->left) / global_state.cell_width);
unsigned int qy = (unsigned int)((double)(global_state.mouse_y - g->top) / global_state.cell_height);
unsigned int qx = (unsigned int)((double)(global_state.callback_os_window->mouse_x - g->left) / global_state.cell_width);
unsigned int qy = (unsigned int)((double)(global_state.callback_os_window->mouse_y - g->top) / global_state.cell_height);
bool ret = false;
Screen *screen = w->render_data.screen;
if (screen && qx <= screen->columns && qy <= screen->lines) {
@ -119,9 +119,9 @@ update_drag(bool from_button, Window *w, bool is_release) {
bool
drag_scroll(Window *w) {
drag_scroll(Window *w, OSWindow *frame) {
unsigned int margin = global_state.cell_height / 2;
double x = global_state.mouse_x, y = global_state.mouse_y;
double x = frame->mouse_x, y = frame->mouse_y;
if (y < w->geometry.top || y > w->geometry.bottom) return false;
if (x < w->geometry.left || x > w->geometry.right) return false;
bool upwards = y <= (w->geometry.top + margin);
@ -130,7 +130,7 @@ drag_scroll(Window *w) {
if (screen->linebuf == screen->main_linebuf) {
screen_history_scroll(screen, SCROLL_LINE, upwards);
update_drag(false, w, false);
global_state.last_mouse_activity_at = monotonic();
frame->last_mouse_activity_at = monotonic();
if (mouse_cursor_shape != ARROW) {
mouse_cursor_shape = ARROW;
set_mouse_cursor(mouse_cursor_shape);
@ -165,7 +165,7 @@ detect_url(Window *w, Screen *screen, unsigned int x, unsigned int y) {
HANDLER(handle_move_event) {
unsigned int x = 0, y = 0;
if (OPT(focus_follows_mouse)) {
Tab *t = global_state.tabs + global_state.active_tab;
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
if (window_idx != t->active_window) {
call_boss(switch_focus_to, "I", window_idx);
}
@ -178,7 +178,7 @@ HANDLER(handle_move_event) {
bool handle_in_kitty = (
(screen->modes.mouse_tracking_mode == ANY_MODE ||
(screen->modes.mouse_tracking_mode == MOTION_MODE && button >= 0)) &&
!(global_state.is_key_pressed[GLFW_KEY_LEFT_SHIFT] || global_state.is_key_pressed[GLFW_KEY_RIGHT_SHIFT])
!(global_state.callback_os_window->is_key_pressed[GLFW_KEY_LEFT_SHIFT] || global_state.callback_os_window->is_key_pressed[GLFW_KEY_RIGHT_SHIFT])
) ? false : true;
if (handle_in_kitty) {
if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) {
@ -249,8 +249,8 @@ open_url(Window *w) {
}
HANDLER(handle_button_event) {
Tab *t = global_state.tabs + global_state.active_tab;
bool is_release = !global_state.mouse_button_pressed[button];
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
bool is_release = !global_state.callback_os_window->mouse_button_pressed[button];
if (window_idx != t->active_window) {
call_boss(switch_focus_to, "I", window_idx);
}
@ -287,7 +287,7 @@ HANDLER(handle_button_event) {
HANDLER(handle_event) {
switch(button) {
case -1:
for (int i = 0; i < GLFW_MOUSE_BUTTON_5; i++) { if (global_state.mouse_button_pressed[i]) { button = i; break; } }
for (int i = 0; i < GLFW_MOUSE_BUTTON_5; i++) { if (global_state.callback_os_window->mouse_button_pressed[i]) { button = i; break; } }
handle_move_event(w, button, modifiers, window_idx);
break;
case GLFW_MOUSE_BUTTON_LEFT:
@ -304,15 +304,15 @@ HANDLER(handle_event) {
static inline void
handle_tab_bar_mouse(int button, int UNUSED modifiers) {
if (button != GLFW_MOUSE_BUTTON_LEFT || !global_state.mouse_button_pressed[button]) return;
call_boss(activate_tab_at, "d", global_state.mouse_x);
if (button != GLFW_MOUSE_BUTTON_LEFT || !global_state.callback_os_window->mouse_button_pressed[button]) return;
call_boss(activate_tab_at, "d", global_state.callback_os_window->mouse_x);
}
static inline Window*
window_for_event(unsigned int *window_idx, bool *in_tab_bar) {
*in_tab_bar = global_state.num_tabs > 1 && global_state.mouse_y >= global_state.viewport_height - global_state.cell_height;
*in_tab_bar = global_state.callback_os_window->num_tabs > 1 && global_state.callback_os_window->mouse_y >= global_state.callback_os_window->viewport_height - global_state.cell_height;
if (!*in_tab_bar) {
Tab *t = global_state.tabs + global_state.active_tab;
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
for (unsigned int i = 0; i < t->num_windows; i++) {
if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) {
*window_idx = i; return t->windows + i;

View File

@ -1007,7 +1007,7 @@ screen_invert_colors(Screen *self) {
}
void
screen_bell(Screen UNUSED *self) {
screen_bell(Screen *self) {
if (global_state.opts.enable_audio_bell) {
int fd = open("/dev/tty", O_WRONLY | O_CLOEXEC | O_NOCTTY);
if (fd > 0) {
@ -1023,7 +1023,7 @@ screen_bell(Screen UNUSED *self) {
if (global_state.opts.visual_bell_duration > 0) {
self->start_visual_bell_at = monotonic();
}
request_window_attention();
request_window_attention(self->window_id);
}
void

View File

@ -31,6 +31,27 @@ static const Window EMPTY_WINDOW = {0};
Tab *tab = global_state.tabs + t;
#define END_WITH_TAB break; }}
OSWindow*
current_os_window() {
if (global_state.callback_os_window) return global_state.callback_os_window;
if (global_state.focussed_os_window) return global_state.focussed_os_window;
return global_state.os_windows;
}
OSWindow*
os_window_for_kitty_window(unsigned int kitty_window_id) {
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
for (size_t t = 0; t < w->num_tabs; t++) {
Tab *tab = w->tabs + t;
for (size_t c = 0; c < tab->num_windows; c++) {
if (tab->windows[c].id == kitty_window_id) return w;
}
}
}
return NULL;
}
static inline void
add_tab(unsigned int id) {
ensure_can_add(global_state.tabs, global_state.num_tabs, "Too many children (add_tab)");
@ -163,6 +184,7 @@ PYWRAP1(set_options) {
S(repaint_delay, repaint_delay);
S(input_delay, repaint_delay);
S(macos_option_as_alt, PyObject_IsTrue);
S(macos_hide_titlebar, PyObject_IsTrue);
PyObject *chars = PyObject_GetAttrString(args, "select_by_word_characters");
if (chars == NULL) return NULL;

View File

@ -19,7 +19,7 @@ typedef struct {
color_type url_color;
double repaint_delay, input_delay;
bool focus_follows_mouse;
bool macos_option_as_alt;
bool macos_option_as_alt, macos_hide_titlebar;
int adjust_line_height_px;
float adjust_line_height_frac;
} Options;
@ -64,23 +64,41 @@ typedef struct {
#define MAX_KEY_COUNT 512
typedef struct {
Options opts;
int x, y, w, h;
bool is_set;
} OSWindowGeometry;
typedef struct {
void *handle;
unsigned long long window_id;
OSWindowGeometry before_fullscreen;
int viewport_width, viewport_height;
double viewport_x_ratio, viewport_y_ratio;
Tab tabs[MAX_CHILDREN];
unsigned int active_tab, num_tabs;
ScreenRenderData tab_bar_render_data;
bool application_focused;
bool is_focused;
double cursor_blink_zero_time, last_mouse_activity_at;
double logical_dpi_x, logical_dpi_y;
float font_sz_in_pts;
double mouse_x, mouse_y;
bool mouse_button_pressed[20];
int viewport_width, viewport_height;
double viewport_x_ratio, viewport_y_ratio;
unsigned int cell_width, cell_height;
PyObject *application_title;
PyObject *boss;
PyObject *window_title;
bool is_key_pressed[MAX_KEY_COUNT];
bool viewport_size_dirty;
} OSWindow;
typedef struct {
Options opts;
double logical_dpi_x, logical_dpi_y;
unsigned long long window_counter;
float font_sz_in_pts;
unsigned int cell_width, cell_height;
PyObject *boss;
OSWindow os_windows[MAX_CHILDREN];
size_t num_os_windows;
OSWindow *callback_os_window, *focussed_os_window;
} GlobalState;
extern GlobalState global_state;
@ -98,7 +116,9 @@ typedef struct {
else Py_DECREF(cret_); \
}
bool drag_scroll(Window *);
OSWindow* os_window_for_kitty_window(unsigned int);
OSWindow* current_os_window();
bool drag_scroll(Window *, OSWindow*);
void draw_borders();
void draw_cells(ssize_t, ssize_t, float, float, float, float, Screen *, CursorRenderInfo *);
void draw_cursor(CursorRenderInfo *);

View File

@ -15,7 +15,7 @@ from .constants import (
)
from .fast_data_types import (
BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BACKGROUND_PROGRAM,
CELL_FOREGROUND_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM,
CELL_FOREGROUND_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, set_clipboard_string,
CURSOR_PROGRAM, GRAPHICS_PROGRAM, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
Screen, compile_program, create_cell_vao, create_graphics_vao,
glfw_post_empty_event, init_cell_program, init_cursor_program, remove_vao,
@ -275,7 +275,7 @@ class Window:
def copy_to_clipboard(self):
text = self.text_for_selection()
if text:
get_boss().glfw_window.set_clipboard_string(text)
set_clipboard_string(text)
def pass_selection_to_program(self, *args):
text = self.text_for_selection()