Skip to content

Commit

Permalink
Enable deadkey handling on MacOS.
Browse files Browse the repository at this point in the history
We replace the old, somewhat flaky direct reads from NSEvent with the
use of UCKeyTranslate.

It is still not perfect, so we do a bunch fo hacks to get the various
keys repeating and emitting the correct CHAR events.
  • Loading branch information
SiegeLordEx authored and SiegeLord committed Nov 18, 2024
1 parent 28b4b86 commit ffe6dc9
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 31 deletions.
1 change: 1 addition & 0 deletions allegro5.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,4 @@ osx_tell_dock_outside_bundle = true
# To restore behavior of older code versions, specify this value to the
# Allegro version that had the desired old behavior.
# joystick_version = 5.2.9
# keyboard_version = 5.2.9
1 change: 1 addition & 0 deletions include/allegro5/internal/aintern_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ AL_FUNC(void *, _al_open_library, (const char *filename));
AL_FUNC(void *, _al_import_symbol, (void *library, const char *symbol));
AL_FUNC(void, _al_close_library, (void *library));
AL_FUNC(uint32_t, _al_get_joystick_compat_version, (void));
AL_FUNC(uint32_t, _al_get_keyboard_compat_version, (void));

#ifdef __cplusplus
}
Expand Down
16 changes: 0 additions & 16 deletions src/joynu.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,22 +302,6 @@ void al_get_joystick_state(ALLEGRO_JOYSTICK *joy, ALLEGRO_JOYSTICK_STATE *ret_st
new_joystick_driver->get_joystick_state(joy, ret_state);
}



uint32_t _al_get_joystick_compat_version(void)
{
ALLEGRO_CONFIG *system_config = al_get_system_config();
const char* compat_version = al_get_config_value(system_config, "compatibility", "joystick_version");
if (!compat_version || strlen(compat_version) == 0)
return al_get_allegro_version();
int version = 0;
int sub_version = 0;
int wip_version = 0;
/* Ignore the release number, we don't expect that to make a difference */
sscanf(compat_version, "%2d.%2d.%2d", &version, &sub_version, &wip_version);
return AL_ID(version, sub_version, wip_version, 0);
}

/*
* Local Variables:
* c-basic-offset: 3
Expand Down
83 changes: 68 additions & 15 deletions src/macosx/keybd.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "allegro5/internal/aintern_keyboard.h"
#include "allegro5/platform/aintosx.h"

#import <Carbon/Carbon.h>

#ifndef ALLEGRO_MACOSX
#error Something is wrong with the makefile
#endif
Expand All @@ -49,6 +51,7 @@
static ALLEGRO_KEYBOARD* osx_get_keyboard(void);
static ALLEGRO_KEYBOARD keyboard;
static ALLEGRO_KEYBOARD_STATE kbdstate;
static UInt32 dead_key_state;



Expand Down Expand Up @@ -105,10 +108,8 @@ static void _handle_key_press(ALLEGRO_DISPLAY* dpy, int unicode, int scancode,
if (!is_repeat) {
_al_event_source_emit_event(&keyboard.es, &event);
}
if (unicode > 0) {
/* Apple maps function, arrow, and other keys to Unicode points. */
/* http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT */
if (unicode >= 0xF700 && unicode <= 0xF747) {
if (unicode != 0) {
if (unicode < 0) {
unicode = 0;
}
event.keyboard.type = ALLEGRO_EVENT_KEY_CHAR;
Expand Down Expand Up @@ -266,17 +267,69 @@ void _al_osx_keyboard_handler(int pressed, NSEvent *event, ALLEGRO_DISPLAY* dpy)
/* Translate OS X modifier flags to Allegro modifier flags */
int key_shifts = translate_modifier_flags([event modifierFlags]);

if (pressed) {
NSString* raw_characters = [event charactersIgnoringModifiers];
NSString* upper_characters = [event characters];
const unichar raw_character = ([raw_characters length] > 0) ? [raw_characters characterAtIndex: 0] : 0;
const unichar upper_character =([upper_characters length] > 0) ? [upper_characters characterAtIndex: 0] : 0;
bool is_repeat = pressed ? ([event isARepeat] == YES) : false;
if (pressed) {
int32_t unichar = 0;
bool new_input = _al_get_keyboard_compat_version() >= AL_ID(5, 2, 10, 0);
NSString *raw_characters = [event charactersIgnoringModifiers];
NSString *characters = [event characters];
UniChar raw_character = ([raw_characters length] > 0) ? [raw_characters characterAtIndex: 0] : 0;
UniChar character = ([characters length] > 0) ? [characters characterAtIndex: 0] : 0;

if (new_input) {
/* https://stackoverflow.com/a/22677690 */
TISInputSourceRef keyboard_input = TISCopyCurrentKeyboardInputSource();
CFDataRef layout_data = TISGetInputSourceProperty(keyboard_input, kTISPropertyUnicodeKeyLayoutData);
const UCKeyboardLayout *layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data);

CGEventFlags modifier_flags = [event modifierFlags];
UInt32 modifier_key_state = (modifier_flags >> 16) & 0xff;

UniChar unicode_string[5];
unicode_string[4] = 0;
UniCharCount unicode_length;

UCKeyTranslate(layout,
[event keyCode],
kUCKeyActionDown,
modifier_key_state,
LMGetKbdType(),
0,
&dead_key_state,
4,
&unicode_length,
unicode_string);

if (unicode_length > 0) {
ALLEGRO_USTR *ustr = al_ustr_new_from_utf16(unicode_string);
unichar = al_ustr_get(ustr, 0);
if (unichar < 0)
unichar = 0;
/* For some reason, pad enter sends a ^C. */
if (scancode == ALLEGRO_KEY_PAD_ENTER && unichar == 3)
unichar = '\r';
/* Single out the few printable characters under 32 */
if (unichar < ' ' && (unichar != '\r' && unichar != '\t' && unichar != '\b'))
unichar = 0;
al_ustr_free(ustr);
}
CFRelease(keyboard_input);
}
else
unichar = character;

/* Apple maps function, arrow, and other keys to Unicode points.
We want to generate CHAR events for them, so we'll override the translation logic.
_handle_key_press will set the unichar back to 0 for these keys. */
if (character >= 0xF700 && character <= 0xF747)
unichar = -1;
/* The delete key. */
if (character == 0xF728 && new_input)
unichar = 127;
/* Special processing to send character 1 for CTRL-A, 2 for CTRL-B etc. */
if ((key_shifts & ALLEGRO_KEYMOD_CTRL) && (isalpha(raw_character)))
_handle_key_press(dpy, tolower(raw_character) - 'a' + 1, scancode, key_shifts, is_repeat);
else
_handle_key_press(dpy, upper_character, scancode, key_shifts, is_repeat);
unichar = tolower(raw_character) - 'a' + 1;
bool is_repeat = pressed ? ([event isARepeat] == YES) : false;
_handle_key_press(dpy, unichar, scancode, key_shifts, is_repeat);
}
else {
_handle_key_release(dpy, key_shifts, scancode);
Expand All @@ -301,15 +354,15 @@ void _al_osx_keyboard_modifiers(unsigned int modifiers, ALLEGRO_DISPLAY* dpy)
changed = (modifiers ^ old_modifiers) & mod_info[i][0];
if (changed) {
if (modifiers & mod_info[i][0]) {
_handle_key_press(dpy, -1, mod_info[i][2], key_shifts, false);
_handle_key_press(dpy, 0, mod_info[i][2], key_shifts, false);
if (i == 0) {
/* Caps lock requires special handling */
_handle_key_release(dpy, key_shifts, mod_info[0][2]);
}
}
else {
if (i == 0) {
_handle_key_press(dpy, -1, mod_info[0][2], key_shifts, false);
_handle_key_press(dpy, 0, mod_info[0][2], key_shifts, false);
}

_handle_key_release(dpy, key_shifts, mod_info[i][2]);
Expand Down
26 changes: 26 additions & 0 deletions src/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,4 +531,30 @@ void al_init_timeout(ALLEGRO_TIMEOUT *timeout, double seconds)
active_sysdrv->vt->init_timeout(timeout, seconds);
}


static uint32_t get_joystick_compat_version(const char* key)
{
ALLEGRO_CONFIG *system_config = al_get_system_config();
const char* compat_version = al_get_config_value(system_config, "compatibility", key);
if (!compat_version || strlen(compat_version) == 0)
return al_get_allegro_version();
int version = 0;
int sub_version = 0;
int wip_version = 0;
/* Ignore the release number, we don't expect that to make a difference */
sscanf(compat_version, "%2d.%2d.%2d", &version, &sub_version, &wip_version);
return AL_ID(version, sub_version, wip_version, 0);
}


uint32_t _al_get_joystick_compat_version(void)
{
return get_joystick_compat_version("joystick_version");
}

uint32_t _al_get_keyboard_compat_version(void)
{
return get_joystick_compat_version("keyboard_version");
}

/* vim: set sts=3 sw=3 et: */

0 comments on commit ffe6dc9

Please sign in to comment.