parent
f383f7721c
commit
779b56276c
1
.gitignore
vendored
1
.gitignore
vendored
@ -81,4 +81,3 @@ tests/timeout
|
||||
tests/title
|
||||
tests/vulkan
|
||||
tests/windows
|
||||
|
||||
|
@ -226,6 +226,7 @@ endif()
|
||||
if (_GLFW_WIN32)
|
||||
|
||||
list(APPEND glfw_PKG_LIBS "-lgdi32")
|
||||
list(APPEND glfw_LIBRARIES "imm32")
|
||||
|
||||
if (GLFW_USE_HYBRID_HPG)
|
||||
set(_GLFW_USE_HYBRID_HPG 1)
|
||||
|
@ -206,6 +206,96 @@ void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods)
|
||||
}
|
||||
@endcode
|
||||
|
||||
@subsection preedit IME Support
|
||||
|
||||
All desktop operating systems support IME (Input Method Editor) to input characters
|
||||
that are not mapped with physical keys. IME have been popular among Eeastern Asian people.
|
||||
And some operating systems start supporting voice input via IME mechanism.
|
||||
|
||||
GLFW provides IME support functions to help
|
||||
you implement better text input features. You should add suitable visualization code for
|
||||
preedit text.
|
||||
|
||||
IME works in front of actual character input events (@ref input_char).
|
||||
If your application uses text input and you want to support IME,
|
||||
you should register preedit callback to receive preedit text before committed.
|
||||
|
||||
@code
|
||||
glfwSetPreeditCallback(window, preedit_callback);
|
||||
@endcode
|
||||
|
||||
The callback function receives chunk of text and focused block information.
|
||||
|
||||
@code
|
||||
static void preedit_callback(GLFWwindow* window, int strLength, unsigned int* string, int blockLength, int* blocks, int focusedBlock) {
|
||||
}
|
||||
@endcode
|
||||
|
||||
strLength and string parameter reprsent whole preedit text. Each character of the preedit string is a codepoint like @ref input_char.
|
||||
|
||||
If you want to type the text "寿司(sushi)", Usually the callback is called several times like the following sequence:
|
||||
|
||||
-# key event: s
|
||||
-# preedit: [string: "s", block: [1], focusedBlock: 0]
|
||||
-# key event: u
|
||||
-# preedit: [string: "す", block: [1], focusedBlock: 0]
|
||||
-# key event: s
|
||||
-# preedit: [string: "すs", block: [2], focusedBlock: 0]
|
||||
-# key event: h
|
||||
-# preedit: [string: "すsh", block: [2], focusedBlock: 0]
|
||||
-# key event: i
|
||||
-# preedit: [string: "すし", block: [2], focusedBlock: 0]
|
||||
-# key event: ' '
|
||||
-# preedit: [string: "寿司", block: [2], focusedBlock: 0]
|
||||
-# char: '寿'
|
||||
-# char: '司'
|
||||
-# preedit: [string: "", block: [], focusedBlock: 0]
|
||||
|
||||
If preedit text includes several semantic blocks, preedit callbacks returns several blocks after a space key pressed:
|
||||
|
||||
-# preedit: [string: "わたしはすしをたべます", block: [11], focusedBlock: 0]
|
||||
-# preedit: [string: "私は寿司を食べます", block: [2, 7], focusedBlock: 1]
|
||||
|
||||
"blocks" is a list of block length. The above case, it contains the following blocks and second block is focused.
|
||||
|
||||
- 私は
|
||||
- [寿司を食べます]
|
||||
|
||||
commited text(passed via regular @ref input_char event), unfocused block, focused block should have different text style.
|
||||
|
||||
|
||||
GLFW provides helper function to teach suitable position of the candidate window to window system.
|
||||
Window system decides the best position from text cursor geometry (x, y coords and height). You should call this function
|
||||
in the above preedit text callback function.
|
||||
|
||||
@code
|
||||
glfwSetPreeditCursorPos(window, x, y, h);
|
||||
glfwGetPreeditCursorPos(window, &x, &y, &h);
|
||||
@endcode
|
||||
|
||||
Sometimes IME task is interrupted by user or application. There are several functions to support these situation.
|
||||
You can receive notification about IME status change(on/off) by using the following function:
|
||||
|
||||
@code
|
||||
glfwSetIMEStatusCallback(window, imestatus_callback);
|
||||
@endcode
|
||||
|
||||
imestatus_callback has simple sigunature like this:
|
||||
|
||||
@code
|
||||
static void imestatus_callback(GLFWwindow* window) {
|
||||
}
|
||||
@endcode
|
||||
|
||||
You can implement the code that resets or commits preedit text when IME status is changed and preedit text is not empty.
|
||||
|
||||
When the focus is gone from text box, you can use the following functions to reset IME status:
|
||||
|
||||
@code
|
||||
void glfwResetPreeditText(GLFWwindow* window);
|
||||
void glfwSetIMEStatus(GLFWwindow* window, int active)
|
||||
int glfwGetIMEStatus(GLFWwindow* window)
|
||||
@endcode
|
||||
|
||||
@subsection input_key_name Key names
|
||||
|
||||
|
@ -686,6 +686,7 @@ extern "C" {
|
||||
#define GLFW_CURSOR 0x00033001
|
||||
#define GLFW_STICKY_KEYS 0x00033002
|
||||
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
|
||||
#define GLFW_IME 0x00033004
|
||||
|
||||
#define GLFW_CURSOR_NORMAL 0x00034001
|
||||
#define GLFW_CURSOR_HIDDEN 0x00034002
|
||||
@ -1100,6 +1101,37 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
||||
*/
|
||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int);
|
||||
|
||||
/*! @brief The function signature for preedit callbacks.
|
||||
*
|
||||
* This is the function signature for preedit callback functions.
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] length Preedit string length.
|
||||
* @param[in] string Preedit string.
|
||||
* @param[in] count Attributed block count.
|
||||
* @param[in] blocksizes List of attributed block size.
|
||||
* @param[in] focusedblock Focused block index.
|
||||
*
|
||||
* @sa @ref preedit
|
||||
* @sa glfwSetPreeditCallback
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef void (* GLFWpreeditfun)(GLFWwindow*,int,unsigned int*,int,int*,int);
|
||||
|
||||
/*! @brief The function signature for IME status change callbacks.
|
||||
*
|
||||
* This is the function signature for IME status change callback functions.
|
||||
*
|
||||
* @param[in] window The window that received the event.
|
||||
*
|
||||
* @sa @ref preedit
|
||||
* @sa glfwSetIMEStatusCallback
|
||||
*
|
||||
* @ingroup monitor
|
||||
*/
|
||||
typedef void (* GLFWimestatusfun)(GLFWwindow*);
|
||||
|
||||
/*! @brief The function signature for file drop callbacks.
|
||||
*
|
||||
* This is the function signature for file drop callbacks.
|
||||
@ -3009,12 +3041,12 @@ GLFWAPI void glfwPostEmptyEvent(void);
|
||||
/*! @brief Returns the value of an input option for the specified window.
|
||||
*
|
||||
* This function returns the value of an input option for the specified window.
|
||||
* The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
||||
* The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||
*
|
||||
* @param[in] window The window to query.
|
||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_INVALID_ENUM.
|
||||
@ -3032,8 +3064,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
||||
/*! @brief Sets an input option for the specified window.
|
||||
*
|
||||
* This function sets an input mode option for the specified window. The mode
|
||||
* must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
||||
* must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||
*
|
||||
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
|
||||
* modes:
|
||||
@ -3059,9 +3091,12 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
||||
* you are only interested in whether mouse buttons have been pressed but not
|
||||
* when or in which order.
|
||||
*
|
||||
* If the mode is `GLFW_IME`, the value must be either `GLFW_TRUE` to turn on IME,
|
||||
* or `GLFW_FALSE` to turn off it.
|
||||
*
|
||||
* @param[in] window The window whose input mode to set.
|
||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`
|
||||
* @param[in] value The new value of the specified input mode.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
|
||||
@ -3422,6 +3457,67 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
|
||||
*/
|
||||
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
||||
|
||||
/*! @brief Retrieves the position of the text cursor relative to the client area of window.
|
||||
*
|
||||
* This function returns position hint to decide the candidate window.
|
||||
*
|
||||
* @param[in] window The window to set the text cursor for.
|
||||
* @param[out] x The text cursor x position (relative position from window coordinates).
|
||||
* @param[out] y The text cursor y position (relative position from window coordinates).
|
||||
* @param[out] h The text cursor height.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref input_char
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* window, int *x, int *y, int *h);
|
||||
|
||||
/*! @brief Notify the text cursor position to window system to decide the candidate window position.
|
||||
*
|
||||
* This function teach position hint to decide the candidate window. The candidate window
|
||||
* is a part of IME(Input Method Editor) and show several candidate strings.
|
||||
*
|
||||
* Windows sytems decide proper pisition from text cursor geometry.
|
||||
* You should call this function in preedit callback.
|
||||
*
|
||||
* @param[in] window The window to set the text cursor for.
|
||||
* @param[in] x The text cursor x position (relative position from window coordinates).
|
||||
* @param[in] y The text cursor y position (relative position from window coordinates).
|
||||
* @param[in] h The text cursor height.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref input_char
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* window, int x, int y, int h);
|
||||
|
||||
/*! @brief Reset IME input status.
|
||||
*
|
||||
* This function resets IME's preedit text.
|
||||
*
|
||||
* @param[in] window The window.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref preedit
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI void glfwResetPreeditText(GLFWwindow* window);
|
||||
|
||||
/*! @brief Sets the key callback.
|
||||
*
|
||||
* This function sets the key callback of the specified window, which is called
|
||||
@ -3536,6 +3632,58 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun);
|
||||
*/
|
||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun);
|
||||
|
||||
/*! @brief Sets the preedit callback.
|
||||
*
|
||||
* This function sets the preedit callback of the specified
|
||||
* window, which is called when an IME is processing text before commited.
|
||||
*
|
||||
* Callback receives relative position of input cursor inside preedit text and
|
||||
* attributed text blocks. This callback is used for on-the-spot text editing
|
||||
* with IME.
|
||||
*
|
||||
* @param[in] window The window whose callback to set.
|
||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
||||
* callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
||||
* error occurred.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref input_char
|
||||
*
|
||||
* @since Added in GLFW 3.X
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* window, GLFWpreeditfun cbfun);
|
||||
|
||||
/*! @brief Sets the IME status change callback.
|
||||
*
|
||||
* This function sets the preedit callback of the specified
|
||||
* window, which is called when an IME is processing text before commited.
|
||||
*
|
||||
* Callback receives relative position of input cursor inside preedit text and
|
||||
* attributed text blocks. This callback is used for on-the-spot text editing
|
||||
* with IME.
|
||||
*
|
||||
* @param[in] window The window whose callback to set.
|
||||
* @param[in] cbfun The new callback, or `NULL` to remove the currently set
|
||||
* callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or an
|
||||
* error occurred.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref input_char
|
||||
*
|
||||
* @since Added in GLFW 3.X
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* window, GLFWimestatusfun cbfun);
|
||||
|
||||
/*! @brief Sets the mouse button callback.
|
||||
*
|
||||
* This function sets the mouse button callback of the specified window, which
|
||||
|
@ -316,8 +316,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||
}
|
||||
|
||||
@end
|
||||
- (void)imeStatusChangeNotified:(NSNotification *)notification {
|
||||
_glfwInputIMEStatus(window);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Delegate for application related notifications
|
||||
@ -714,6 +717,56 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
[markedText initWithAttributedString:string];
|
||||
else
|
||||
[markedText initWithString:string];
|
||||
NSString* markedTextString = markedText.string;
|
||||
|
||||
NSUInteger i, length = [markedTextString length];
|
||||
int ctext = window->ctext;
|
||||
while (ctext < length+1) {
|
||||
ctext = (ctext == 0) ? 1 : ctext*2;
|
||||
}
|
||||
if (ctext != window->ctext) {
|
||||
unsigned int* preeditText = realloc(window->preeditText, sizeof(unsigned int)*ctext);
|
||||
if (preeditText == NULL) {
|
||||
return;
|
||||
}
|
||||
window->preeditText = preeditText;
|
||||
window->ctext = ctext;
|
||||
}
|
||||
window->ntext = length;
|
||||
window->preeditText[length] = 0;
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const unichar codepoint = [markedTextString characterAtIndex:i];
|
||||
window->preeditText[i] = codepoint;
|
||||
}
|
||||
int focusedBlock = 0;
|
||||
NSInteger offset = 0;
|
||||
window->nblocks = 0;
|
||||
while (offset < length) {
|
||||
NSRange effectiveRange;
|
||||
NSDictionary *attributes = [markedText attributesAtIndex:offset effectiveRange:&effectiveRange];
|
||||
|
||||
if (window->nblocks == window->cblocks) {
|
||||
int cblocks = window->cblocks * 2;
|
||||
int* blocks = realloc(window->preeditAttributeBlocks, sizeof(int)*cblocks);
|
||||
if (blocks == NULL) {
|
||||
return;
|
||||
}
|
||||
window->preeditAttributeBlocks = blocks;
|
||||
window->cblocks = cblocks;
|
||||
}
|
||||
window->preeditAttributeBlocks[window->nblocks] = effectiveRange.length;
|
||||
offset += effectiveRange.length;
|
||||
if (effectiveRange.length == 0) {
|
||||
break;
|
||||
}
|
||||
NSNumber* underline = (NSNumber*) [attributes objectForKey:@"NSUnderline"];
|
||||
if ([underline intValue] != 1) {
|
||||
focusedBlock = window->nblocks;
|
||||
}
|
||||
window->nblocks++;
|
||||
}
|
||||
_glfwInputPreedit(window, focusedBlock);
|
||||
}
|
||||
|
||||
- (void)unmarkText
|
||||
@ -742,8 +795,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
||||
{
|
||||
int xpos, ypos;
|
||||
_glfwPlatformGetWindowPos(window, &xpos, &ypos);
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0);
|
||||
return NSMakeRect(xpos + window->preeditCursorPosX, transformY(ypos + window->preeditCursorPosY), 0.0, window->preeditCursorHeight);
|
||||
}
|
||||
|
||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||
@ -1049,6 +1101,11 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||
[window->ns.object setAcceptsMouseMovedEvents:YES];
|
||||
[window->ns.object setRestorable:NO];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver: window->ns.delegate
|
||||
selector:@selector(imeStatusChangeNotified:)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object: nil];
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
||||
@ -1102,6 +1159,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
if (_glfw.ns.disabledCursorWindow == window)
|
||||
_glfw.ns.disabledCursorWindow = NULL;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: window->ns.delegate];
|
||||
|
||||
[window->ns.object orderOut:nil];
|
||||
|
||||
if (window->monitor)
|
||||
@ -1735,6 +1794,49 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
#endif
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
NSTextInputContext *context = [NSTextInputContext currentInputContext];
|
||||
[context discardMarkedText];
|
||||
[window->ns.view unmarkText];
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
// Mac OS has several input sources.
|
||||
// this code assumes input methods not in ascii capable inputs using IME.
|
||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
||||
if (active) {
|
||||
NSArray* allInputSources = CFBridgingRelease(TISCreateInputSourceList(NULL, false));
|
||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
||||
int i;
|
||||
int count = [allInputSources count];
|
||||
for (i = 0; i < count; i++) {
|
||||
TISInputSourceRef source = (__bridge TISInputSourceRef)([allInputSources objectAtIndex: i]);
|
||||
NSString* sourceID = (__bridge NSString *)(TISGetInputSourceProperty(source, kTISPropertyInputSourceID));
|
||||
if ([asciiSourceID compare: sourceID] != NSOrderedSame) {
|
||||
TISSelectInputSource(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (asciiSource) {
|
||||
TISSelectInputSource(asciiSource);
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
TISInputSourceRef currentSource = TISCopyCurrentKeyboardInputSource();
|
||||
NSString* currentSourceID = (__bridge NSString *)(TISGetInputSourceProperty(currentSource, kTISPropertyInputSourceID));
|
||||
NSArray* asciiInputSources = CFBridgingRelease(TISCreateASCIICapableInputSourceList());
|
||||
TISInputSourceRef asciiSource = (__bridge TISInputSourceRef)([asciiInputSources firstObject]);
|
||||
if (asciiSource) {
|
||||
NSString* asciiSourceID = (__bridge NSString *)(TISGetInputSourceProperty(asciiSource, kTISPropertyInputSourceID));
|
||||
return ([asciiSourceID compare: currentSourceID] == NSOrderedSame) ? GLFW_FALSE : GLFW_TRUE;
|
||||
}
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
63
src/input.c
63
src/input.c
@ -79,6 +79,20 @@ void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWb
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock)
|
||||
{
|
||||
if (window->callbacks.preedit) {
|
||||
window->callbacks.preedit((GLFWwindow*) window, window->ntext, window->preeditText, window->nblocks, window->preeditAttributeBlocks, focusedBlock);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwInputIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
if (window->callbacks.imestatus) {
|
||||
window->callbacks.imestatus((GLFWwindow*) window);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
if (window->callbacks.scroll)
|
||||
@ -162,6 +176,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
|
||||
return window->stickyKeys;
|
||||
case GLFW_STICKY_MOUSE_BUTTONS:
|
||||
return window->stickyMouseButtons;
|
||||
case GLFW_IME:
|
||||
return _glfwPlatformGetIMEStatus(window);
|
||||
default:
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
||||
return 0;
|
||||
@ -245,6 +261,13 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
case GLFW_IME:
|
||||
_glfwPlatformSetIMEStatus(window, value ? GLFW_TRUE : GLFW_FALSE);
|
||||
break;
|
||||
default:
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
||||
@ -469,6 +492,30 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
||||
_glfwPlatformSetCursor(window, cursor);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetPreeditCursorPos(GLFWwindow* handle, int *x, int *y, int *h)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
if (x)
|
||||
*x = window->preeditCursorPosX;
|
||||
if (y)
|
||||
*y = window->preeditCursorPosY;
|
||||
if (h)
|
||||
*h = window->preeditCursorHeight;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetPreeditCursorPos(GLFWwindow* handle, int x, int y, int h)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
window->preeditCursorPosX = x;
|
||||
window->preeditCursorPosY = y;
|
||||
window->preeditCursorHeight = h;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwResetPreeditText(GLFWwindow* handle) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_glfwPlatformResetPreeditText(window);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
@ -499,6 +546,22 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmods
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWpreeditfun glfwSetPreeditCallback(GLFWwindow* handle, GLFWpreeditfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.preedit, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* handle, GLFWimestatusfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.imestatus, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
|
||||
GLFWmousebuttonfun cbfun)
|
||||
{
|
||||
|
@ -370,9 +370,19 @@ struct _GLFWwindow
|
||||
int cursorMode;
|
||||
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||
char keys[GLFW_KEY_LAST + 1];
|
||||
|
||||
// Virtual cursor position when cursor is disabled
|
||||
double virtualCursorPosX, virtualCursorPosY;
|
||||
|
||||
// Preedit texts
|
||||
unsigned int* preeditText;
|
||||
int ntext;
|
||||
int ctext;
|
||||
int* preeditAttributeBlocks;
|
||||
int nblocks;
|
||||
int cblocks;
|
||||
int preeditCursorPosX, preeditCursorPosY, preeditCursorHeight;
|
||||
|
||||
_GLFWcontext context;
|
||||
|
||||
struct {
|
||||
@ -391,6 +401,8 @@ struct _GLFWwindow
|
||||
GLFWkeyfun key;
|
||||
GLFWcharfun character;
|
||||
GLFWcharmodsfun charmods;
|
||||
GLFWpreeditfun preedit;
|
||||
GLFWimestatusfun imestatus;
|
||||
GLFWdropfun drop;
|
||||
} callbacks;
|
||||
|
||||
@ -818,6 +830,46 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhy
|
||||
*/
|
||||
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
|
||||
|
||||
/*! @copydoc glfwResetPreeditText
|
||||
* @ingroup platform
|
||||
*/
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window);
|
||||
|
||||
/*! @brief Set IME status.
|
||||
*
|
||||
* This function set IME status.
|
||||
*
|
||||
* @param[in] window The window.
|
||||
* @param[in] active Turns on IME if `GFLW_TRUE` is given. Otherwise (`GLFW_FALSE`) turns off.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref preedit
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup platform
|
||||
*/
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active);
|
||||
|
||||
/*! @brief Get IME status.
|
||||
*
|
||||
* This function get IME status.
|
||||
*
|
||||
* @param[in] window The window.
|
||||
* @return When IME is active, this function returns `GFLW_TRUE`. Otherwise `GLFW_FALSE`.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* This function may only be called from the main thread.
|
||||
*
|
||||
* @sa @ref preedit
|
||||
*
|
||||
* @since Added in GLFW 3.X.
|
||||
*
|
||||
* @ingroup platform
|
||||
*/
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window);
|
||||
|
||||
//========================================================================
|
||||
// Event API functions
|
||||
@ -904,6 +956,19 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
|
||||
*/
|
||||
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain);
|
||||
|
||||
/*! @brief Notifies shared code of a IME preedit text update event.
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] focusedBlock Focused preedit text block index.
|
||||
* @ingroup event
|
||||
*/
|
||||
void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock);
|
||||
|
||||
/*! @brief Notifies shared code of a IME status change.
|
||||
* @param[in] window The window that received the event.
|
||||
* @ingroup event
|
||||
*/
|
||||
void _glfwInputIMEStatus(_GLFWwindow* window);
|
||||
|
||||
/*! @brief Notifies shared code of a scroll event.
|
||||
* @param[in] window The window that received the event.
|
||||
* @param[in] xoffset The scroll offset along the x-axis.
|
||||
|
@ -890,6 +890,25 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return err;
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
@ -301,3 +301,16 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
#include <windowsx.h>
|
||||
#include <shellapi.h>
|
||||
#include <imm.h>
|
||||
|
||||
#define _GLFW_KEY_INVALID -2
|
||||
|
||||
@ -422,6 +423,15 @@ static void releaseMonitor(_GLFWwindow* window)
|
||||
_glfwRestoreVideoModeWin32(window->monitor);
|
||||
}
|
||||
|
||||
// Set cursor position to decide candidate window
|
||||
static void _win32ChangeCursorPosition(HIMC hIMC, _GLFWwindow* window) {
|
||||
int x = window->preeditCursorPosX;
|
||||
int y = window->preeditCursorPosY;
|
||||
int h = window->preeditCursorHeight;
|
||||
CANDIDATEFORM excludeRect = {0, CFS_EXCLUDE, {x, y}, {x, y, x, y+h}};
|
||||
ImmSetCandidateWindow(hIMC, &excludeRect);
|
||||
}
|
||||
|
||||
// Window callback function (handles window messages)
|
||||
//
|
||||
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
@ -536,7 +546,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
}
|
||||
|
||||
_glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
|
||||
return 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
case WM_KEYDOWN:
|
||||
@ -570,7 +580,93 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_IME_COMPOSITION:
|
||||
{
|
||||
if (lParam & GCS_RESULTSTR) {
|
||||
window->nblocks = 0;
|
||||
window->ntext = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
return TRUE;
|
||||
}
|
||||
if (lParam & GCS_COMPSTR) {
|
||||
HIMC hIMC = ImmGetContext(hWnd);
|
||||
// get preedit data sizes
|
||||
LONG preeditTextLength = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
|
||||
LONG attrLength = ImmGetCompositionString(hIMC, GCS_COMPATTR, NULL, 0);
|
||||
LONG clauseLength = ImmGetCompositionString(hIMC, GCS_COMPCLAUSE, NULL, 0);
|
||||
if (preeditTextLength > 0) {
|
||||
// get preedit data
|
||||
int length = preeditTextLength/sizeof(WCHAR);
|
||||
LPWSTR buffer = (LPWSTR)malloc(sizeof(WCHAR)+preeditTextLength);
|
||||
LPSTR attributes = (LPSTR)malloc(attrLength);
|
||||
DWORD *clauses = (DWORD*)malloc(clauseLength);
|
||||
ImmGetCompositionStringW(hIMC, GCS_COMPSTR, buffer, preeditTextLength);
|
||||
ImmGetCompositionString(hIMC, GCS_COMPATTR, attributes, attrLength);
|
||||
ImmGetCompositionString(hIMC, GCS_COMPCLAUSE, clauses, clauseLength);
|
||||
// store preedit text
|
||||
int ctext = window->ctext;
|
||||
while (ctext < length+1) {
|
||||
ctext = (ctext == 0) ? 1 : ctext*2;
|
||||
}
|
||||
if (ctext != window->ctext) {
|
||||
unsigned int* preeditText = realloc(window->preeditText, sizeof(unsigned int)*ctext);
|
||||
if (preeditText == NULL) {
|
||||
return 0;
|
||||
free(buffer);
|
||||
free(attributes);
|
||||
free(clauses);
|
||||
}
|
||||
window->preeditText = preeditText;
|
||||
window->ctext = ctext;
|
||||
}
|
||||
window->ntext = length;
|
||||
window->preeditText[length] = 0;
|
||||
int i;
|
||||
for (i=0; i < length; i++) {
|
||||
window->preeditText[i] = buffer[i];
|
||||
}
|
||||
// store blocks
|
||||
window->nblocks = clauseLength/sizeof(DWORD)-1;
|
||||
// last element of clauses is a block count, but
|
||||
// text length is convenient.
|
||||
clauses[window->nblocks] = length;
|
||||
int cblocks = window->cblocks;
|
||||
while (cblocks < window->nblocks) {
|
||||
cblocks = (cblocks == 0) ? 1 : cblocks*2;
|
||||
}
|
||||
if (cblocks != window->cblocks) {
|
||||
int* blocks = realloc(window->preeditAttributeBlocks, sizeof(int)*cblocks);
|
||||
if (blocks == NULL) {
|
||||
return 0;
|
||||
free(buffer);
|
||||
free(attributes);
|
||||
free(clauses);
|
||||
}
|
||||
window->preeditAttributeBlocks = blocks;
|
||||
window->cblocks = cblocks;
|
||||
}
|
||||
int focusedBlock = 0;
|
||||
for (i=0; i < window->nblocks; i++) {
|
||||
window->preeditAttributeBlocks[i] = clauses[i+1]-clauses[i];
|
||||
if (attributes[clauses[i]] != ATTR_CONVERTED) {
|
||||
focusedBlock = i;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
free(attributes);
|
||||
free(clauses);
|
||||
_glfwInputPreedit(window, focusedBlock);
|
||||
_win32ChangeCursorPosition(hIMC, window);
|
||||
}
|
||||
ImmReleaseContext(hWnd, hIMC);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_IME_NOTIFY:
|
||||
if (wParam == IMN_SETOPENSTATUS)
|
||||
_glfwInputIMEStatus(window);
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
@ -1705,6 +1801,30 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return err;
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
HWND hWnd = window->win32.handle;
|
||||
HIMC hIMC = ImmGetContext(hWnd);
|
||||
ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
ImmReleaseContext(hWnd, hIMC);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
HWND hWnd = window->win32.handle;
|
||||
HIMC hIMC = ImmGetContext(hWnd);
|
||||
ImmSetOpenStatus(hIMC, active ? TRUE : FALSE);
|
||||
ImmReleaseContext(hWnd, hIMC);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
HWND hWnd = window->win32.handle;
|
||||
HIMC hIMC = ImmGetContext(hWnd);
|
||||
BOOL result = ImmGetOpenStatus(hIMC);
|
||||
ImmReleaseContext(hWnd, hIMC);
|
||||
return result ? GLFW_TRUE : GLFW_FALSE;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
10
src/window.c
10
src/window.c
@ -230,6 +230,10 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
||||
if (wndconfig.focused)
|
||||
_glfwPlatformFocusWindow(window);
|
||||
}
|
||||
|
||||
window->preeditCursorPosX = 0;
|
||||
window->preeditCursorPosY = height;
|
||||
window->preeditCursorHeight = 0;
|
||||
}
|
||||
|
||||
return (GLFWwindow*) window;
|
||||
@ -408,7 +412,11 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
|
||||
|
||||
*prev = window->next;
|
||||
}
|
||||
|
||||
// Clear memory for preedit text
|
||||
if (window->preeditText)
|
||||
free(window->preeditText);
|
||||
if (window->preeditAttributeBlocks)
|
||||
free(window->preeditAttributeBlocks);
|
||||
free(window);
|
||||
}
|
||||
|
||||
|
@ -1027,6 +1027,28 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return err;
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: IME not implemented yet");
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: IME not implemented yet");
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||
{
|
||||
// TODO
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: IME not implemented yet");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
@ -128,6 +128,16 @@ typedef struct _GLFWwindowX11
|
||||
unsigned int lastKeyCode;
|
||||
Time lastKeyTime;
|
||||
|
||||
// Preedit callbacks
|
||||
XIMCallback preeditStartCallback;
|
||||
XIMCallback preeditDoneCallback;
|
||||
XIMCallback preeditDrawCallback;
|
||||
XIMCallback preeditCaretCallback;
|
||||
XIMCallback statusStartCallback;
|
||||
XIMCallback statusDoneCallback;
|
||||
XIMCallback statusDrawCallback;
|
||||
|
||||
int imeFocus;
|
||||
} _GLFWwindowX11;
|
||||
|
||||
// X11-specific global data
|
||||
|
244
src/x11_window.c
244
src/x11_window.c
@ -48,6 +48,9 @@
|
||||
#define Button6 6
|
||||
#define Button7 7
|
||||
|
||||
#if defined(X_HAVE_UTF8_STRING)
|
||||
static unsigned int decodeUTF8(const char** s);
|
||||
#endif
|
||||
|
||||
// Wait for data to arrive using select
|
||||
// This avoids blocking other threads via the per-display Xlib lock that also
|
||||
@ -437,6 +440,196 @@ static char** parseUriList(char* text, int* count)
|
||||
return paths;
|
||||
}
|
||||
|
||||
// Update cursor position to decide candidate window
|
||||
static void _ximChangeCursorPosition(XIC xic, _GLFWwindow* window)
|
||||
{
|
||||
XVaNestedList preedit_attr;
|
||||
XPoint spot;
|
||||
|
||||
spot.x = window->preeditCursorPosX;
|
||||
spot.y = window->preeditCursorPosY + window->preeditCursorHeight;
|
||||
preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
|
||||
XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
|
||||
XFree(preedit_attr);
|
||||
}
|
||||
|
||||
// IME Start callback (do nothing)
|
||||
static void _ximPreeditStartCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||
{
|
||||
}
|
||||
|
||||
// IME Done callback (do nothing)
|
||||
static void _ximPreeditDoneCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||
{
|
||||
}
|
||||
|
||||
// IME Draw callback
|
||||
static void _ximPreeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct *callData)
|
||||
{
|
||||
int i, j, length, ctext, rstart, rend;
|
||||
XIMText* text;
|
||||
const char* src;
|
||||
unsigned int codePoint;
|
||||
unsigned int* preeditText;
|
||||
XIMFeedback f;
|
||||
_GLFWwindow* window = (_GLFWwindow*)clientData;
|
||||
|
||||
// keep cursor position to reduce API call
|
||||
int cursorX = window->preeditCursorPosX;
|
||||
int cursorY = window->preeditCursorPosY;
|
||||
int cursorHeight = window->preeditCursorHeight;
|
||||
|
||||
if (!callData->text) {
|
||||
// preedit text is empty
|
||||
window->ntext = 0;
|
||||
window->nblocks = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
return;
|
||||
} else {
|
||||
text = callData->text;
|
||||
length = callData->chg_length;
|
||||
if (text->encoding_is_wchar) {
|
||||
// wchar is not supported
|
||||
return;
|
||||
}
|
||||
ctext = window->ctext;
|
||||
while (ctext < length+1) {
|
||||
ctext = (ctext == 0) ? 1 : ctext * 2;
|
||||
}
|
||||
if (ctext != window->ctext) {
|
||||
preeditText = realloc(window->preeditText, sizeof(unsigned int)*ctext);
|
||||
if (preeditText == NULL) {
|
||||
return;
|
||||
}
|
||||
window->preeditText = preeditText;
|
||||
window->ctext = ctext;
|
||||
}
|
||||
window->ntext = length;
|
||||
window->preeditText[length] = 0;
|
||||
if (window->cblocks == 0) {
|
||||
window->preeditAttributeBlocks = malloc(sizeof(int)*4);
|
||||
window->cblocks = 4;
|
||||
}
|
||||
src = text->string.multi_byte;
|
||||
rend = 0;
|
||||
rstart = length;
|
||||
for (i = 0, j = 0; i < text->length; i++) {
|
||||
#if defined(X_HAVE_UTF8_STRING)
|
||||
codePoint = decodeUTF8(&src);
|
||||
#else
|
||||
codePoint = *src;
|
||||
src++;
|
||||
#endif
|
||||
if (i < callData->chg_first || callData->chg_first+length < i) {
|
||||
continue;
|
||||
}
|
||||
window->preeditText[j++] = codePoint;
|
||||
f = text->feedback[i];
|
||||
if ((f & XIMReverse) || (f & XIMHighlight)) {
|
||||
rend = i;
|
||||
if (i < rstart) {
|
||||
rstart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rstart == length) {
|
||||
window->nblocks = 1;
|
||||
window->preeditAttributeBlocks[0] = length;
|
||||
window->preeditAttributeBlocks[1] = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
} else if (rstart == 0) {
|
||||
if (rend == length -1) {
|
||||
window->nblocks = 1;
|
||||
window->preeditAttributeBlocks[0] = length;
|
||||
window->preeditAttributeBlocks[1] = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
} else {
|
||||
window->nblocks = 2;
|
||||
window->preeditAttributeBlocks[0] = rend + 1;
|
||||
window->preeditAttributeBlocks[1] = length - rend - 1;
|
||||
window->preeditAttributeBlocks[2] = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
}
|
||||
} else if (rend == length -1) {
|
||||
window->nblocks = 2;
|
||||
window->preeditAttributeBlocks[0] = rstart;
|
||||
window->preeditAttributeBlocks[1] = length - rstart;
|
||||
window->preeditAttributeBlocks[2] = 0;
|
||||
_glfwInputPreedit(window, 1);
|
||||
} else {
|
||||
window->nblocks = 3;
|
||||
window->preeditAttributeBlocks[0] = rstart;
|
||||
window->preeditAttributeBlocks[1] = rend - rstart + 1;
|
||||
window->preeditAttributeBlocks[2] = length - rend - 1;
|
||||
window->preeditAttributeBlocks[3] = 0;
|
||||
_glfwInputPreedit(window, 1);
|
||||
}
|
||||
if ((cursorX != window->preeditCursorPosX)
|
||||
|| (cursorY != window->preeditCursorPosY)
|
||||
|| (cursorHeight != window->preeditCursorHeight)) {
|
||||
_ximChangeCursorPosition(xic, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IME Caret callback (do nothing)
|
||||
static void _ximPreeditCaretCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||
{
|
||||
}
|
||||
|
||||
static void _ximStatusStartCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*)clientData;
|
||||
window->x11.imeFocus = GLFW_TRUE;
|
||||
}
|
||||
|
||||
static void _ximStatusDoneCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*)clientData;
|
||||
window->x11.imeFocus = GLFW_FALSE;
|
||||
}
|
||||
|
||||
static void _ximStatusDrawCallback(XIC xic, XPointer clientData, XIMStatusDrawCallbackStruct* callData)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*)clientData;
|
||||
_glfwInputIMEStatus(window);
|
||||
}
|
||||
|
||||
// Create XIM Preedit callback
|
||||
static XVaNestedList _createXIMPreeditCallbacks(_GLFWwindow* window)
|
||||
{
|
||||
window->x11.preeditStartCallback.client_data = (XPointer)window;
|
||||
window->x11.preeditStartCallback.callback = (XIMProc)_ximPreeditStartCallback;
|
||||
window->x11.preeditDoneCallback.client_data = (XPointer)window;
|
||||
window->x11.preeditDoneCallback.callback = (XIMProc)_ximPreeditDoneCallback;
|
||||
window->x11.preeditDrawCallback.client_data = (XPointer)window;
|
||||
window->x11.preeditDrawCallback.callback = (XIMProc)_ximPreeditDrawCallback;
|
||||
window->x11.preeditCaretCallback.client_data = (XPointer)window;
|
||||
window->x11.preeditCaretCallback.callback = (XIMProc)_ximPreeditCaretCallback;
|
||||
return XVaCreateNestedList (0,
|
||||
XNPreeditStartCallback, &window->x11.preeditStartCallback.client_data,
|
||||
XNPreeditDoneCallback, &window->x11.preeditDoneCallback.client_data,
|
||||
XNPreeditDrawCallback, &window->x11.preeditDrawCallback.client_data,
|
||||
XNPreeditCaretCallback, &window->x11.preeditCaretCallback.client_data,
|
||||
NULL);
|
||||
}
|
||||
|
||||
// Create XIM status callback
|
||||
static XVaNestedList _createXIMStatusCallbacks(_GLFWwindow* window)
|
||||
{
|
||||
window->x11.statusStartCallback.client_data = (XPointer)window;
|
||||
window->x11.statusStartCallback.callback = (XIMProc)_ximStatusStartCallback;
|
||||
window->x11.statusDoneCallback.client_data = (XPointer)window;
|
||||
window->x11.statusDoneCallback.callback = (XIMProc)_ximStatusDoneCallback;
|
||||
window->x11.statusDrawCallback.client_data = (XPointer)window;
|
||||
window->x11.statusDrawCallback.callback = (XIMProc)_ximStatusDrawCallback;
|
||||
return XVaCreateNestedList (0,
|
||||
XNStatusStartCallback, &window->x11.statusStartCallback.client_data,
|
||||
XNStatusDoneCallback, &window->x11.statusDoneCallback.client_data,
|
||||
XNStatusDrawCallback, &window->x11.statusDrawCallback.client_data,
|
||||
NULL);
|
||||
}
|
||||
|
||||
// Centers the cursor over the window client area
|
||||
//
|
||||
static void centerCursor(_GLFWwindow* window)
|
||||
@ -646,14 +839,23 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||
|
||||
if (_glfw.x11.im)
|
||||
{
|
||||
XVaNestedList preeditList = _createXIMPreeditCallbacks(window);
|
||||
XVaNestedList statusList = _createXIMStatusCallbacks(window);
|
||||
window->x11.ic = XCreateIC(_glfw.x11.im,
|
||||
XNInputStyle,
|
||||
XIMPreeditNothing | XIMStatusNothing,
|
||||
XIMPreeditCallbacks | XIMStatusCallbacks,
|
||||
XNClientWindow,
|
||||
window->x11.handle,
|
||||
XNFocusWindow,
|
||||
window->x11.handle,
|
||||
XNPreeditAttributes,
|
||||
preeditList,
|
||||
XNStatusAttributes,
|
||||
statusList,
|
||||
NULL);
|
||||
XFree(preeditList);
|
||||
XFree(statusList);
|
||||
window->x11.imeFocus = GLFW_FALSE;
|
||||
}
|
||||
|
||||
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
||||
@ -2480,6 +2682,46 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformResetPreeditText(_GLFWwindow* window) {
|
||||
XIC ic = window->x11.ic;
|
||||
|
||||
/* restore conversion state after resetting ic later */
|
||||
XIMPreeditState preedit_state = XIMPreeditUnKnown;
|
||||
XVaNestedList preedit_attr;
|
||||
char* result;
|
||||
|
||||
if (window->ntext == 0)
|
||||
return;
|
||||
|
||||
preedit_attr = XVaCreateNestedList(0, XNPreeditState, &preedit_state, NULL);
|
||||
XGetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
|
||||
XFree(preedit_attr);
|
||||
|
||||
result = XmbResetIC(ic);
|
||||
|
||||
preedit_attr = XVaCreateNestedList(0, XNPreeditState, preedit_state, NULL);
|
||||
XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
|
||||
XFree(preedit_attr);
|
||||
|
||||
window->ntext = 0;
|
||||
window->nblocks = 0;
|
||||
_glfwInputPreedit(window, 0);
|
||||
|
||||
XFree (result);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active) {
|
||||
XIC ic = window->x11.ic;
|
||||
if (active) {
|
||||
XSetICFocus(ic);
|
||||
} else {
|
||||
XUnsetICFocus(ic);
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformGetIMEStatus(_GLFWwindow* window) {
|
||||
return window->x11.imeFocus;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
|
@ -420,6 +420,45 @@ static void char_mods_callback(GLFWwindow* window, unsigned int codepoint, int m
|
||||
get_mods_name(mods));
|
||||
}
|
||||
|
||||
static void preedit_callback(GLFWwindow* window, int strLength, unsigned int* string, int blockLength, int* blocks, int focusedBlock) {
|
||||
Slot* slot = glfwGetWindowUserPointer(window);
|
||||
int i, blockIndex = -1, blockCount = 0;
|
||||
int width, height;
|
||||
printf("%08x to %i at %0.3f: Preedit text ",
|
||||
counter++, slot->number, glfwGetTime());
|
||||
if (strLength == 0 || blockLength == 0) {
|
||||
printf("(empty)\n");
|
||||
} else {
|
||||
for (i = 0; i < strLength; i++) {
|
||||
if (blockCount == 0) {
|
||||
if (blockIndex == focusedBlock) {
|
||||
printf("]");
|
||||
}
|
||||
blockIndex++;
|
||||
blockCount = blocks[blockIndex];
|
||||
printf("\n block %d: ", blockIndex);
|
||||
if (blockIndex == focusedBlock) {
|
||||
printf("[");
|
||||
}
|
||||
}
|
||||
printf("%s", get_character_string(string[i]));
|
||||
blockCount--;
|
||||
}
|
||||
if (blockIndex == focusedBlock) {
|
||||
printf("]");
|
||||
}
|
||||
printf("\n");
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
glfwSetPreeditCursorPos(window, width/2, height/2, 20);
|
||||
}
|
||||
}
|
||||
|
||||
static void ime_callback(GLFWwindow* window) {
|
||||
Slot* slot = glfwGetWindowUserPointer(window);
|
||||
printf("%08x to %i at %0.3f: IME switched\n",
|
||||
counter++, slot->number, glfwGetTime());
|
||||
}
|
||||
|
||||
static void drop_callback(GLFWwindow* window, int count, const char** paths)
|
||||
{
|
||||
int i;
|
||||
@ -596,6 +635,8 @@ int main(int argc, char** argv)
|
||||
glfwSetKeyCallback(slots[i].window, key_callback);
|
||||
glfwSetCharCallback(slots[i].window, char_callback);
|
||||
glfwSetCharModsCallback(slots[i].window, char_mods_callback);
|
||||
glfwSetPreeditCallback(slots[i].window, preedit_callback);
|
||||
glfwSetIMEStatusCallback(slots[i].window, ime_callback);
|
||||
glfwSetDropCallback(slots[i].window, drop_callback);
|
||||
|
||||
glfwMakeContextCurrent(slots[i].window);
|
||||
|
Loading…
Reference in New Issue
Block a user