linux input handling improvements
parent
73b964c3ea
commit
d1e86d78bb
|
@ -83,16 +83,17 @@
|
|||
|
||||
#define Cursor XCursor
|
||||
#undef function
|
||||
#undef internal
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#define function static
|
||||
#undef Cursor
|
||||
|
||||
#undef internal
|
||||
#include <fontconfig/fontconfig.h>
|
||||
#define internal static
|
||||
|
||||
|
@ -151,8 +152,11 @@ struct Linux_Vars {
|
|||
XIM xim;
|
||||
XIC xic;
|
||||
FcConfig* fontconfig;
|
||||
XkbDescPtr xkb;
|
||||
|
||||
Linux_Input_Chunk input;
|
||||
int xkb_event;
|
||||
int xkb_group; // active keyboard layout (0-3)
|
||||
|
||||
int epoll;
|
||||
int step_timer_fd;
|
||||
|
@ -593,7 +597,7 @@ linux_find_font(Face_Description* desc) {
|
|||
FcChar8 *filename = 0;
|
||||
FcPatternGetString(font, FC_FILE, 0, &filename);
|
||||
if(filename) {
|
||||
printf("FONTCONFIG FILENAME = %s\n", filename);
|
||||
LINUX_FN_DEBUG("FONTCONFIG FILENAME = %s\n", filename);
|
||||
result.font.file_name = push_u8_stringf(linuxvars.frame_arena, "%s", filename);
|
||||
}
|
||||
|
||||
|
@ -608,14 +612,19 @@ linux_find_font(Face_Description* desc) {
|
|||
internal
|
||||
font_make_face_sig() {
|
||||
|
||||
// FIXME
|
||||
if(description->font.file_name.str[0] != '/') {
|
||||
Face_Description desc2 = linux_find_font(description);
|
||||
description = &desc2;
|
||||
}
|
||||
|
||||
Face* result = ft__font_make_face(arena, description, scale_factor);
|
||||
|
||||
// if it failed to load the font directly, try via fontconfig.
|
||||
if(!result) {
|
||||
Face_Description desc2 = {};
|
||||
desc2.parameters = description->parameters;
|
||||
desc2.font.file_name = string_front_of_path(description->font.file_name);
|
||||
|
||||
printf("FONT %.*s\n", string_expand(desc2.font.file_name));
|
||||
desc2 = linux_find_font(&desc2);
|
||||
result = ft__font_make_face(arena, &desc2, scale_factor);
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
// is this fatal? 4ed.cpp:277 (caller) does not check for null.
|
||||
String_Const_u8 s = description->font.file_name;
|
||||
|
@ -882,12 +891,6 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) {
|
|||
|
||||
XSync(linuxvars.dpy, False);
|
||||
|
||||
XWindowAttributes attr;
|
||||
if (XGetWindowAttributes(linuxvars.dpy, linuxvars.win, &attr)){
|
||||
//*window_width = WinAttribs.width;
|
||||
//*window_height = WinAttribs.height;
|
||||
}
|
||||
|
||||
Atom wm_protos[] = {
|
||||
linuxvars.atom_WM_DELETE_WINDOW,
|
||||
linuxvars.atom__NET_WM_PING
|
||||
|
@ -913,12 +916,8 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) {
|
|||
XSetLocaleModifiers("");
|
||||
b32 locale_supported = XSupportsLocale();
|
||||
|
||||
//LOGF("Supported locale?: %s.\n", locale_supported ? "Yes" : "No");
|
||||
if (!locale_supported){
|
||||
//LOG("Reverting to 'C' ... ");
|
||||
setlocale(LC_ALL, "C");
|
||||
locale_supported = XSupportsLocale();
|
||||
//LOGF("C is supported? %s.\n", locale_supported ? "Yes" : "No");
|
||||
}
|
||||
|
||||
linuxvars.xim = XOpenIM(dpy, 0, 0, 0);
|
||||
|
@ -975,12 +974,27 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) {
|
|||
| EnterWindowMask | LeaveWindowMask
|
||||
| PointerMotionMask
|
||||
| FocusChangeMask
|
||||
| StructureNotifyMask | MappingNotify
|
||||
| StructureNotifyMask
|
||||
| ExposureMask | VisibilityChangeMask
|
||||
| xim_event_mask;
|
||||
|
||||
XSelectInput(linuxvars.dpy, linuxvars.win, event_mask);
|
||||
|
||||
// init XKB keyboard extension
|
||||
|
||||
if(!XkbQueryExtension(linuxvars.dpy, 0, &linuxvars.xkb_event, 0, 0, 0)) {
|
||||
system_error_box("XKB Extension not available.");
|
||||
}
|
||||
|
||||
XkbSelectEvents(linuxvars.dpy, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask);
|
||||
linuxvars.xkb = XkbGetKeyboard(linuxvars.dpy, XkbAllComponentsMask, XkbUseCoreKbd);
|
||||
if(!linuxvars.xkb) {
|
||||
system_error_box("Error getting XKB keyboard details.");
|
||||
}
|
||||
|
||||
// closer to windows behaviour (holding key doesn't generate release events)
|
||||
XkbSetDetectableAutoRepeat(linuxvars.dpy, True, NULL);
|
||||
|
||||
XCursor cursors[APP_MOUSE_CURSOR_COUNT] = {
|
||||
None,
|
||||
None,
|
||||
|
@ -1007,33 +1021,56 @@ global Key_Code keycode_lookup_table[255];
|
|||
internal void
|
||||
linux_keycode_init(Display* dpy){
|
||||
|
||||
struct SymCode { KeySym sym; Key_Code code; };
|
||||
SymCode sym_table[100];
|
||||
|
||||
block_zero_array(sym_table);
|
||||
block_zero_array(keycode_lookup_table);
|
||||
|
||||
SymCode* p = sym_table;
|
||||
for(Key_Code k = KeyCode_A; k <= KeyCode_Z; ++k) {
|
||||
*p++ = { XK_a + (k - KeyCode_A), k };
|
||||
// Find these keys by physical position, and map to QWERTY KeyCodes
|
||||
#define K(k) glue(KeyCode_, k)
|
||||
static const u8 positional_keys[] = {
|
||||
K(1), K(2), K(3), K(4), K(5), K(6), K(7), K(8), K(9), K(0), K(Minus), K(Equal),
|
||||
K(Q), K(W), K(E), K(R), K(T), K(Y), K(U), K(I), K(O), K(P), K(LeftBracket), K(RightBracket),
|
||||
K(A), K(S), K(D), K(F), K(G), K(H), K(J), K(K), K(L), K(Semicolon), K(Quote), /*uk hash*/0,
|
||||
K(Z), K(X), K(C), K(V), K(B), K(N), K(M), K(Comma), K(Period), K(ForwardSlash), 0, 0
|
||||
};
|
||||
#undef K
|
||||
|
||||
// XKB gives the alphanumeric keys names like AE01 -> E is the row (from B-E), 01 is the column (01-12).
|
||||
// to get key names in .ps file: setxkbmap -print | xkbcomp - - | xkbprint -label name - out.ps
|
||||
|
||||
static const int ncols = 12;
|
||||
static const int nrows = 4;
|
||||
|
||||
for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) {
|
||||
const char* name = linuxvars.xkb->names->keys[i].name;
|
||||
|
||||
if(name[0] == 'A' && name[1] >= 'B' && name[1] <= 'E') {
|
||||
int row = (nrows - 1) - (name[1] - 'B');
|
||||
int col = (name[2] - '0') * 10 + (name[3] - '0') - 1;
|
||||
|
||||
if(row >= 0 && row < nrows && col >= 0 && col < ncols) {
|
||||
keycode_lookup_table[i] = positional_keys[row * ncols + col];
|
||||
}
|
||||
}
|
||||
|
||||
// a few special cases:
|
||||
|
||||
else if(memcmp(name, "TLDE", XkbKeyNameLength) == 0) {
|
||||
keycode_lookup_table[i] = KeyCode_Tick;
|
||||
} else if(memcmp(name, "BKSL", XkbKeyNameLength) == 0) {
|
||||
keycode_lookup_table[i] = KeyCode_BackwardSlash;
|
||||
} else if(memcmp(name, "LSGT", XkbKeyNameLength) == 0) {
|
||||
// UK extra key between left shift and Z, not sure what to do with it...
|
||||
// Setting to F13-F16 seems to break text input.
|
||||
// it prints \ and | with shift. KeyCode_Backslash will be where UK # is.
|
||||
// keycode_lookup_table[i] =
|
||||
}
|
||||
}
|
||||
|
||||
for(Key_Code k = KeyCode_0; k <= KeyCode_9; ++k) {
|
||||
*p++ = { XK_0 + (k - KeyCode_0), k };
|
||||
}
|
||||
// Find the rest by their key label
|
||||
struct SymCode { KeySym sym; Key_Code code; };
|
||||
SymCode sym_table[100];
|
||||
SymCode* p = sym_table;
|
||||
|
||||
*p++ = { XK_space, KeyCode_Space };
|
||||
*p++ = { XK_grave, KeyCode_Tick };
|
||||
*p++ = { XK_minus, KeyCode_Minus };
|
||||
*p++ = { XK_equal, KeyCode_Equal };
|
||||
*p++ = { XK_bracketleft, KeyCode_LeftBracket };
|
||||
*p++ = { XK_bracketright, KeyCode_RightBracket };
|
||||
*p++ = { XK_semicolon, KeyCode_Semicolon };
|
||||
*p++ = { XK_apostrophe, KeyCode_Quote };
|
||||
*p++ = { XK_comma, KeyCode_Comma };
|
||||
*p++ = { XK_period, KeyCode_Period };
|
||||
*p++ = { XK_slash, KeyCode_ForwardSlash };
|
||||
*p++ = { XK_backslash, KeyCode_BackwardSlash };
|
||||
*p++ = { XK_Tab, KeyCode_Tab };
|
||||
*p++ = { XK_Escape, KeyCode_Escape };
|
||||
*p++ = { XK_Pause, KeyCode_Pause };
|
||||
|
@ -1069,27 +1106,21 @@ linux_keycode_init(Display* dpy){
|
|||
const int table_size = p - sym_table;
|
||||
Assert(table_size < ArrayCount(sym_table));
|
||||
|
||||
int key_min, key_max, syms_per_code;
|
||||
XDisplayKeycodes(dpy, &key_min, &key_max);
|
||||
for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) {
|
||||
KeySym sym = NoSymbol;
|
||||
|
||||
int key_count = (key_max - key_min) + 1;
|
||||
KeySym* syms = XGetKeyboardMapping(dpy, key_min, key_count, &syms_per_code);
|
||||
// lookup key in current layout with no modifiers held (0)
|
||||
if(!XkbTranslateKeyCode(linuxvars.xkb, i, XkbBuildCoreState(0, linuxvars.xkb_group), NULL, &sym)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syms == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
int key = key_min;
|
||||
for(int i = 0; i < key_count * syms_per_code; ++i){
|
||||
for(int j = 0; j < table_size; ++j){
|
||||
if (sym_table[j].sym == syms[i]){
|
||||
keycode_lookup_table[key + (i/syms_per_code)] = sym_table[j].code;
|
||||
for(int j = 0; j < table_size; ++j) {
|
||||
if(sym_table[j].sym == sym) {
|
||||
keycode_lookup_table[i] = sym_table[j].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XFree(syms);
|
||||
}
|
||||
|
||||
internal void
|
||||
|
@ -1097,9 +1128,8 @@ linux_epoll_init(void) {
|
|||
struct epoll_event e = {};
|
||||
e.events = EPOLLIN | EPOLLET;
|
||||
|
||||
//linuxvars.inotify_fd = inotify_init1(IN_NONBLOCK);
|
||||
linuxvars.step_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
|
||||
linuxvars.epoll = epoll_create(16);
|
||||
linuxvars.epoll = epoll_create(16);
|
||||
|
||||
e.data.ptr = &epoll_tag_x11;
|
||||
epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, ConnectionNumber(linuxvars.dpy), &e);
|
||||
|
@ -1261,18 +1291,16 @@ linux_handle_x11_events() {
|
|||
int len = Xutf8LookupString(linuxvars.xic, &event.xkey, (char*)buf, sizeof(buf) - 1, &keysym, &status);
|
||||
|
||||
if (status == XBufferOverflow){
|
||||
//TODO(inso): handle properly
|
||||
Xutf8ResetIC(linuxvars.xic);
|
||||
XSetICFocus(linuxvars.xic);
|
||||
}
|
||||
|
||||
if (keysym == XK_ISO_Left_Tab){
|
||||
printf("left tab? [%.*s]\n", len, buf);
|
||||
add_modifier(mods, KeyCode_Shift);
|
||||
}
|
||||
|
||||
Key_Code key = keycode_lookup_table[(u8)event.xkey.keycode];
|
||||
printf("key [%d] = %s\n", event.xkey.keycode, key_code_name[key]);
|
||||
//printf("key %d = %s\n", event.xkey.keycode, key_code_name[key]);
|
||||
|
||||
Input_Event* key_event = NULL;
|
||||
if(key) {
|
||||
|
@ -1323,7 +1351,9 @@ linux_handle_x11_events() {
|
|||
} break;
|
||||
|
||||
case MotionNotify: {
|
||||
linuxvars.input.pers.mouse = { event.xmotion.x, event.xmotion.y };
|
||||
int x = clamp(0, event.xmotion.x, render_target.width - 1);
|
||||
int y = clamp(0, event.xmotion.y, render_target.height - 1);
|
||||
linuxvars.input.pers.mouse = { x, y };
|
||||
should_step = true;
|
||||
} break;
|
||||
|
||||
|
@ -1333,6 +1363,16 @@ linux_handle_x11_events() {
|
|||
case Button1: {
|
||||
linuxvars.input.trans.mouse_l_press = true;
|
||||
linuxvars.input.pers.mouse_l = true;
|
||||
|
||||
// NOTE(inso): improves selection dragging (especially in notepad-like mode).
|
||||
// we will still get mouse events when the pointer leaves the window if it's dragging.
|
||||
XGrabPointer(
|
||||
linuxvars.dpy,
|
||||
linuxvars.win,
|
||||
True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
None, None, CurrentTime);
|
||||
|
||||
} break;
|
||||
|
||||
case Button3: {
|
||||
|
@ -1356,6 +1396,8 @@ linux_handle_x11_events() {
|
|||
case Button1: {
|
||||
linuxvars.input.trans.mouse_l_release = true;
|
||||
linuxvars.input.pers.mouse_l = false;
|
||||
|
||||
XUngrabPointer(linuxvars.dpy, CurrentTime);
|
||||
} break;
|
||||
|
||||
case Button3: {
|
||||
|
@ -1365,13 +1407,6 @@ linux_handle_x11_events() {
|
|||
}
|
||||
} break;
|
||||
|
||||
case MappingNotify: {
|
||||
if (event.xmapping.request == MappingModifier || event.xmapping.request == MappingKeyboard){
|
||||
XRefreshKeyboardMapping(&event.xmapping);
|
||||
linux_keycode_init(linuxvars.dpy);
|
||||
}
|
||||
} break;
|
||||
|
||||
case FocusIn:
|
||||
case FocusOut: {
|
||||
linuxvars.input.pers.mouse_l = false;
|
||||
|
@ -1454,6 +1489,16 @@ linux_handle_x11_events() {
|
|||
}
|
||||
}
|
||||
|
||||
else if(event.type == linuxvars.xkb_event) {
|
||||
XkbEvent* kb = (XkbEvent*)&event;
|
||||
|
||||
// Keyboard layout changed, refresh lookup table.
|
||||
if(kb->any.xkb_type == XkbStateNotify && kb->state.group != linuxvars.xkb_group) {
|
||||
linuxvars.xkb_group = kb->state.group;
|
||||
XkbRefreshKeyboardMapping((XkbMapNotifyEvent*)kb);
|
||||
linux_keycode_init(linuxvars.dpy);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@ system_get_path(Arena* arena, System_Path_Code path_code){
|
|||
case SystemPath_CurrentDirectory: {
|
||||
// glibc extension: getcwd allocates its own memory if passed NULL
|
||||
char *working_dir = getcwd(NULL, 0);
|
||||
LINUX_FN_DEBUG("cwd = [%s]", working_dir);
|
||||
|
||||
u64 working_dir_len = cstring_length(working_dir);
|
||||
u8 *out = push_array(arena, u8, working_dir_len + 1);
|
||||
block_copy(out, working_dir, working_dir_len);
|
||||
|
@ -43,8 +41,6 @@ system_get_path(Arena* arena, System_Path_Code path_code){
|
|||
}
|
||||
|
||||
result = string_remove_last_folder(SCu8(buf, n));
|
||||
|
||||
LINUX_FN_DEBUG("bin dir = [%.*s]", (int)result.size, result.str);
|
||||
} break;
|
||||
}
|
||||
|
||||
|
@ -104,7 +100,7 @@ system_get_canonical(Arena* arena, String_Const_u8 name){
|
|||
|
||||
internal File_List
|
||||
system_get_file_list(Arena* arena, String_Const_u8 directory){
|
||||
LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str);
|
||||
//LINUX_FN_DEBUG("%.*s", (int)directory.size, directory.str);
|
||||
File_List result = {};
|
||||
|
||||
char* path = strndupa((char*)directory.str, directory.size);
|
||||
|
@ -162,7 +158,7 @@ system_get_file_list(Arena* arena, String_Const_u8 directory){
|
|||
|
||||
internal File_Attributes
|
||||
system_quick_file_attributes(Arena* scratch, String_Const_u8 file_name){
|
||||
LINUX_FN_DEBUG("%.*s", (int)file_name.size, file_name.str);
|
||||
//LINUX_FN_DEBUG("%.*s", (int)file_name.size, file_name.str);
|
||||
struct stat file_stat;
|
||||
stat((const char*)file_name.str, &file_stat);
|
||||
return linux_file_attributes_from_struct_stat(&file_stat);
|
||||
|
@ -306,7 +302,7 @@ system_signal_step(u32 code){
|
|||
|
||||
internal void
|
||||
system_sleep(u64 microseconds){
|
||||
LINUX_FN_DEBUG("%" PRIu64, microseconds);
|
||||
//LINUX_FN_DEBUG("%" PRIu64, microseconds);
|
||||
struct timespec requested;
|
||||
struct timespec remaining;
|
||||
u64 seconds = microseconds / Million(1);
|
||||
|
@ -317,7 +313,7 @@ system_sleep(u64 microseconds){
|
|||
|
||||
internal void
|
||||
system_post_clipboard(String_Const_u8 str){
|
||||
LINUX_FN_DEBUG("%.*s", (int)str.size, str.str);
|
||||
//LINUX_FN_DEBUG("%.*s", (int)str.size, str.str);
|
||||
linalloc_clear(linuxvars.clipboard_out_arena);
|
||||
linuxvars.clipboard_out_contents = push_u8_stringf(linuxvars.clipboard_out_arena, "%.*s", str.size, str.str);
|
||||
XSetSelectionOwner(linuxvars.dpy, linuxvars.atom_CLIPBOARD, linuxvars.win, CurrentTime);
|
||||
|
@ -377,7 +373,7 @@ system_cli_call(Arena* scratch, char* path, char* script, CLI_Handles* cli_out){
|
|||
internal void
|
||||
system_cli_begin_update(CLI_Handles* cli){
|
||||
// NOTE(inso): I don't think anything needs to be done here.
|
||||
LINUX_FN_DEBUG();
|
||||
//LINUX_FN_DEBUG();
|
||||
}
|
||||
|
||||
internal b32
|
||||
|
@ -507,7 +503,7 @@ system_thread_get_id(void){
|
|||
|
||||
internal void
|
||||
system_acquire_global_frame_mutex(Thread_Context* tctx){
|
||||
LINUX_FN_DEBUG();
|
||||
//LINUX_FN_DEBUG();
|
||||
if (tctx->kind == ThreadKind_AsyncTasks){
|
||||
system_mutex_acquire(linuxvars.global_frame_mutex);
|
||||
}
|
||||
|
@ -515,7 +511,7 @@ system_acquire_global_frame_mutex(Thread_Context* tctx){
|
|||
|
||||
internal void
|
||||
system_release_global_frame_mutex(Thread_Context* tctx){
|
||||
LINUX_FN_DEBUG();
|
||||
//LINUX_FN_DEBUG();
|
||||
if (tctx->kind == ThreadKind_AsyncTasks){
|
||||
system_mutex_release(linuxvars.global_frame_mutex);
|
||||
}
|
||||
|
@ -599,7 +595,7 @@ system_memory_allocate(u64 size, String_Const_u8 location){
|
|||
void* result = mmap(
|
||||
NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
// TODO(andrew): Allocation tracking?
|
||||
//LINUX_FN_DEBUG("%" PRIu64 ", %.*s %p", size, (int)location.size, location.str, result);
|
||||
LINUX_FN_DEBUG("%" PRIu64 ", %.*s %p", size, (int)location.size, location.str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -672,6 +668,6 @@ system_is_fullscreen(void){
|
|||
|
||||
internal Input_Modifier_Set
|
||||
system_get_keyboard_modifiers(Arena* arena){
|
||||
LINUX_FN_DEBUG();
|
||||
//LINUX_FN_DEBUG();
|
||||
return(copy_modifier_set(arena, &linuxvars.input.pers.modifiers));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue