483 lines
18 KiB
C
483 lines
18 KiB
C
/*
|
|
* keys.c
|
|
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
|
*
|
|
* Distributed under terms of the GPL3 license.
|
|
*/
|
|
|
|
#include "state.h"
|
|
#include "keys.h"
|
|
#include "screen.h"
|
|
#include "glfw-wrapper.h"
|
|
#include "control-codes.h"
|
|
#include <structmember.h>
|
|
|
|
// python KeyEvent object {{{
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
PyObject *key, *shifted_key, *alternate_key;
|
|
PyObject *mods, *action, *native_key, *ime_state;
|
|
PyObject *text;
|
|
} PyKeyEvent;
|
|
|
|
static PyObject* convert_glfw_key_event_to_python(const GLFWkeyevent *ev);
|
|
|
|
static PyObject*
|
|
new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) {
|
|
static char *kwds[] = {"key", "shifted_key", "alternate_key", "mods", "action", "native_key", "ime_state", "text", NULL};
|
|
GLFWkeyevent ev = {.action=GLFW_PRESS};
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "I|IIiiiiz", kwds, &ev.key, &ev.shifted_key, &ev.alternate_key, &ev.mods, &ev.action, &ev.native_key, &ev.ime_state, &ev.text)) return NULL;
|
|
return convert_glfw_key_event_to_python(&ev);
|
|
}
|
|
|
|
static void
|
|
dealloc(PyKeyEvent* self) {
|
|
Py_CLEAR(self->key); Py_CLEAR(self->shifted_key); Py_CLEAR(self->alternate_key);
|
|
Py_CLEAR(self->mods); Py_CLEAR(self->action); Py_CLEAR(self->native_key); Py_CLEAR(self->ime_state);
|
|
Py_CLEAR(self->text);
|
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
}
|
|
|
|
static PyMemberDef members[] = {
|
|
#define M(x) {#x, T_OBJECT, offsetof(PyKeyEvent, x), READONLY, #x}
|
|
M(key), M(shifted_key), M(alternate_key),
|
|
M(mods), M(action), M(native_key), M(ime_state),
|
|
M(text),
|
|
{NULL},
|
|
#undef M
|
|
};
|
|
|
|
PyTypeObject PyKeyEvent_Type = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
.tp_name = "fast_data_types.KeyEvent",
|
|
.tp_basicsize = sizeof(PyKeyEvent),
|
|
.tp_dealloc = (destructor)dealloc,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = "A key event",
|
|
.tp_members = members,
|
|
.tp_new = new,
|
|
};
|
|
|
|
static PyObject*
|
|
convert_glfw_key_event_to_python(const GLFWkeyevent *ev) {
|
|
PyKeyEvent *self = (PyKeyEvent*)PyKeyEvent_Type.tp_alloc(&PyKeyEvent_Type, 0);
|
|
if (!self) return NULL;
|
|
#define C(x) { unsigned long t = ev->x; self->x = PyLong_FromUnsignedLong(t); if (self->x == NULL) { Py_CLEAR(self); return NULL; } }
|
|
C(key); C(shifted_key); C(alternate_key); C(mods); C(action); C(native_key); C(ime_state);
|
|
#undef C
|
|
self->text = PyUnicode_FromString(ev->text ? ev->text : "");
|
|
if (!self->text) { Py_CLEAR(self); return NULL; }
|
|
return (PyObject*)self;
|
|
}
|
|
// }}}
|
|
|
|
static Window*
|
|
active_window(void) {
|
|
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;
|
|
}
|
|
|
|
void
|
|
update_ime_focus(OSWindow *osw, bool focused) {
|
|
if (!osw || !osw->handle) return;
|
|
GLFWIMEUpdateEvent ev = { .focused = focused, .type = GLFW_IME_UPDATE_FOCUS };
|
|
glfwUpdateIMEState(osw->handle, &ev);
|
|
}
|
|
|
|
void
|
|
update_ime_position(Window* w, Screen *screen) {
|
|
unsigned int cell_width = global_state.callback_os_window->fonts_data->cell_width, cell_height = global_state.callback_os_window->fonts_data->cell_height;
|
|
unsigned int left = w->geometry.left, top = w->geometry.top;
|
|
left += screen->cursor->x * cell_width;
|
|
top += screen->cursor->y * cell_height;
|
|
GLFWIMEUpdateEvent ev = { .type = GLFW_IME_UPDATE_CURSOR_POSITION };
|
|
ev.cursor.left = left; ev.cursor.top = top; ev.cursor.width = cell_width; ev.cursor.height = cell_height;
|
|
glfwUpdateIMEState(global_state.callback_os_window->handle, &ev);
|
|
}
|
|
|
|
const char*
|
|
format_mods(unsigned mods) {
|
|
static char buf[128];
|
|
char *p = buf, *s;
|
|
#define pr(x) p += snprintf(p, sizeof(buf) - (p - buf) - 1, x)
|
|
pr("mods: ");
|
|
s = p;
|
|
if (mods & GLFW_MOD_CONTROL) pr("ctrl+");
|
|
if (mods & GLFW_MOD_ALT) pr("alt+");
|
|
if (mods & GLFW_MOD_SHIFT) pr("shift+");
|
|
if (mods & GLFW_MOD_SUPER) pr("super+");
|
|
if (mods & GLFW_MOD_HYPER) pr("hyper+");
|
|
if (mods & GLFW_MOD_META) pr("meta+");
|
|
if (mods & GLFW_MOD_CAPS_LOCK) pr("capslock+");
|
|
if (mods & GLFW_MOD_NUM_LOCK) pr("numlock+");
|
|
if (p == s) pr("none");
|
|
else p--;
|
|
pr(" ");
|
|
#undef pr
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
on_key_input(GLFWkeyevent *ev) {
|
|
Window *w = active_window();
|
|
const int action = ev->action, mods = ev->mods;
|
|
const uint32_t key = ev->key, native_key = ev->native_key;
|
|
const char *text = ev->text ? ev->text : "";
|
|
|
|
debug("\x1b[33mon_key_input\x1b[m: glfw key: 0x%x native_code: 0x%x action: %s %stext: '%s' state: %d ",
|
|
key, native_key,
|
|
(action == GLFW_RELEASE ? "RELEASE" : (action == GLFW_PRESS ? "PRESS" : "REPEAT")),
|
|
format_mods(mods), text, ev->ime_state);
|
|
if (!w) { debug("no active window, ignoring\n"); return; }
|
|
if (OPT(mouse_hide_wait) < 0 && !is_modifier_key(key)) hide_mouse(global_state.callback_os_window);
|
|
Screen *screen = w->render_data.screen;
|
|
id_type active_window_id = w->id;
|
|
|
|
switch(ev->ime_state) {
|
|
case GLFW_IME_WAYLAND_DONE_EVENT:
|
|
// If we update IME position here it sends GNOME's text input system into
|
|
// an infinite loop. See https://github.com/kovidgoyal/kitty/issues/5105
|
|
screen_draw_overlay_text(screen, NULL);
|
|
debug("handled wayland IME done event\n");
|
|
return;
|
|
case GLFW_IME_PREEDIT_CHANGED:
|
|
update_ime_position(w, screen);
|
|
screen_draw_overlay_text(screen, text);
|
|
debug("updated pre-edit text: '%s'\n", text);
|
|
return;
|
|
case GLFW_IME_COMMIT_TEXT:
|
|
if (*text) {
|
|
schedule_write_to_child(w->id, 1, text, strlen(text));
|
|
debug("committed pre-edit text: %s\n", text);
|
|
} else debug("committed pre-edit text: (null)\n");
|
|
screen_draw_overlay_text(screen, NULL);
|
|
return;
|
|
case GLFW_IME_NONE:
|
|
// for macOS, update ime position on every key input
|
|
// because the position is required before next input
|
|
// On Linux this is needed by Fig integration: https://github.com/kovidgoyal/kitty/issues/5241
|
|
update_ime_position(w, screen);
|
|
break;
|
|
default:
|
|
debug("invalid state, ignoring\n");
|
|
return;
|
|
}
|
|
PyObject *ke = NULL;
|
|
#define create_key_event() { ke = convert_glfw_key_event_to_python(ev); if (!ke) { PyErr_Print(); return; } }
|
|
if (global_state.in_sequence_mode) {
|
|
debug("in sequence mode, handling as shortcut\n");
|
|
if (
|
|
action != GLFW_RELEASE && !is_modifier_key(key)
|
|
) {
|
|
w->last_special_key_pressed = key;
|
|
create_key_event();
|
|
call_boss(process_sequence, "O", ke);
|
|
Py_CLEAR(ke);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
|
create_key_event();
|
|
w->last_special_key_pressed = 0;
|
|
PyObject *ret = PyObject_CallMethod(global_state.boss, "dispatch_possible_special_key", "O", ke);
|
|
Py_CLEAR(ke);
|
|
bool consumed = false;
|
|
// the shortcut could have created a new window or closed the
|
|
// window, rendering the pointer no longer valid
|
|
w = window_for_window_id(active_window_id);
|
|
if (ret == NULL) { PyErr_Print(); }
|
|
else {
|
|
consumed = ret == Py_True;
|
|
Py_DECREF(ret);
|
|
if (consumed) {
|
|
debug("handled as shortcut\n");
|
|
if (w) w->last_special_key_pressed = key;
|
|
return;
|
|
}
|
|
}
|
|
if (!w) return;
|
|
} else if (w->last_special_key_pressed == key) {
|
|
w->last_special_key_pressed = 0;
|
|
debug("ignoring release event for previous press that was handled as shortcut\n");
|
|
return;
|
|
}
|
|
#undef create_key_event
|
|
if (action == GLFW_REPEAT && !screen->modes.mDECARM) {
|
|
debug("discarding repeat key event as DECARM is off\n");
|
|
return;
|
|
}
|
|
if (screen->scrolled_by && action == GLFW_PRESS && !is_modifier_key(key)) {
|
|
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
|
}
|
|
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
|
int size = encode_glfw_key_event(ev, screen->modes.mDECCKM, screen_current_key_encoding_flags(screen), encoded_key);
|
|
if (size == SEND_TEXT_TO_CHILD) {
|
|
schedule_write_to_child(w->id, 1, text, strlen(text));
|
|
debug("sent key as text to child\n");
|
|
} else if (size > 0) {
|
|
if (size == 1 && screen->modes.mHANDLE_TERMIOS_SIGNALS) {
|
|
if (screen_send_signal_for_key(screen, *encoded_key)) return;
|
|
}
|
|
schedule_write_to_child(w->id, 1, encoded_key, size);
|
|
debug("sent encoded key to child\n");
|
|
} else {
|
|
debug("ignoring as keyboard mode does not support encoding this event\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
fake_scroll(Window *w, int amount, bool upwards) {
|
|
if (!w) return;
|
|
int key = upwards ? GLFW_FKEY_UP : GLFW_FKEY_DOWN;
|
|
GLFWkeyevent ev = {.key = key };
|
|
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
|
Screen *screen = w->render_data.screen;
|
|
uint8_t flags = screen_current_key_encoding_flags(screen);
|
|
while (amount-- > 0) {
|
|
ev.action = GLFW_PRESS;
|
|
int size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);
|
|
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
|
ev.action = GLFW_RELEASE;
|
|
size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);
|
|
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
|
}
|
|
}
|
|
|
|
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
|
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
|
#define M(name, arg_type) {#name, (PyCFunction)(void (*) (void))(py##name), arg_type, NULL}
|
|
|
|
PYWRAP1(key_for_native_key_name) {
|
|
const char *name;
|
|
int case_sensitive = 0;
|
|
PA("s|p", &name, &case_sensitive);
|
|
#ifndef __APPLE__
|
|
if (glfwGetNativeKeyForName) { // if this function is called before GLFW is initialized glfwGetNativeKeyForName will be NULL
|
|
int native_key = glfwGetNativeKeyForName(name, case_sensitive);
|
|
if (native_key) return Py_BuildValue("i", native_key);
|
|
}
|
|
#endif
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject*
|
|
pyencode_key_for_tty(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
|
static char *kwds[] = {"key", "shifted_key", "alternate_key", "mods", "action", "key_encoding_flags", "text", "cursor_key_mode", NULL};
|
|
unsigned int key = 0, shifted_key = 0, alternate_key = 0, mods = 0, action = GLFW_PRESS, key_encoding_flags = 0;
|
|
const char *text = NULL;
|
|
int cursor_key_mode = 0;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "I|IIIIIzp", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &key_encoding_flags, &text, &cursor_key_mode)) return NULL;
|
|
GLFWkeyevent ev = { .key = key, .shifted_key = shifted_key, .alternate_key = alternate_key, .text = text, .action = action, .mods = mods };
|
|
char output[KEY_BUFFER_SIZE+1] = {0};
|
|
int num = encode_glfw_key_event(&ev, cursor_key_mode, key_encoding_flags, output);
|
|
if (num == SEND_TEXT_TO_CHILD) return PyUnicode_FromString(text);
|
|
return PyUnicode_FromStringAndSize(output, MAX(0, num));
|
|
}
|
|
|
|
static PyMethodDef module_methods[] = {
|
|
M(key_for_native_key_name, METH_VARARGS),
|
|
M(encode_key_for_tty, METH_VARARGS | METH_KEYWORDS),
|
|
{0}
|
|
};
|
|
|
|
// SingleKey {{{
|
|
typedef uint64_t keybitfield;
|
|
#define KEY_BITS 51
|
|
#define MOD_BITS 12
|
|
#if 1 << (MOD_BITS-1) < GLFW_MOD_KITTY
|
|
#error "Not enough mod bits"
|
|
#endif
|
|
typedef union Key {
|
|
struct {
|
|
keybitfield mods : MOD_BITS;
|
|
keybitfield is_native: 1;
|
|
keybitfield key : KEY_BITS;
|
|
};
|
|
keybitfield val;
|
|
} Key;
|
|
|
|
static PyTypeObject SingleKey_Type;
|
|
static char *SingleKey_kwds[] = {"mods", "is_native", "key", NULL};
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
|
|
Key key;
|
|
bool defined_with_kitty_mod;
|
|
} SingleKey;
|
|
|
|
static inline void
|
|
SingleKey_set_vals(SingleKey *self, long long key, unsigned short mods, int is_native) {
|
|
if (key >= 0 && (unsigned long long)key <= BIT_MASK(keybitfield, KEY_BITS)) {
|
|
keybitfield k = (keybitfield)(unsigned long long)key;
|
|
self->key.key = k & BIT_MASK(keybitfield, KEY_BITS);
|
|
}
|
|
if (!(mods & 1 << (MOD_BITS + 1))) self->key.mods = mods & BIT_MASK(uint32_t, MOD_BITS);
|
|
if (is_native > -1) self->key.is_native = is_native ? 1 : 0;
|
|
}
|
|
|
|
static PyObject *
|
|
SingleKey_new(PyTypeObject *type, PyObject *args, PyObject *kw) {
|
|
long long key = -1; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|HpL", SingleKey_kwds, &mods, &is_native, &key)) return NULL;
|
|
SingleKey *self = (SingleKey *)type->tp_alloc(type, 0);
|
|
if (self) SingleKey_set_vals(self, key, mods, is_native);
|
|
return (PyObject*)self;
|
|
}
|
|
|
|
static void
|
|
SingleKey_dealloc(SingleKey* self) {
|
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_repr(PyObject *s) {
|
|
SingleKey *self = (SingleKey*)s;
|
|
char buf[128];
|
|
int pos = 0;
|
|
pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "SingleKey(");
|
|
unsigned int mods = self->key.mods;
|
|
if (mods) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "mods=%u, ", mods);
|
|
if (self->key.is_native) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "is_native=True, ");
|
|
unsigned long long key = self->key.key;
|
|
if (key) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "key=%llu, ", key);
|
|
if (buf[pos-1] == ' ') pos -= 2;
|
|
pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, ")");
|
|
return PyUnicode_FromString(buf);
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_get_key(SingleKey *self, void UNUSED *closure) {
|
|
const unsigned long long val = self->key.key;
|
|
return PyLong_FromUnsignedLongLong(val);
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_get_mods(SingleKey *self, void UNUSED *closure) {
|
|
const unsigned long mods = self->key.mods;
|
|
return PyLong_FromUnsignedLong(mods);
|
|
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_get_is_native(SingleKey *self, void UNUSED *closure) {
|
|
if (self->key.is_native) Py_RETURN_TRUE;
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_defined_with_kitty_mod(SingleKey *self, void UNUSED *closure) {
|
|
if (self->defined_with_kitty_mod || (self->key.mods & GLFW_MOD_KITTY)) Py_RETURN_TRUE;
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
|
|
static PyGetSetDef SingleKey_getsetters[] = {
|
|
{"key", (getter)SingleKey_get_key, NULL, "The key as an integer", NULL},
|
|
{"mods", (getter)SingleKey_get_mods, NULL, "The modifiers as an integer", NULL},
|
|
{"is_native", (getter)SingleKey_get_is_native, NULL, "A bool", NULL},
|
|
{"defined_with_kitty_mod", (getter)SingleKey_defined_with_kitty_mod, NULL, "A bool", NULL},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
static Py_hash_t
|
|
SingleKey_hash(PyObject *self) {
|
|
Py_hash_t ans = ((SingleKey*)self)->key.val;
|
|
if (ans == -1) ans = -2;
|
|
return ans;
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_richcompare(PyObject *self, PyObject *other, int op) {
|
|
if (!PyObject_TypeCheck(other, &SingleKey_Type)) { PyErr_SetString(PyExc_TypeError, "Cannot compare SingleKey to other objects"); return NULL; }
|
|
SingleKey *a = (SingleKey*)self, *b = (SingleKey*)other;
|
|
Py_RETURN_RICHCOMPARE(a->key.val, b->key.val, op);
|
|
}
|
|
|
|
static Py_ssize_t
|
|
SingleKey___len__(PyObject *self UNUSED) {
|
|
return 3;
|
|
}
|
|
|
|
static PyObject *
|
|
SingleKey_item(PyObject *o, Py_ssize_t i) {
|
|
SingleKey *self = (SingleKey*)o;
|
|
switch(i) {
|
|
case 0:
|
|
return SingleKey_get_mods(self, NULL);
|
|
case 1:
|
|
return SingleKey_get_is_native(self, NULL);
|
|
case 2:
|
|
return SingleKey_get_key(self, NULL);
|
|
}
|
|
PyErr_SetString(PyExc_IndexError, "tuple index out of range");
|
|
return NULL;
|
|
}
|
|
|
|
static PySequenceMethods SingleKey_sequence_methods = {
|
|
.sq_length = SingleKey___len__,
|
|
.sq_item = SingleKey_item,
|
|
};
|
|
|
|
static PyObject*
|
|
SingleKey_resolve_kitty_mod(SingleKey *self, PyObject *km) {
|
|
if (!(self->key.mods & GLFW_MOD_KITTY)) { Py_INCREF(self); return (PyObject*)self; }
|
|
unsigned long kitty_mod = PyLong_AsUnsignedLong(km);
|
|
if (PyErr_Occurred()) return NULL;
|
|
SingleKey *ans = (SingleKey*)SingleKey_Type.tp_alloc(&SingleKey_Type, 0);
|
|
if (!ans) return NULL;
|
|
ans->key.val = self->key.val;
|
|
ans->key.mods = (ans->key.mods & ~GLFW_MOD_KITTY) | kitty_mod;
|
|
ans->defined_with_kitty_mod = true;
|
|
return (PyObject*)ans;
|
|
}
|
|
|
|
static PyObject*
|
|
SingleKey_replace(SingleKey *self, PyObject *args, PyObject *kw) {
|
|
long long key = -2; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kw, "|HpL", SingleKey_kwds, &mods, &is_native, &key)) return NULL;
|
|
SingleKey *ans = (SingleKey*)SingleKey_Type.tp_alloc(&SingleKey_Type, 0);
|
|
if (ans) {
|
|
if (key == -1) key = 0;
|
|
ans->key.val = self->key.val;
|
|
SingleKey_set_vals(ans, key, mods, is_native);
|
|
}
|
|
return (PyObject*)ans;
|
|
}
|
|
|
|
static PyMethodDef SingleKey_methods[] = {
|
|
{"_replace", (PyCFunction)(void (*) (void))SingleKey_replace, METH_VARARGS | METH_KEYWORDS, ""},
|
|
{"resolve_kitty_mod", (PyCFunction)SingleKey_resolve_kitty_mod, METH_O, ""},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
|
|
static PyTypeObject SingleKey_Type = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
.tp_name = "fast_data_types.SingleKey",
|
|
.tp_basicsize = sizeof(SingleKey),
|
|
.tp_dealloc = (destructor)SingleKey_dealloc,
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_doc = "Compact and fast representation of a single key as defined in the config",
|
|
.tp_new = SingleKey_new,
|
|
.tp_hash = SingleKey_hash,
|
|
.tp_richcompare = SingleKey_richcompare,
|
|
.tp_as_sequence = &SingleKey_sequence_methods,
|
|
.tp_repr = SingleKey_repr,
|
|
.tp_methods = SingleKey_methods,
|
|
.tp_getset = SingleKey_getsetters,
|
|
}; // }}}
|
|
|
|
|
|
bool
|
|
init_keys(PyObject *module) {
|
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
|
if (PyType_Ready(&PyKeyEvent_Type) < 0) return false;
|
|
if (PyModule_AddObject(module, "KeyEvent", (PyObject *)&PyKeyEvent_Type) != 0) return 0;
|
|
Py_INCREF(&PyKeyEvent_Type);
|
|
ADD_TYPE(SingleKey);
|
|
return true;
|
|
}
|