Get rid of the GLFW ctypes bindings

This commit is contained in:
Kovid Goyal 2016-11-29 20:09:18 +05:30
parent a52c4670dc
commit f713117908
9 changed files with 270 additions and 478 deletions

409
glfw.py
View File

@ -1,409 +0,0 @@
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# GLFW - An OpenGL framework
# API version: 3.0.1
# WWW: http://www.glfw.org/
# ----------------------------------------------------------------------------
# Copyright (c) 2002-2006 Marcus Geelnard
# Copyright (c) 2006-2010 Camilla Berglund
#
# Python bindings - Copyright (c) 2013 Nicolas P. Rougier
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would
# be appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not
# be misrepresented as being the original software.
#
# 3. This notice may not be removed or altered from any source
# distribution.
#
# -----------------------------------------------------------------------------
# NOTE:
# This source has been modified from its original form by the vispy dev team
import os
import ctypes.util
from collections import namedtuple
from ctypes import (Structure, POINTER, CFUNCTYPE, byref, c_char_p, c_int,
c_uint, c_double, c_float, c_ushort)
_glfw_file = None
# First if there is an environment variable pointing to the library
if 'GLFW_LIBRARY' in os.environ:
if os.path.exists(os.environ['GLFW_LIBRARY']):
_glfw_file = os.path.realpath(os.environ['GLFW_LIBRARY'])
# Else, try to find it
if _glfw_file is None:
order = ['glfw', 'glfw3']
for check in order:
_glfw_file = ctypes.util.find_library(check)
if _glfw_file is not None:
break
# Else, we failed and exit
if _glfw_file is None:
raise OSError('GLFW library not found')
# Load it
_glfw = ctypes.CDLL(_glfw_file)
# Ensure it's new enough
def glfwGetVersion():
major, minor, rev = c_int(0), c_int(0), c_int(0)
_glfw.glfwGetVersion(byref(major), byref(minor), byref(rev))
return major.value, minor.value, rev.value
version = glfwGetVersion()
if version[0] != 3:
version = '.'.join([str(v) for v in version])
raise OSError('Need GLFW library version 3, found version %s' % version)
# --- Version -----------------------------------------------------------------
GLFW_VERSION_MAJOR = version[0]
GLFW_VERSION_MINOR = version[1]
GLFW_VERSION_REVISION = version[2]
__version__ = GLFW_VERSION_MAJOR, GLFW_VERSION_MINOR, GLFW_VERSION_REVISION
# --- Structures --------------------------------------------------------------
class GLFWvidmode(Structure):
_fields_ = [('width', c_int),
('height', c_int),
('redBits', c_int),
('greenBits', c_int),
('blueBits', c_int),
('refreshRate', c_int)]
class GLFWgammaramp(Structure):
_fields_ = [('red', POINTER(c_ushort)),
('green', POINTER(c_ushort)),
('blue', POINTER(c_ushort)),
('size', c_int)]
class GLFWwindow(Structure):
pass
class GLFWmonitor(Structure):
pass
# --- Callbacks ---------------------------------------------------------------
errorfun = CFUNCTYPE(None, c_int, c_char_p)
windowposfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int)
windowsizefun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int)
windowclosefun = CFUNCTYPE(None, POINTER(GLFWwindow))
windowrefreshfun = CFUNCTYPE(None, POINTER(GLFWwindow))
windowfocusfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int)
windowiconifyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int)
framebuffersizefun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int)
mousebuttonfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int)
cursorposfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double)
cursorenterfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int)
scrollfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_double, c_double)
keyfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_int, c_int, c_int, c_int)
charfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint)
charmodsfun = CFUNCTYPE(None, POINTER(GLFWwindow), c_uint, c_int)
monitorfun = CFUNCTYPE(None, POINTER(GLFWmonitor), c_int)
# --- Init --------------------------------------------------------------------
glfwInit = _glfw.glfwInit
glfwTerminate = _glfw.glfwTerminate
# glfwGetVersion = _glfw.glfwGetVersion
# --- Error -------------------------------------------------------------------
# glfwSetErrorCallback = _glfw.glfwSetErrorCallback
# --- Monitor -----------------------------------------------------------------
# glfwGetMonitors = _glfw.glfwGetMonitors
# glfwGetMonitors.restype = POINTER(GLFWmonitor)
glfwGetPrimaryMonitor = _glfw.glfwGetPrimaryMonitor
glfwGetPrimaryMonitor.restype = POINTER(GLFWmonitor)
# glfwGetMonitorPos = _glfw.glfwGetMonitorPos
# glfwGetMonitorPhysicalSize = _glfw.glfwGetMonitorPhysicalSize
glfwGetMonitorName = _glfw.glfwGetMonitorName
glfwGetMonitorName.restype = c_char_p
# glfwSetMonitorCallback = _glfw.glfwSetMonitorCallback
# glfwGetVideoModes = _glfw.glfwGetVideoModes
# glfwGetVideoMode = _glfw.glfwGetVideoMode
# --- Gama --------------------------------------------------------------------
glfwSetGamma = _glfw.glfwSetGamma
# glfwGetGammaRamp = _glfw.glfwGetGammaRamp
# glfwSetGammaRamp = _glfw.glfwSetGammaRamp
# --- Window ------------------------------------------------------------------
glfwDefaultWindowHints = _glfw.glfwDefaultWindowHints
glfwWindowHint = _glfw.glfwWindowHint
# glfwCreateWindow = _glfw.glfwCreateWindow
# glfwDestroyWindow = _glfw.glfwDestroyWindow
glfwWindowShouldClose = _glfw.glfwWindowShouldClose
glfwSetWindowShouldClose = _glfw.glfwSetWindowShouldClose
glfwSetWindowTitle = _glfw.glfwSetWindowTitle
# glfwGetWindowPos = _glfw.glfwGetWindowPos
glfwSetWindowPos = _glfw.glfwSetWindowPos
# glfwGetWindowSize = _glfw.glfwGetWindowSize
glfwSetWindowSize = _glfw.glfwSetWindowSize
# glfwGetFramebufferSize = _glfw.glfwGetFramebufferSize
glfwIconifyWindow = _glfw.glfwIconifyWindow
glfwRestoreWindow = _glfw.glfwRestoreWindow
glfwShowWindow = _glfw.glfwShowWindow
glfwHideWindow = _glfw.glfwHideWindow
glfwGetWindowMonitor = _glfw.glfwGetWindowMonitor
glfwGetWindowAttrib = _glfw.glfwGetWindowAttrib
glfwSetWindowUserPointer = _glfw.glfwSetWindowUserPointer
glfwGetWindowUserPointer = _glfw.glfwGetWindowUserPointer
# glfwSetWindowPosCallback = _glfw.glfwSetWindowPosCallback
# glfwSetWindowSizeCallback = _glfw.glfwSetWindowSizeCallback
# glfwSetWindowCloseCallback = _glfw.glfwSetWindowCloseCallback
# glfwSetWindowRefreshCallback = _glfw.glfwSetWindowRefreshCallback
# glfwSetWindowFocusCallback = _glfw.glfwSetWindowFocusCallback
# glfwSetWindowIconifyCallback = _glfw.glfwSetWindowIconifyCallback
# glfwSetFramebufferSizeCallback = _glfw.glfwSetFramebufferSizeCallback
glfwPollEvents = _glfw.glfwPollEvents
glfwWaitEvents = _glfw.glfwWaitEvents
glfwPostEmptyEvent = _glfw.glfwPostEmptyEvent
# --- Input -------------------------------------------------------------------
glfwGetInputMode = _glfw.glfwGetInputMode
glfwSetInputMode = _glfw.glfwSetInputMode
glfwGetKey = _glfw.glfwGetKey
glfwGetMouseButton = _glfw.glfwGetMouseButton
# glfwGetCursorPos = _glfw.glfwGetCursorPos
glfwSetCursorPos = _glfw.glfwSetCursorPos
# glfwSetKeyCallback = _glfw.glfwSetKeyCallback
# glfwSetCharCallback = _glfw.glfwSetCharCallback
# glfwSetMouseButtonCallback = _glfw.glfwSetMouseButtonCallback
# glfwSetCursorPosCallback = _glfw.glfwSetCursorPosCallback
# glfwSetCursorEnterCallback = _glfw.glfwSetCursorEnterCallback
# glfwSetScrollCallback = _glfw.glfwSetScrollCallback
glfwJoystickPresent = _glfw.glfwJoystickPresent
# glfwGetJoystickAxes = _glfw.glfwGetJoystickAxes
# glfwGetJoystickButtons = _glfw.glfwGetJoystickButtons
glfwGetJoystickName = _glfw.glfwGetJoystickName
glfwGetJoystickName.restype = c_char_p
# --- Clipboard ---------------------------------------------------------------
glfwSetClipboardString = _glfw.glfwSetClipboardString
glfwGetClipboardString = _glfw.glfwGetClipboardString
glfwGetClipboardString.restype = c_char_p
# --- Timer -------------------------------------------------------------------
glfwGetTime = _glfw.glfwGetTime
glfwGetTime.restype = c_double
glfwSetTime = _glfw.glfwSetTime
# --- Context -----------------------------------------------------------------
glfwMakeContextCurrent = _glfw.glfwMakeContextCurrent
glfwGetCurrentContext = _glfw.glfwGetCurrentContext
glfwSwapBuffers = _glfw.glfwSwapBuffers
glfwSwapInterval = _glfw.glfwSwapInterval
glfwExtensionSupported = _glfw.glfwExtensionSupported
glfwGetProcAddress = _glfw.glfwGetProcAddress
# --- Pythonizer --------------------------------------------------------------
# This keeps track of current windows
__windows__ = []
__destroyed__ = []
# This is to prevent garbage collection on callbacks
__c_callbacks__ = {}
__py_callbacks__ = {}
__c_error_callback__ = None
def glfwCreateWindow(width=640, height=480, title="GLFW Window",
monitor=None, share=None):
_glfw.glfwCreateWindow.restype = POINTER(GLFWwindow)
window = _glfw.glfwCreateWindow(width, height, title, monitor, share)
__windows__.append(window)
__destroyed__.append(False)
index = __windows__.index(window)
__c_callbacks__[index] = {}
__py_callbacks__[index] = {'errorfun': None,
'monitorfun': None,
'windowposfun': None,
'windowsizefun': None,
'windowclosefun': None,
'windowrefreshfun': None,
'windowfocusfun': None,
'windowiconifyfun': None,
'framebuffersizefun': None,
'keyfun': None,
'charfun': None,
'charmodsfun': None,
'mousebuttonfun': None,
'cursorposfun': None,
'cursorenterfun': None,
'scrollfun': None}
return window
def glfwDestroyWindow(window):
index = __windows__.index(window)
if not __destroyed__[index]:
_glfw.glfwDestroyWindow(window)
# We do not delete window from the list (or it would impact numbering)
del __c_callbacks__[index]
del __py_callbacks__[index]
# del __windows__[index]
__destroyed__[index] = True
def glfwGetWindowPos(window):
xpos, ypos = c_int(0), c_int(0)
_glfw.glfwGetWindowPos(window, byref(xpos), byref(ypos))
return xpos.value, ypos.value
def glfwGetCursorPos(window):
xpos, ypos = c_double(0), c_double(0)
_glfw.glfwGetCursorPos(window, byref(xpos), byref(ypos))
return int(xpos.value), int(ypos.value)
def glfwGetWindowSize(window):
width, height = c_int(0), c_int(0)
_glfw.glfwGetWindowSize(window, byref(width), byref(height))
return width.value, height.value
def glfwGetFramebufferSize(window):
width, height = c_int(0), c_int(0)
_glfw.glfwGetFramebufferSize(window, byref(width), byref(height))
return width.value, height.value
def glfwGetMonitors():
count = c_int(0)
_glfw.glfwGetMonitors.restype = POINTER(POINTER(GLFWmonitor))
c_monitors = _glfw.glfwGetMonitors(byref(count))
return [c_monitors[i] for i in range(count.value)]
def glfwGetVideoModes(monitor):
count = c_int(0)
_glfw.glfwGetVideoModes.restype = POINTER(GLFWvidmode)
c_modes = _glfw.glfwGetVideoModes(monitor, byref(count))
modes = []
for i in range(count.value):
modes.append((c_modes[i].width,
c_modes[i].height,
c_modes[i].redBits,
c_modes[i].blueBits,
c_modes[i].greenBits,
c_modes[i].refreshRate))
return modes
def glfwGetMonitorPos(monitor):
xpos, ypos = c_int(0), c_int(0)
_glfw.glfwGetMonitorPos(monitor, byref(xpos), byref(ypos))
return xpos.value, ypos.value
def glfwGetMonitorPhysicalSize(monitor):
width, height = c_int(0), c_int(0)
_glfw.glfwGetMonitorPhysicalSize(monitor, byref(width), byref(height))
return width.value, height.value
VideoMode = namedtuple('VideoMode', 'width height redBits blueBits greenBits refreshRate')
def glfwGetVideoMode(monitor):
_glfw.glfwGetVideoMode.restype = POINTER(GLFWvidmode)
c_mode = _glfw.glfwGetVideoMode(monitor).contents
return VideoMode(
c_mode.width, c_mode.height, c_mode.redBits, c_mode.blueBits, c_mode.greenBits, c_mode.refreshRate)
def GetGammaRamp(monitor):
_glfw.glfwGetGammaRamp.restype = POINTER(GLFWgammaramp)
c_gamma = _glfw.glfwGetGammaRamp(monitor).contents
gamma = {'red': [], 'green': [], 'blue': []}
if c_gamma:
for i in range(c_gamma.size):
gamma['red'].append(c_gamma.red[i])
gamma['green'].append(c_gamma.green[i])
gamma['blue'].append(c_gamma.blue[i])
return gamma
def glfwGetJoystickAxes(joy):
count = c_int(0)
_glfw.glfwGetJoystickAxes.restype = POINTER(c_float)
c_axes = _glfw.glfwGetJoystickAxes(joy, byref(count))
axes = [c_axes[i].value for i in range(count)]
return axes
def glfwGetJoystickButtons(joy):
count = c_int(0)
_glfw.glfwGetJoystickButtons.restype = POINTER(c_int)
c_buttons = _glfw.glfwGetJoystickButtons(joy, byref(count))
buttons = [c_buttons[i].value for i in range(count)]
return buttons
# --- Callbacks ---------------------------------------------------------------
def __callback__(name):
callback = 'glfwSet%sCallback' % name
fun = '%sfun' % name.lower()
code = """
def %(callback)s(window, callback = None):
index = __windows__.index(window)
old_callback = __py_callbacks__[index]['%(fun)s']
__py_callbacks__[index]['%(fun)s'] = callback
if callback: callback = %(fun)s(callback)
__c_callbacks__[index]['%(fun)s'] = callback
_glfw.%(callback)s(window, callback)
return old_callback""" % {'callback': callback, 'fun': fun}
return code
exec(__callback__('Monitor'))
exec(__callback__('WindowPos'))
exec(__callback__('WindowSize'))
exec(__callback__('WindowClose'))
exec(__callback__('WindowRefresh'))
exec(__callback__('WindowFocus'))
exec(__callback__('WindowIconify'))
exec(__callback__('FramebufferSize'))
exec(__callback__('Key'))
exec(__callback__('Char'))
exec(__callback__('CharMods'))
exec(__callback__('MouseButton'))
exec(__callback__('CursorPos'))
exec(__callback__('Scroll'))
# Error callback does not take window parameter
def glfwSetErrorCallback(callback=None):
global __c_error_callback__
__c_error_callback__ = errorfun(callback)
_glfw.glfwSetErrorCallback(__c_error_callback__)

View File

@ -22,6 +22,8 @@ static PyMethodDef module_methods[] = {
{"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_NOARGS, ""},
{"glfw_post_empty_event", (PyCFunction)glfw_post_empty_event, METH_NOARGS, ""},
{"glfw_get_physical_dpi", (PyCFunction)glfw_get_physical_dpi, METH_NOARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};
@ -54,6 +56,7 @@ PyInit_fast_data_types(void) {
if (!init_Face(m)) return NULL;
if (!add_module_gl_constants(m)) return NULL;
if (!init_glfw(m)) return NULL;
if (!init_Window(m)) return NULL;
PyModule_AddIntConstant(m, "BOLD", BOLD_SHIFT);
PyModule_AddIntConstant(m, "ITALIC", ITALIC_SHIFT);
PyModule_AddIntConstant(m, "REVERSE", REVERSE_SHIFT);

View File

@ -176,6 +176,7 @@ typedef struct {
PyTypeObject Cursor_Type;
PyTypeObject Face_Type;
PyTypeObject Window_Type;
typedef struct {
PyObject_HEAD
@ -295,6 +296,7 @@ int init_SpriteMap(PyObject *);
int init_ChangeTracker(PyObject *);
int init_Screen(PyObject *);
int init_Face(PyObject *);
int init_Window(PyObject *);
PyObject* create_256_color_table();
PyObject* read_bytes_dump(PyObject UNUSED *, PyObject *);
PyObject* read_bytes(PyObject UNUSED *, PyObject *);

View File

@ -5,22 +5,103 @@
*/
#include "data-types.h"
#include <structmember.h>
#include <GLFW/glfw3.h>
#define MAX_WINDOWS 255
/* static PyObject* window_weakrefs[MAX_WINDOWS + 1] = {0}; */
#define MAX_WINDOWS 256
#define CALLBACK(name, fmt, ...) \
if ((name) != NULL) { \
PyGILState_STATE _pystate = PyGILState_Ensure(); \
PyObject *_pyret = PyObject_CallFunction((name), fmt, __VA_ARGS__); \
PyGILState_Release(_pystate); \
if (_pyret == NULL) { PyErr_Print(); PyErr_Clear(); return; } \
if (_pyret == NULL && PyErr_Occurred() != NULL) PyErr_Print(); \
Py_CLEAR(_pyret); \
PyGILState_Release(_pystate); \
}
#define WINDOW_CALLBACK(name, fmt, ...) \
Window *self = find_window(w); \
if (self) { CALLBACK(self->name, "O" fmt, self, __VA_ARGS__); }
// Library Setup {{{
typedef struct {
PyObject_HEAD
GLFWwindow *window;
PyObject *framebuffer_size_callback, *char_mods_callback, *key_callback, *mouse_button_callback, *scroll_callback, *cursor_pos_callback, *window_focus_callback;
} Window;
static Window* window_weakrefs[MAX_WINDOWS] = {0};
static inline Window*
find_window(GLFWwindow *w) {
for(int i = 0; i < MAX_WINDOWS; i++) {
if (window_weakrefs[i] == NULL) break;
if (window_weakrefs[i]->window == w) return window_weakrefs[i];
}
return NULL;
}
static void
framebuffer_size_callback(GLFWwindow *w, int width, int height) {
WINDOW_CALLBACK(framebuffer_size_callback, "ii", width, height);
}
static void
char_mods_callback(GLFWwindow *w, unsigned int codepoint, int mods) {
WINDOW_CALLBACK(char_mods_callback, "Ii", codepoint, mods);
}
static void
key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) {
WINDOW_CALLBACK(key_callback, "iiii", key, scancode, action, mods);
}
static void
mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
WINDOW_CALLBACK(mouse_button_callback, "iii", button, action, mods);
}
static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset) {
WINDOW_CALLBACK(scroll_callback, "dd", xoffset, yoffset);
}
static void
cursor_pos_callback(GLFWwindow *w, double x, double y) {
WINDOW_CALLBACK(cursor_pos_callback, "dd", x, y);
}
static void
window_focus_callback(GLFWwindow *w, int focused) {
WINDOW_CALLBACK(window_focus_callback, "O", focused ? Py_True : Py_False);
}
static PyObject*
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
Window *self;
char *title;
int width, height, i;
if (!PyArg_ParseTuple(args, "iis", &width, &height, &title)) return NULL;
self = (Window *)type->tp_alloc(type, 0);
if (self != NULL) {
self->window = glfwCreateWindow(width, height, title, NULL, NULL);
if (self->window == NULL) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Failed to create GLFWWindow"); return NULL; }
for(i = 0; i < MAX_WINDOWS; i++) {
if (window_weakrefs[i] == NULL) { window_weakrefs[i] = self; break; }
}
if (i >= MAX_WINDOWS) { Py_CLEAR(self); PyErr_SetString(PyExc_ValueError, "Too many windows created"); return NULL; }
glfwSetFramebufferSizeCallback(self->window, framebuffer_size_callback);
glfwSetCharModsCallback(self->window, char_mods_callback);
glfwSetKeyCallback(self->window, key_callback);
glfwSetMouseButtonCallback(self->window, mouse_button_callback);
glfwSetScrollCallback(self->window, scroll_callback);
glfwSetCursorPosCallback(self->window, cursor_pos_callback);
glfwSetWindowFocusCallback(self->window, window_focus_callback);
}
return (PyObject*)self;
}
// Global functions {{{
static PyObject *error_callback = NULL;
static void
@ -36,6 +117,7 @@ glfw_set_error_callback(PyObject UNUSED *self, PyObject *callback) {
Py_RETURN_NONE;
}
PyObject*
glfw_init(PyObject UNUSED *self) {
PyObject *ans = glfwInit() ? Py_True: Py_False;
@ -73,8 +155,130 @@ glfw_wait_events(PyObject UNUSED *self) {
Py_RETURN_NONE;
}
PyObject*
glfw_post_empty_event(PyObject UNUSED *self) {
glfwPostEmptyEvent();
Py_RETURN_NONE;
}
PyObject*
glfw_get_physical_dpi(PyObject UNUSED *self) {
GLFWmonitor *m = glfwGetPrimaryMonitor();
if (m == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get primary monitor"); return NULL; }
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 = vm->width / (width / 25.4);
float dpiy = vm->height / (height / 25.4);
return Py_BuildValue("ff", dpix, dpiy);
}
// }}}
static void
dealloc(Window* self) {
for(unsigned int i = 0; i < MAX_WINDOWS; i++) {
if (window_weakrefs[i] == self) window_weakrefs[i] = NULL;
}
Py_CLEAR(self->framebuffer_size_callback); Py_CLEAR(self->char_mods_callback); Py_CLEAR(self->key_callback); Py_CLEAR(self->mouse_button_callback); Py_CLEAR(self->scroll_callback); Py_CLEAR(self->cursor_pos_callback); Py_CLEAR(self->window_focus_callback);
if (self->window != NULL) glfwDestroyWindow(self->window);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject*
swap_buffers(Window *self) {
glfwSwapBuffers(self->window);
Py_RETURN_NONE;
}
static PyObject*
make_context_current(Window *self) {
glfwMakeContextCurrent(self->window);
Py_RETURN_NONE;
}
static PyObject*
should_close(Window *self) {
PyObject *ans = glfwWindowShouldClose(self->window) ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyObject*
get_clipboard_string(Window *self) {
return Py_BuildValue("s", glfwGetClipboardString(self->window));
}
static PyObject*
set_should_close(Window *self, PyObject *args) {
int c;
if (!PyArg_ParseTuple(args, "p", &c)) return NULL;
glfwSetWindowShouldClose(self->window, c);
Py_RETURN_NONE;
}
static PyObject*
is_key_pressed(Window *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_title(Window *self, PyObject *args) {
char *title;
if(!PyArg_ParseTuple(args, "s", &title)) return NULL;
glfwSetWindowTitle(self->window, title);
Py_RETURN_NONE;
}
// Boilerplate {{{
#define MND(name, args) {#name, (PyCFunction)name, args, ""}
static PyMethodDef methods[] = {
MND(swap_buffers, METH_NOARGS),
MND(get_clipboard_string, METH_NOARGS),
MND(should_close, METH_NOARGS),
MND(set_should_close, METH_VARARGS),
MND(is_key_pressed, METH_VARARGS),
MND(make_context_current, METH_NOARGS),
{"set_title", (PyCFunction)_set_title, METH_VARARGS, ""},
{NULL} /* Sentinel */
};
static PyMemberDef members[] = {
#define CBE(name) {#name, T_OBJECT_EX, offsetof(Window, name), 0, #name}
CBE(framebuffer_size_callback),
CBE(char_mods_callback),
CBE(key_callback),
CBE(mouse_button_callback),
CBE(scroll_callback),
CBE(cursor_pos_callback),
CBE(window_focus_callback),
{NULL}
#undef CBE
};
PyTypeObject Window_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.Window",
.tp_basicsize = sizeof(Window),
.tp_dealloc = (destructor)dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "A GLFW window",
.tp_methods = methods,
.tp_members = members,
.tp_new = new,
};
INIT_TYPE(Window)
// constants {{{
bool
init_glfw(PyObject *m) {
@ -336,3 +540,4 @@ return true;
#undef ADDC
}
// }}}
// }}}

View File

@ -15,3 +15,5 @@ PyObject* glfw_terminate(PyObject UNUSED *self);
PyObject* glfw_window_hint(PyObject UNUSED *self, PyObject *args);
PyObject* glfw_swap_interval(PyObject UNUSED *self, PyObject *args);
PyObject* glfw_wait_events(PyObject UNUSED *self);
PyObject* glfw_post_empty_event(PyObject UNUSED *self);
PyObject* glfw_get_physical_dpi(PyObject UNUSED *self);

View File

@ -19,9 +19,8 @@ from .fast_data_types import (
GLFW_CONTEXT_VERSION_MINOR, GLFW_OPENGL_PROFILE,
GLFW_OPENGL_FORWARD_COMPAT, GLFW_OPENGL_CORE_PROFILE, GLFW_SAMPLES,
glfw_set_error_callback, glfw_init, glfw_terminate, glfw_window_hint,
glfw_swap_interval, glfw_wait_events
glfw_swap_interval, glfw_wait_events, Window
)
import glfw
def option_parser():
@ -54,33 +53,29 @@ def clear_buffers(window, opts):
glClearColor(bg.red / 255, bg.green / 255, bg.blue / 255, 1)
glfw_swap_interval(0)
glClear(GL_COLOR_BUFFER_BIT)
glfw.glfwSwapBuffers(window)
window.swap_buffers()
glClear(GL_COLOR_BUFFER_BIT)
glfw_swap_interval(1)
def run_app(opts, args):
setup_opengl()
window = glfw.glfwCreateWindow(
viewport_size.width, viewport_size.height, args.cls.encode('utf-8'), None, None)
if not window:
raise SystemExit("glfwCreateWindow failed")
glfw.glfwSetWindowTitle(window, appname.encode('utf-8'))
window = Window(
viewport_size.width, viewport_size.height, args.cls)
window.set_title(appname)
window.make_context_current()
glewInit()
tabs = TabManager(window, opts, args)
tabs.start()
clear_buffers(window, opts)
try:
glfw.glfwMakeContextCurrent(window)
glewInit()
tabs = TabManager(window, opts, args)
tabs.start()
clear_buffers(window, opts)
try:
while not glfw.glfwWindowShouldClose(window):
tabs.render()
glfw.glfwSwapBuffers(window)
glfw_wait_events()
finally:
tabs.destroy()
while not window.should_close():
tabs.render()
window.swap_buffers()
glfw_wait_events()
finally:
glfw.glfwDestroyWindow(window)
tabs.destroy()
del window
def on_glfw_error(code, msg):

View File

@ -14,12 +14,11 @@ from threading import Thread
from time import monotonic
from queue import Queue, Empty
import glfw
from .child import Child
from .constants import viewport_size, shell_path, appname, set_tab_manager, tab_manager, wakeup, cell_size, MODIFIER_KEYS
from .fast_data_types import (
glViewport, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GLFW_PRESS,
GLFW_REPEAT, GLFW_MOUSE_BUTTON_1
GLFW_REPEAT, GLFW_MOUSE_BUTTON_1, glfw_post_empty_event
)
from .fonts import set_font_family
from .borders import Borders, BordersProgram
@ -123,13 +122,13 @@ class TabManager(Thread):
cell_size.width, cell_size.height = set_font_family(opts.font_family, opts.font_size)
self.opts, self.args = opts, args
self.glfw_window = glfw_window
glfw.glfwSetFramebufferSizeCallback(glfw_window, partial(self.queue_action, self.on_window_resize))
glfw.glfwSetCharModsCallback(glfw_window, partial(self.queue_action, self.on_text_input))
glfw.glfwSetKeyCallback(glfw_window, partial(self.queue_action, self.on_key))
glfw.glfwSetMouseButtonCallback(glfw_window, partial(self.queue_action, self.on_mouse_button))
glfw.glfwSetScrollCallback(glfw_window, partial(self.queue_action, self.on_mouse_scroll))
glfw.glfwSetCursorPosCallback(glfw_window, partial(self.queue_action, self.on_mouse_move))
glfw.glfwSetWindowFocusCallback(glfw_window, partial(self.queue_action, self.on_focus))
glfw_window.framebuffer_size_callback = partial(self.queue_action, self.on_window_resize)
glfw_window.char_mods_callback = partial(self.queue_action, self.on_text_input)
glfw_window.key_callback = partial(self.queue_action, self.on_key)
glfw_window.mouse_button_callback = partial(self.queue_action, self.on_mouse_button)
glfw_window.scroll_callback = partial(self.queue_action, self.on_mouse_scroll)
glfw_window.cursor_pos_callback = partial(self.queue_action, self.on_mouse_move)
glfw_window.window_focus_callback = partial(self.queue_action, self.on_focus)
self.tabs = deque()
self.tabs.append(Tab(opts, args))
self.sprites = Sprites()
@ -156,8 +155,8 @@ class TabManager(Thread):
def shutdown(self):
if not self.shutting_down:
self.shutting_down = True
glfw.glfwSetWindowShouldClose(self.glfw_window, True)
glfw.glfwPostEmptyEvent()
self.glfw_window.set_should_close(True)
glfw_post_empty_event()
def __iter__(self):
yield from iter(self.tabs)
@ -277,7 +276,7 @@ class TabManager(Thread):
tab.relayout()
self.pending_resize = None
self.resize_gl_viewport = True
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
@property
def active_tab(self):
@ -328,19 +327,19 @@ class TabManager(Thread):
return w
def on_mouse_button(self, window, button, action, mods):
w = self.window_for_pos(*glfw.glfwGetCursorPos(window))
w = self.window_for_pos(*window.get_cursor_pos())
if w is not None:
if button == GLFW_MOUSE_BUTTON_1 and w is not self.active_window:
pass # TODO: Switch focus to this window
w.on_mouse_button(window, button, action, mods)
def on_mouse_move(self, window, xpos, ypos):
w = self.window_for_pos(*glfw.glfwGetCursorPos(window))
w = self.window_for_pos(*window.get_cursor_pos())
if w is not None:
w.on_mouse_move(window, xpos, ypos)
def on_mouse_scroll(self, window, x, y):
w = self.window_for_pos(*glfw.glfwGetCursorPos(window))
w = self.window_for_pos(*window.get_cursor_pos())
if w is not None:
w.on_mouse_scroll(window, x, y)
@ -355,7 +354,7 @@ class TabManager(Thread):
if tab is not None:
if tab.title != self.glfw_window_title:
self.glfw_window_title = tab.title
glfw.glfwSetWindowTitle(self.glfw_window, self.glfw_window_title.encode('utf-8'))
self.glfw_window.set_title(self.glfw_window_title)
with self.sprites:
self.sprites.render_dirty_cells()
tab.render()

View File

@ -12,6 +12,7 @@ from contextlib import contextmanager
from functools import lru_cache
from time import monotonic
from .fast_data_types import glfw_get_physical_dpi
libc = ctypes.CDLL(None)
wcwidth_native = libc.wcwidth
@ -50,14 +51,9 @@ def get_logical_dpi():
def get_dpi():
import glfw
if not hasattr(get_dpi, 'ans'):
m = glfw.glfwGetPrimaryMonitor()
width, height = glfw.glfwGetMonitorPhysicalSize(m)
vmode = glfw.glfwGetVideoMode(m)
dpix = vmode.width / (width / 25.4)
dpiy = vmode.height / (height / 25.4)
get_dpi.ans = {'physical': (dpix, dpiy), 'logical': get_logical_dpi()}
pdpi = glfw_get_physical_dpi()
get_dpi.ans = {'physical': pdpi, 'logical': get_logical_dpi()}
return get_dpi.ans
# Color names {{{

View File

@ -8,14 +8,13 @@ from collections import deque
from functools import partial
from time import monotonic
import glfw
from .char_grid import CharGrid
from .constants import wakeup, tab_manager, appname, WindowGeometry
from .fast_data_types import (
BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump,
read_bytes, GLFW_MOD_SHIFT, GLFW_MOUSE_BUTTON_1, GLFW_PRESS,
GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, GLFW_KEY_LEFT_SHIFT,
GLFW_KEY_RIGHT_SHIFT
GLFW_KEY_RIGHT_SHIFT, glfw_post_empty_event
)
from .terminfo import get_capabilities
from .utils import sanitize_title, get_primary_selection
@ -83,7 +82,7 @@ class Window:
def update_screen(self):
self.char_grid.update_cell_data()
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def focus_changed(self, focused):
if focused:
@ -95,7 +94,7 @@ class Window:
def title_changed(self, new_title):
self.title = sanitize_title(new_title or appname)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def icon_changed(self, new_icon):
pass # TODO: Implement this
@ -113,7 +112,7 @@ class Window:
color_changes[w] = val
code += 1
self.char_grid.change_colors(color_changes)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def request_capabilities(self, q):
self.write_to_child(get_capabilities(q))
@ -121,16 +120,16 @@ class Window:
def dispatch_multi_click(self, x, y):
if len(self.click_queue) > 2 and self.click_queue[-1] - self.click_queue[-3] <= 2 * self.opts.click_interval:
self.char_grid.multi_click(3, x, y)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
elif len(self.click_queue) > 1 and self.click_queue[-1] - self.click_queue[-2] <= self.opts.click_interval:
self.char_grid.multi_click(2, x, y)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def on_mouse_button(self, window, button, action, mods):
handle_event = mods == GLFW_MOD_SHIFT or not self.screen.mouse_button_tracking_enabled()
if handle_event:
if button == GLFW_MOUSE_BUTTON_1:
x, y = glfw.glfwGetCursorPos(window)
x, y = window.get_cursor_pos()
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
self.char_grid.update_drag(action == GLFW_PRESS, x, y)
if action == GLFW_RELEASE:
@ -140,7 +139,7 @@ class Window:
if action == GLFW_RELEASE:
self.paste_from_selection()
else:
x, y = glfw.glfwGetCursorPos(window)
x, y = window.get_cursor_pos()
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
x, y = self.char_grid.cell_for_pos(x, y)
@ -150,14 +149,14 @@ class Window:
def on_mouse_scroll(self, window, x, y):
handle_event = (
glfw.glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS or
glfw.glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS or
window.is_key_pressed(GLFW_KEY_LEFT_SHIFT) or
window.is_key_pressed(GLFW_KEY_RIGHT_SHIFT) or
not self.screen.mouse_button_tracking_enabled())
if handle_event:
s = int(round(y * self.opts.wheel_scroll_multiplier))
if abs(s) > 0:
self.char_grid.scroll(abs(s), s > 0)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
# actions {{{
@ -170,9 +169,9 @@ class Window:
self.write_to_child(text)
def paste_from_clipboard(self):
text = glfw.glfwGetClipboardString(tab_manager().glfw_window)
text = tab_manager().glfw_window.get_clipboard_string()
if text:
self.paste(text.decode('utf-8'))
self.paste(text)
def paste_from_selection(self):
text = get_primary_selection()
@ -183,27 +182,27 @@ class Window:
def scroll_line_up(self):
self.char_grid.scroll('line', True)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def scroll_line_down(self):
self.char_grid.scroll('line', False)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def scroll_page_up(self):
self.char_grid.scroll('page', True)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def scroll_page_down(self):
self.char_grid.scroll('page', False)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def scroll_home(self):
self.char_grid.scroll('full', True)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
def scroll_end(self):
self.char_grid.scroll('full', False)
glfw.glfwPostEmptyEvent()
glfw_post_empty_event()
# }}}
def dump_commands(self, *a):