aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pugl/pugl.h79
-rw-r--r--pugl/pugl_osx.m160
-rw-r--r--pugl/pugl_win.c177
-rw-r--r--pugl/pugl_x11.c67
-rw-r--r--test/pugl_cairo_test.c4
-rw-r--r--test/pugl_test.c4
-rw-r--r--test/test_utils.h17
7 files changed, 288 insertions, 220 deletions
diff --git a/pugl/pugl.h b/pugl/pugl.h
index d3bcc7f..0f36bfe 100644
--- a/pugl/pugl.h
+++ b/pugl/pugl.h
@@ -113,15 +113,6 @@ typedef enum {
} PuglWindowHintValue;
/**
- Convenience symbols for ASCII control characters.
-*/
-typedef enum {
- PUGL_CHAR_BACKSPACE = 0x08,
- PUGL_CHAR_ESCAPE = 0x1B,
- PUGL_CHAR_DELETE = 0x7F
-} PuglChar;
-
-/**
Keyboard modifier flags.
*/
typedef enum {
@@ -132,14 +123,24 @@ typedef enum {
} PuglMod;
/**
- Special (non-Unicode) keyboard keys.
+ Special keyboard keys.
+
+ All keys, special or not, are expressed as a Unicode code point. This
+ enumeration defines constants for special keys that do not have a standard
+ code point, and some convenience constants for control characters.
- The numerical values of these symbols occupy a reserved range of Unicode
- points, so it is possible to express either a PuglKey value or a Unicode
- character in the same variable. This is sometimes useful for interfacing
- with APIs that do not make this distinction.
+ Keys that do not have a standard code point use values in the Private Use
+ Area in the Basic Multilingual Plane (U+E000 to U+F8FF). Applications must
+ take care to not interpret these values beyond key detection, the mapping
+ used here is arbitrary and specific to Pugl.
*/
typedef enum {
+ // ASCII control codes
+ PUGL_KEY_BACKSPACE = 0x08,
+ PUGL_KEY_ESCAPE = 0x1B,
+ PUGL_KEY_DELETE = 0x7F,
+
+ // Unicode Private Use Area
PUGL_KEY_F1 = 0xE000,
PUGL_KEY_F2,
PUGL_KEY_F3,
@@ -179,6 +180,7 @@ typedef enum {
PUGL_CLOSE, /**< Close view */
PUGL_KEY_PRESS, /**< Key press */
PUGL_KEY_RELEASE, /**< Key release */
+ PUGL_TEXT, /**< Character entry */
PUGL_ENTER_NOTIFY, /**< Pointer entered view */
PUGL_LEAVE_NOTIFY, /**< Pointer left view */
PUGL_MOTION_NOTIFY, /**< Pointer motion */
@@ -261,23 +263,14 @@ typedef struct {
/**
Key press/release event.
- Keys that correspond to a Unicode character have `character` and `string`
- set. Other keys will have `character` 0, but `special` may be set if this
- is a known special key.
+ This represents low-level key press and release events. This event type
+ should be used for "raw" keyboard handing (key bindings, for example), but
+ must not be interpreted as text input.
- A key press may be part of a multi-key sequence to generate a wide
- character. If `filter` is set, this event is part of a multi-key sequence
- and should be ignored if the application is reading textual input.
- Following the series of filtered press events, a press event with
- `character` and `string` (but `keycode` 0) will be sent. This event will
- have no corresponding release event.
-
- Generally, an application should either work with raw keyboard press/release
- events based on `keycode` (ignoring events with `keycode` 0), or read
- textual input based on `character` or `string` (ignoring releases and events
- with `filter` 1). Note that blindly appending `string` will yield incorrect
- text, since press events are sent for both individually composed keys and
- the resulting synthetic multi-byte press.
+ Keys are represented as Unicode code points, using the "natural" code point
+ for the key wherever possible (see @ref PuglKey for details). The `key`
+ field will be set to the code for the pressed key, without any modifiers
+ applied (by the shift or control keys).
*/
typedef struct {
PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */
@@ -289,13 +282,30 @@ typedef struct {
double y_root; /**< Root-relative Y coordinate. */
uint32_t state; /**< Bitwise OR of PuglMod flags. */
uint32_t keycode; /**< Raw key code. */
- uint32_t character; /**< Unicode character code, or 0. */
- PuglKey special; /**< Special key, or 0. */
- char string[8]; /**< UTF-8 string. */
- bool filter; /**< True if part of a multi-key sequence. */
+ uint32_t key; /**< Unshifted Unicode character code, or 0. */
} PuglEventKey;
/**
+ Character input event.
+
+ This represents text input, usually as the result of a key press. The text
+ is given both as a Unicode character code and a UTF-8 string.
+*/
+typedef struct {
+ PuglEventType type; /**< PUGL_CHAR. */
+ uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ uint32_t time; /**< Time in milliseconds. */
+ double x; /**< View-relative X coordinate. */
+ double y; /**< View-relative Y coordinate. */
+ double x_root; /**< Root-relative X coordinate. */
+ double y_root; /**< Root-relative Y coordinate. */
+ uint32_t state; /**< Bitwise OR of PuglMod flags. */
+ uint32_t keycode; /**< Raw key code. */
+ uint32_t character; /**< Unicode character code */
+ char string[8]; /**< UTF-8 string. */
+} PuglEventText;
+
+/**
Pointer crossing event (enter and leave).
*/
typedef struct {
@@ -372,6 +382,7 @@ typedef union {
PuglEventExpose expose; /**< PUGL_EXPOSE. */
PuglEventClose close; /**< PUGL_CLOSE. */
PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */
+ PuglEventText text; /**< PUGL_TEXT. */
PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */
PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */
PuglEventScroll scroll; /**< PUGL_SCROLL. */
diff --git a/pugl/pugl_osx.m b/pugl/pugl_osx.m
index 0546eec..6f3d4bc 100644
--- a/pugl/pugl_osx.m
+++ b/pugl/pugl_osx.m
@@ -92,13 +92,14 @@ struct PuglInternalsImpl {
@end
-@interface PuglOpenGLView : NSOpenGLView
+@interface PuglOpenGLView : NSOpenGLView<NSTextInputClient>
{
@public
- PuglView* puglview;
- NSTrackingArea* trackingArea;
- NSTimer* timer;
- NSTimer* urgentTimer;
+ PuglView* puglview;
+ NSTrackingArea* trackingArea;
+ NSMutableAttributedString* markedText;
+ NSTimer* timer;
+ NSTimer* urgentTimer;
}
@end
@@ -421,10 +422,13 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
const NSPoint wloc = [self eventLocation:event];
const NSPoint rloc = [NSEvent mouseLocation];
- const NSString* chars = [event characters];
- const char* str = [chars UTF8String];
- const uint32_t code = puglDecodeUTF8((const uint8_t*)str);
- PuglEventKey ev = {
+ const PuglKey spec = keySymToSpecial(puglview, event);
+ const NSString* chars = [event charactersIgnoringModifiers];
+ const char* str = [[chars lowercaseString] UTF8String];
+ const uint32_t code = (
+ spec ? spec : puglDecodeUTF8((const uint8_t*)str));
+
+ const PuglEventKey ev = {
PUGL_KEY_PRESS,
0,
[event timestamp],
@@ -434,22 +438,27 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(puglview, event),
[event keyCode],
- (code != 0xFFFD) ? code : 0,
- keySymToSpecial(puglview, event),
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false
+ (code != 0xFFFD) ? code : 0
};
- strncpy(ev.string, str, 8);
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
+
+ if (!spec) {
+ [self interpretKeyEvents:@[event]];
+ }
}
- (void) keyUp:(NSEvent*)event
{
const NSPoint wloc = [self eventLocation:event];
const NSPoint rloc = [NSEvent mouseLocation];
- const NSString* chars = [event characters];
- const char* str = [chars UTF8String];
- PuglEventKey ev = {
+ const PuglKey spec = keySymToSpecial(puglview, event);
+ const NSString* chars = [event charactersIgnoringModifiers];
+ const char* str = [[chars lowercaseString] UTF8String];
+ const uint32_t code =
+ (spec ? spec : puglDecodeUTF8((const uint8_t*)str));
+
+ const PuglEventKey ev = {
PUGL_KEY_RELEASE,
0,
[event timestamp],
@@ -459,15 +468,109 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(puglview, event),
[event keyCode],
- puglDecodeUTF8((const uint8_t*)str),
- keySymToSpecial(puglview, event),
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false,
+ (code != 0xFFFD) ? code : 0
};
- strncpy(ev.string, str, 8);
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
+- (BOOL) hasMarkedText
+{
+ return [markedText length] > 0;
+}
+
+- (NSRange) markedRange
+{
+ return (([markedText length] > 0)
+ ? NSMakeRange(0, [markedText length] - 1)
+ : NSMakeRange(NSNotFound, 0));
+}
+
+- (NSRange) selectedRange
+{
+ return NSMakeRange(NSNotFound, 0);
+}
+
+- (void)setMarkedText:(id)string
+ selectedRange:(NSRange)selected
+ replacementRange:(NSRange)replacement
+{
+ [markedText release];
+ markedText = (
+ [string isKindOfClass:[NSAttributedString class]]
+ ? [[NSMutableAttributedString alloc] initWithAttributedString:string]
+ : [[NSMutableAttributedString alloc] initWithString:string]);
+}
+
+- (void) unmarkText
+{
+ [[markedText mutableString] setString:@""];
+}
+
+- (NSArray*) validAttributesForMarkedText
+{
+ return @[];
+}
+
+- (NSAttributedString*)
+ attributedSubstringForProposedRange:(NSRange)range
+ actualRange:(NSRangePointer)actual
+{
+ return nil;
+}
+
+- (NSUInteger) characterIndexForPoint:(NSPoint)point
+{
+ return 0;
+}
+
+- (NSRect) firstRectForCharacterRange:(NSRange)range
+ actualRange:(NSRangePointer)actual
+{
+ const NSRect frame = [(id)puglview bounds];
+ return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0);
+}
+
+- (void) insertText:(id)string
+ replacementRange:(NSRange)replacement
+{
+ NSEvent* const event = [NSApp currentEvent];
+ NSString* const characters =
+ ([string isKindOfClass:[NSAttributedString class]]
+ ? [string string]
+ : (NSString*)string);
+
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ for (size_t i = 0; i < [characters length]; ++i) {
+ const uint32_t code = [characters characterAtIndex:i];
+ char utf8[8] = {0};
+ NSUInteger len = 0;
+
+ [characters getBytes:utf8
+ maxLength:sizeof(utf8)
+ usedLength:&len
+ encoding:NSUTF8StringEncoding
+ options:0
+ range:NSMakeRange(i, i + 1)
+ remainingRange:nil];
+
+ PuglEventText ev = { PUGL_TEXT,
+ 0,
+ [event timestamp],
+ wloc.x,
+ puglview->height - wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(puglview, event),
+ [event keyCode],
+ code,
+ { 0, 0, 0, 0, 0, 0, 0, 0 } };
+
+ memcpy(ev.string, utf8, len);
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
+ }
+}
+
- (void) flagsChanged:(NSEvent*)event
{
const uint32_t mods = getModifiers(puglview, event);
@@ -501,10 +604,7 @@ handleCrossing(PuglOpenGLView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
mods,
[event keyCode],
- 0,
- special,
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- false
+ special
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -648,8 +748,10 @@ puglCreateWindow(PuglView* view, const char* title)
[NSAutoreleasePool new];
impl->app = [NSApplication sharedApplication];
- impl->glview = [PuglOpenGLView alloc];
- impl->glview->puglview = view;
+ impl->glview = [PuglOpenGLView alloc];
+ impl->glview->trackingArea = nil;
+ impl->glview->markedText = [[NSMutableAttributedString alloc] init];
+ impl->glview->puglview = view;
[impl->glview initWithFrame:NSMakeRect(0, 0, view->width, view->height)];
[impl->glview addConstraint:
@@ -706,6 +808,8 @@ puglCreateWindow(PuglView* view, const char* title)
[window makeKeyAndOrderFront:window];
}
+ [impl->glview updateTrackingAreas];
+
return 0;
}
diff --git a/pugl/pugl_win.c b/pugl/pugl_win.c
index c2ccff3..afbae31 100644
--- a/pugl/pugl_win.c
+++ b/pugl/pugl_win.c
@@ -484,32 +484,31 @@ initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam)
event->scroll.dy = 0;
}
+/** Return the code point for buf, or the replacement character on error. */
static uint32_t
-utf16_to_code_point(const wchar_t* input, const int input_size)
-{
- uint32_t code_unit = *input;
- // Equiv. range check between 0xD800 to 0xDBFF inclusive
- if ((code_unit & 0xFC00) == 0xD800) {
- if (input_size < 2) {
- // "Error: is surrogate but input_size too small"
- return 0xFFFD; // replacement character
+puglDecodeUTF16(const wchar_t* buf, const int len)
+{
+ const uint32_t c0 = buf[0];
+ const uint32_t c1 = buf[0];
+ if (c0 >= 0xD800 && c0 < 0xDC00) {
+ if (len < 2) {
+ return 0xFFFD; // Surrogate, but length is only 1
+ } else if (c1 >= 0xDC00 && c1 <= 0xDFFF) {
+ return ((c0 & 0x03FF) << 10) + (c1 & 0x03FF) + 0x10000;
}
- uint32_t code_unit_2 = *++input;
- // Equiv. range check between 0xDC00 to 0xDFFF inclusive
- if ((code_unit_2 & 0xFC00) == 0xDC00) {
- return (code_unit << 10) + code_unit_2 - 0x35FDC00;
- }
-
- // TODO: push_back(code_unit_2);
- // "Error: Unpaired surrogates."
- return 0xFFFD; // replacement character
+ return 0xFFFD; // Unpaired surrogates
}
- return code_unit;
+
+ return c0;
}
static void
-initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam)
+initKeyEvent(PuglEventKey* event,
+ PuglView* view,
+ bool press,
+ WPARAM wParam,
+ LPARAM lParam)
{
POINT rpos = { 0, 0 };
GetCursorPos(&rpos);
@@ -517,92 +516,46 @@ initKeyEvent(PuglEvent* event, PuglView* view, bool press, LPARAM lParam)
POINT cpos = { rpos.x, rpos.y };
ScreenToClient(view->impl->hwnd, &rpos);
- event->key.type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
- event->key.time = GetMessageTime() / 1e3;
- event->key.state = getModifiers();
- event->key.x_root = rpos.x;
- event->key.y_root = rpos.y;
- event->key.x = cpos.x;
- event->key.y = cpos.y;
- event->key.keycode = (uint32_t)((lParam & 0xFF0000) >> 16);
- event->key.character = 0;
- event->key.special = (PuglKey)0;
- event->key.filter = 0;
-}
-
-static void
-wcharBufToEvent(wchar_t* buf, int n, PuglEvent* event)
-{
- if (n > 0) {
- char* charp = (char*)event->key.string;
- if (!WideCharToMultiByte(CP_UTF8, 0, buf, n,
- charp, 8, NULL, NULL)) {
- /* error: could not convert to utf-8,
- GetLastError has details */
- memset(event->key.string, 0, 8);
- // replacement character
- event->key.string[0] = 0xEF;
- event->key.string[1] = 0xBF;
- event->key.string[2] = 0xBD;
- }
-
- event->key.character = utf16_to_code_point(buf, n);
- } else {
- // replacement character
- event->key.string[0] = 0xEF;
- event->key.string[1] = 0xBF;
- event->key.string[2] = 0xBD;
- event->key.character = 0xFFFD;
+ const unsigned vkey = (unsigned)wParam;
+ const unsigned vcode = MapVirtualKey(vkey, MAPVK_VK_TO_VSC);
+ const unsigned kchar = MapVirtualKey(vkey, MAPVK_VK_TO_CHAR);
+ const bool dead = kchar >> (sizeof(UINT) * 8 - 1) & 1;
+
+ event->type = press ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
+ event->time = GetMessageTime() / 1e3;
+ event->state = getModifiers();
+ event->x_root = rpos.x;
+ event->y_root = rpos.y;
+ event->x = cpos.x;
+ event->y = cpos.y;
+ event->keycode = (uint32_t)((lParam & 0xFF0000) >> 16);
+ event->key = 0;
+
+ const PuglKey special = keySymToSpecial(vkey);
+ if (special) {
+ event->key = special;
+ } else if (!dead) {
+ // Translate unshifted key
+ BYTE keyboardState[256] = {0};
+ wchar_t buf[5] = {0};
+ const int ulen = ToUnicode(vkey, vcode, keyboardState, buf, 4, 1<<2);
+ event->key = puglDecodeUTF16(buf, ulen);
}
}
static void
-translateMessageParamsToEvent(LPARAM lParam, WPARAM wParam, PuglEvent* event)
-{
- (void)lParam;
-
- /* TODO: This is a kludge. Would be nice to use ToUnicode here, but this
- breaks composed keys because it messes with the keyboard state. Not
- sure how to correctly handle this on Windows. */
-
- // This is how I really want to do this, but it breaks composed keys (é,
- // è, ü, ö, and so on) because ToUnicode messes with the keyboard state.
-
- //wchar_t buf[5];
- //BYTE keyboard_state[256];
- //int wcharCount = 0;
- //GetKeyboardState(keyboard_state);
- //wcharCount = ToUnicode(wParam, MapVirtualKey(wParam, MAPVK_VK_TO_VSC),
- // keyboard_state, buf, 4, 0);
- //wcharBufToEvent(buf, wcharCount, event);
-
- // So, since Google refuses to give me a better solution, and if no one
- // else has a better solution, I will make a hack...
- wchar_t buf[5] = { 0, 0, 0, 0, 0 };
- WPARAM c = MapVirtualKey((unsigned)wParam, MAPVK_VK_TO_CHAR);
- buf[0] = c & 0xffff;
- // TODO: This does not take caps lock into account
- // TODO: Dead keys should affect key releases as well
- if (!(event->key.state && PUGL_MOD_SHIFT))
- buf[0] = towlower(buf[0]);
- wcharBufToEvent(buf, 1, event);
- event->key.filter = ((c >> 31) & 0x1);
-}
+initCharEvent(PuglEvent* event, PuglView* view, WPARAM wParam, LPARAM lParam)
+{
+ const wchar_t utf16[2] = { wParam & 0xFFFF, (wParam >> 16) & 0xFFFF };
-static void
-translateCharEventToEvent(WPARAM wParam, PuglEvent* event)
-{
- wchar_t buf[2];
- int wcharCount;
- if (wParam & 0xFFFF0000) {
- wcharCount = 2;
- buf[0] = (wParam & 0xFFFF);
- buf[1] = ((wParam >> 16) & 0xFFFF);
- } else {
- wcharCount = 1;
- buf[0] = (wParam & 0xFFFF);
+ initKeyEvent(&event->key, view, true, wParam, lParam);
+ event->type = PUGL_TEXT;
+ event->text.character = puglDecodeUTF16(utf16, 2);
+
+ if (!WideCharToMultiByte(
+ CP_UTF8, 0, utf16, 2, event->text.string, 8, NULL, NULL)) {
+ memset(event->text.string, 0, 8);
}
- wcharBufToEvent(buf, wcharCount, event);
}
static bool
@@ -780,30 +733,14 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_KEYDOWN:
if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- if (!(event.key.special = keySymToSpecial(wParam))) {
- event.key.type = PUGL_NOTHING;
- }
- }
- break;
- case WM_CHAR:
- if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- translateCharEventToEvent(wParam, &event);
- }
- break;
- case WM_DEADCHAR:
- if (!ignoreKeyEvent(view, lParam)) {
- initKeyEvent(&event, view, true, lParam);
- translateCharEventToEvent(wParam, &event);
- event.key.filter = 1;
+ initKeyEvent(&event.key, view, true, wParam, lParam);
}
break;
case WM_KEYUP:
- initKeyEvent(&event, view, false, lParam);
- if (!(event.key.special = keySymToSpecial(wParam))) {
- translateMessageParamsToEvent(lParam, wParam, &event);
- }
+ initKeyEvent(&event.key, view, false, wParam, lParam);
+ break;
+ case WM_CHAR:
+ initCharEvent(&event, view, wParam, lParam);
break;
case WM_SETFOCUS:
stopFlashing(view);
diff --git a/pugl/pugl_x11.c b/pugl/pugl_x11.c
index d5e51b4..f10902f 100644
--- a/pugl/pugl_x11.c
+++ b/pugl/pugl_x11.c
@@ -253,34 +253,55 @@ keySymToSpecial(KeySym sym)
return (PuglKey)0;
}
-static void
-translateKey(PuglView* view, XEvent* xevent, PuglEventKey* event)
+static int
+lookupString(XIC xic, XEvent* xevent, char* str, KeySym* sym)
{
- KeySym sym = 0;
- memset(event->string, 0, 8);
- event->filter = XFilterEvent(xevent, None);
- if (xevent->type == KeyRelease || event->filter || !view->impl->xic) {
- if (XLookupString(&xevent->xkey, event->string, 7, &sym, NULL) == 1) {
- event->character = (uint8_t)event->string[0];
- }
- } else {
- /* TODO: Not sure about this. On my system, some characters work with
- Xutf8LookupString but not with XmbLookupString, and some are the
- opposite. */
- Status status = 0;
+ Status status = 0;
+
#ifdef X_HAVE_UTF8_STRING
- const int n = Xutf8LookupString(
- view->impl->xic, &xevent->xkey, event->string, 7, &sym, &status);
+ const int n = Xutf8LookupString(xic, &xevent->xkey, str, 7, sym, &status);
#else
- const int n = XmbLookupString(
- view->impl->xic, &xevent->xkey, event->string, 7, &sym, &status);
+ const int n = XmbLookupString(xic, &xevent->xkey, str, 7, sym, &status);
#endif
- if (n > 0) {
- event->character = puglDecodeUTF8((const uint8_t*)event->string);
+
+ return status == XBufferOverflow ? 0 : n;
+}
+
+static void
+translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
+{
+ const unsigned state = xevent->xkey.state;
+ const bool filter = XFilterEvent(xevent, None);
+
+ event->key.keycode = xevent->xkey.keycode;
+ xevent->xkey.state = 0;
+
+ // Lookup unshifted key
+ char ustr[8] = {0};
+ KeySym sym = 0;
+ const int ufound = XLookupString(&xevent->xkey, ustr, 8, &sym, NULL);
+ const PuglKey special = keySymToSpecial(sym);
+
+ event->key.key = ((special || ufound <= 0)
+ ? special
+ : puglDecodeUTF8((const uint8_t*)ustr));
+
+ if (xevent->type == KeyPress && !filter && !special) {
+ // Lookup shifted key for possible text event
+ xevent->xkey.state = state;
+
+ char sstr[8] = {0};
+ const int sfound = lookupString(view->impl->xic, xevent, sstr, &sym);
+ if (sfound > 0) {
+ // Dispatch key event now
+ puglDispatchEvent(view, event);
+
+ // "Return" a text event in its place
+ event->text.type = PUGL_TEXT;
+ event->text.character = puglDecodeUTF8((const uint8_t*)sstr);
+ memcpy(event->text.string, sstr, sizeof(sstr));
}
}
- event->special = keySymToSpecial(sym);
- event->keycode = xevent->xkey.keycode;
}
static uint32_t
@@ -387,7 +408,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.key.x_root = xevent.xkey.x_root;
event.key.y_root = xevent.xkey.y_root;
event.key.state = translateModifiers(xevent.xkey.state);
- translateKey(view, &xevent, &event.key);
+ translateKey(view, &xevent, &event);
break;
case EnterNotify:
case LeaveNotify:
diff --git a/test/pugl_cairo_test.c b/test/pugl_cairo_test.c
index 524e8b0..47907db 100644
--- a/test/pugl_cairo_test.c
+++ b/test/pugl_cairo_test.c
@@ -129,9 +129,7 @@ onEvent(PuglView* view, const PuglEvent* event)
{
switch (event->type) {
case PUGL_KEY_PRESS:
- if (event->key.character == 'q' ||
- event->key.character == 'Q' ||
- event->key.character == PUGL_CHAR_ESCAPE) {
+ if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
quit = 1;
}
break;
diff --git a/test/pugl_test.c b/test/pugl_test.c
index fdf78c1..a609027 100644
--- a/test/pugl_test.c
+++ b/test/pugl_test.c
@@ -103,9 +103,7 @@ onEvent(PuglView* view, const PuglEvent* event)
quit = 1;
break;
case PUGL_KEY_PRESS:
- if (event->key.character == 'q' ||
- event->key.character == 'Q' ||
- event->key.character == PUGL_CHAR_ESCAPE) {
+ if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
quit = 1;
}
break;
diff --git a/test/test_utils.h b/test/test_utils.h
index 285cc83..fb04f4b 100644
--- a/test/test_utils.h
+++ b/test/test_utils.h
@@ -101,16 +101,15 @@ printEvent(const PuglEvent* event, const char* prefix)
{
switch (event->type) {
case PUGL_KEY_PRESS:
- return fprintf(stderr, "%sKey %u (char U+%04X special U+%04X) press (%s)%s\n",
- prefix,
- event->key.keycode, event->key.character, event->key.special,
- event->key.string, event->key.filter ? " (filtered)" : "");
-
+ return fprintf(stderr, "%sKey press code %3u key U+%04X\n",
+ prefix, event->key.keycode, event->key.key);
case PUGL_KEY_RELEASE:
- return fprintf(stderr, "%sKey %u (char U+%04X special U+%04X) release (%s)%s\n",
- prefix,
- event->key.keycode, event->key.character, event->key.special,
- event->key.string, event->key.filter ? " (filtered)" : "");
+ return fprintf(stderr, "%sKey release code %3u key U+%04X\n",
+ prefix, event->key.keycode, event->key.key);
+ case PUGL_TEXT:
+ return fprintf(stderr, "%sText entry code %3u char U+%04X (%s)\n",
+ prefix, event->text.keycode,
+ event->text.character, event->text.string);
case PUGL_BUTTON_PRESS:
case PUGL_BUTTON_RELEASE:
return (fprintf(stderr, "%sMouse %d %s at %f,%f ",