Compare commits
3 Commits
master
...
preedit-im
Author | SHA1 | Date | |
---|---|---|---|
|
ed59b80cc7 | ||
|
0df386d06a | ||
|
779b56276c |
1
.gitignore
vendored
1
.gitignore
vendored
@ -81,4 +81,3 @@ tests/timeout
|
|||||||
tests/title
|
tests/title
|
||||||
tests/vulkan
|
tests/vulkan
|
||||||
tests/windows
|
tests/windows
|
||||||
|
|
||||||
|
@ -226,6 +226,7 @@ endif()
|
|||||||
if (_GLFW_WIN32)
|
if (_GLFW_WIN32)
|
||||||
|
|
||||||
list(APPEND glfw_PKG_LIBS "-lgdi32")
|
list(APPEND glfw_PKG_LIBS "-lgdi32")
|
||||||
|
list(APPEND glfw_LIBRARIES "-limm32")
|
||||||
|
|
||||||
if (GLFW_USE_HYBRID_HPG)
|
if (GLFW_USE_HYBRID_HPG)
|
||||||
set(_GLFW_USE_HYBRID_HPG 1)
|
set(_GLFW_USE_HYBRID_HPG 1)
|
||||||
|
106
docs/input.dox
106
docs/input.dox
@ -207,6 +207,112 @@ void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods)
|
|||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
|
|
||||||
|
@subsection preedit IME support
|
||||||
|
|
||||||
|
All modern operating systems provide a
|
||||||
|
[IME](https://en.wikipedia.org/wiki/Input_method) (Input Method Editor)
|
||||||
|
mechanism to input character sets that cannot be mapped to physical keys, such
|
||||||
|
as [CJK characters](https://en.wikipedia.org/wiki/CJK_characters) (Chinese,
|
||||||
|
Japanese, Korean). Some operating systems also support speech-to-text input via
|
||||||
|
the IME mechanism.
|
||||||
|
|
||||||
|
GLFW provides IME support functions to help you implement better text input
|
||||||
|
features. You should add suitable visualization code for pre-edit 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 pre-edit callback to receive pre-edit text before committed.
|
||||||
|
|
||||||
|
@code
|
||||||
|
glfwSetPreeditCallback(window, preedit_callback);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
The callback function receives the pre-edit text and block information.
|
||||||
|
|
||||||
|
@code
|
||||||
|
static void preedit_callback(GLFWwindow* window, unsigned int* codepoints, int blockCount, int* blocks, int focusedBlock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
The codepoints parameter contains the whole pre-edit text. Each character of the
|
||||||
|
pre-edit string is a Unicode codepoint like with @ref input_char.
|
||||||
|
|
||||||
|
If you want to type the text "寿司" (sushi), usually the callback is called
|
||||||
|
several times like the following sequence:
|
||||||
|
|
||||||
|
@code
|
||||||
|
-# 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]
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
If pre-edit text includes several semantic blocks, pre-edit callbacks returns
|
||||||
|
several blocks after a space key pressed:
|
||||||
|
|
||||||
|
@code
|
||||||
|
-# preedit: [string: "わたしはすしをたべます", block: [11], focusedBlock: 0]
|
||||||
|
-# preedit: [string: "私は寿司を食べます", block: [2, 7], focusedBlock: 1]
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
"blocks" is a list of block length. The above case, it contains the following
|
||||||
|
blocks and second block is focused.
|
||||||
|
|
||||||
|
@code
|
||||||
|
- 私は
|
||||||
|
- [寿司を食べます]
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
committed 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 (window coordinates and height). You should call this function in the
|
||||||
|
above pre-edit text callback function.
|
||||||
|
|
||||||
|
@code
|
||||||
|
int xpos, ypos, height;
|
||||||
|
glfwSetPreeditCursorPos(window, xpos, ypos, height);
|
||||||
|
glfwGetPreeditCursorPos(window, &xpos, &ypos, &height);
|
||||||
|
@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, ime_status_callback);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
The ime_status_callback has simple signature like this:
|
||||||
|
|
||||||
|
@code
|
||||||
|
static void ime_status_callback(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
You can implement the code that resets or commits pre-edit text when IME status
|
||||||
|
is changed and pre-edit text is not empty.
|
||||||
|
|
||||||
|
When the focus is gone from text box, you can use @ref glfwSetInputMode, @ref
|
||||||
|
glfwGetInputMode with the `GLFW_IME` mode and the @ref glfwResetPreeditText
|
||||||
|
function.
|
||||||
|
|
||||||
|
|
||||||
@subsection input_key_name Key names
|
@subsection input_key_name Key names
|
||||||
|
|
||||||
If you wish to refer to keys by name, you can query the keyboard layout
|
If you wish to refer to keys by name, you can query the keyboard layout
|
||||||
|
@ -686,6 +686,7 @@ extern "C" {
|
|||||||
#define GLFW_CURSOR 0x00033001
|
#define GLFW_CURSOR 0x00033001
|
||||||
#define GLFW_STICKY_KEYS 0x00033002
|
#define GLFW_STICKY_KEYS 0x00033002
|
||||||
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
|
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
|
||||||
|
#define GLFW_IME 0x00033004
|
||||||
|
|
||||||
#define GLFW_CURSOR_NORMAL 0x00034001
|
#define GLFW_CURSOR_NORMAL 0x00034001
|
||||||
#define GLFW_CURSOR_HIDDEN 0x00034002
|
#define GLFW_CURSOR_HIDDEN 0x00034002
|
||||||
@ -1100,6 +1101,36 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,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] 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*,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.
|
/*! @brief The function signature for file drop callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for file drop callbacks.
|
* This is the function signature for file drop callbacks.
|
||||||
@ -3009,12 +3040,12 @@ GLFWAPI void glfwPostEmptyEvent(void);
|
|||||||
/*! @brief Returns the value of an input option for the specified window.
|
/*! @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.
|
* 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
|
* The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||||
*
|
*
|
||||||
* @param[in] window The window to query.
|
* @param[in] window The window to query.
|
||||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||||
* GLFW_INVALID_ENUM.
|
* GLFW_INVALID_ENUM.
|
||||||
@ -3032,8 +3063,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
|||||||
/*! @brief Sets an input option for the specified window.
|
/*! @brief Sets an input option for the specified window.
|
||||||
*
|
*
|
||||||
* This function sets an input mode option for the specified window. The mode
|
* This function sets an input mode option for the specified window. The mode
|
||||||
* must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
* must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`.
|
||||||
*
|
*
|
||||||
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
|
* If the mode is `GLFW_CURSOR`, the value must be one of the following cursor
|
||||||
* modes:
|
* modes:
|
||||||
@ -3059,9 +3090,12 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
|||||||
* you are only interested in whether mouse buttons have been pressed but not
|
* you are only interested in whether mouse buttons have been pressed but not
|
||||||
* when or in which order.
|
* 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] window The window whose input mode to set.
|
||||||
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or
|
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`.
|
* `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_IME`
|
||||||
* @param[in] value The new value of the specified input mode.
|
* @param[in] value The new value of the specified input mode.
|
||||||
*
|
*
|
||||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
|
||||||
@ -3422,6 +3456,69 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor);
|
|||||||
*/
|
*/
|
||||||
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor);
|
||||||
|
|
||||||
|
/*! @brief Retrieves the position of the caret.
|
||||||
|
*
|
||||||
|
* This function returns the position of the caret, in screen coordinates,
|
||||||
|
* relative to the upper-left corner of the client area of the specified
|
||||||
|
* window.
|
||||||
|
*
|
||||||
|
* @param[in] window The desired window.
|
||||||
|
* @param[out] xpos Where to store the caret x-coordinate, relative to the
|
||||||
|
* left edge of the client area, or `NULL`.
|
||||||
|
* @param[out] ypos Where to store the caret y-coordinate, relative to the to
|
||||||
|
* top edge of the client area, or `NULL`.
|
||||||
|
* @param[out] ypos Where to store the caret height, or `NULL`.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref input_char
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwGetPreeditCaretPos(GLFWwindow* window, int* xpos, int* ypos, int* height);
|
||||||
|
|
||||||
|
/*! @brief Sets the caret position used to place the IME candidate window.
|
||||||
|
*
|
||||||
|
* This function teach position hint to decide the candidate window. The
|
||||||
|
* candidate window is a part of IME (Input Method Editor) and shows several
|
||||||
|
* candidate strings.
|
||||||
|
*
|
||||||
|
* Windows sytems decide proper position from text cursor geometry.
|
||||||
|
* You should call this function in preedit callback.
|
||||||
|
*
|
||||||
|
* @param[in] window The window to set the caret position for.
|
||||||
|
* @param[in] xpos The x-coordinate of the caret within the window client area.
|
||||||
|
* @param[in] ypos The y-coordinate of the caret within the window client area.
|
||||||
|
* @param[in] height The height of the caret.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref input_char
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwSetPreeditCaretPos(GLFWwindow* window, int xpos, int ypos, int height);
|
||||||
|
|
||||||
|
/*! @brief Reset IME input status.
|
||||||
|
*
|
||||||
|
* This function resets IME's preedit text.
|
||||||
|
*
|
||||||
|
* @param[in] window The desired window.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref preedit
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwResetPreeditText(GLFWwindow* window);
|
||||||
|
|
||||||
/*! @brief Sets the key callback.
|
/*! @brief Sets the key callback.
|
||||||
*
|
*
|
||||||
* This function sets the key callback of the specified window, which is called
|
* This function sets the key callback of the specified window, which is called
|
||||||
@ -3536,6 +3633,56 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun);
|
|||||||
*/
|
*/
|
||||||
GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun 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.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref input_char
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*
|
||||||
|
* The callback receives the relative position of the input caret 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.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @sa @ref input_char
|
||||||
|
*
|
||||||
|
* @since Added in version 3.3.
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
GLFWAPI GLFWimestatusfun glfwSetIMEStatusCallback(GLFWwindow* window, GLFWimestatusfun cbfun);
|
||||||
|
|
||||||
/*! @brief Sets the mouse button callback.
|
/*! @brief Sets the mouse button callback.
|
||||||
*
|
*
|
||||||
* This function sets the mouse button callback of the specified window, which
|
* 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);
|
_glfwInputWindowFocus(window, GLFW_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
- (void)imeStatusChangeNotified:(NSNotification *)notification {
|
||||||
|
_glfwInputIMEStatus(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
// Delegate for application related notifications
|
// Delegate for application related notifications
|
||||||
@ -714,6 +717,43 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
[markedText initWithAttributedString:string];
|
[markedText initWithAttributedString:string];
|
||||||
else
|
else
|
||||||
[markedText initWithString:string];
|
[markedText initWithString:string];
|
||||||
|
|
||||||
|
NSString* markedTextString = markedText.string;
|
||||||
|
|
||||||
|
NSUInteger i, length = [markedTextString length];
|
||||||
|
|
||||||
|
free(window->preeditText);
|
||||||
|
window->preeditText = calloc(length + 1, sizeof(unsigned int));
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
window->preeditText[i] = [markedTextString characterAtIndex:i];
|
||||||
|
|
||||||
|
int focusedBlock = 0;
|
||||||
|
NSInteger offset = 0;
|
||||||
|
|
||||||
|
window->preeditBlockCount = 0;
|
||||||
|
|
||||||
|
while (offset < length)
|
||||||
|
{
|
||||||
|
NSRange effectiveRange;
|
||||||
|
NSDictionary *attributes = [markedText attributesAtIndex:offset
|
||||||
|
effectiveRange:&effectiveRange];
|
||||||
|
|
||||||
|
window->preeditBlockCount++;
|
||||||
|
window->preeditBlocks = realloc(window->preeditBlocks,
|
||||||
|
window->preeditBlockCount * sizeof(int));
|
||||||
|
|
||||||
|
window->preeditBlocks[window->preeditBlockCount - 1] = effectiveRange.length;
|
||||||
|
offset += effectiveRange.length;
|
||||||
|
if (!effectiveRange.length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
NSNumber* underline = (NSNumber*) [attributes objectForKey:@"NSUnderline"];
|
||||||
|
if ([underline intValue] != 1)
|
||||||
|
focusedBlock = window->preeditBlockCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_glfwInputPreedit(window, focusedBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)unmarkText
|
- (void)unmarkText
|
||||||
@ -742,8 +782,10 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
{
|
{
|
||||||
int xpos, ypos;
|
int xpos, ypos;
|
||||||
_glfwPlatformGetWindowPos(window, &xpos, &ypos);
|
_glfwPlatformGetWindowPos(window, &xpos, &ypos);
|
||||||
const NSRect contentRect = [window->ns.view frame];
|
return NSMakeRect(xpos + window->preeditCursorPosX,
|
||||||
return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0);
|
transformY(ypos + window->preeditCursorPosY),
|
||||||
|
0.0,
|
||||||
|
window->preeditCursorHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
|
||||||
@ -1049,6 +1091,11 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
[window->ns.object setAcceptsMouseMovedEvents:YES];
|
[window->ns.object setAcceptsMouseMovedEvents:YES];
|
||||||
[window->ns.object setRestorable:NO];
|
[window->ns.object setRestorable:NO];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver: window->ns.delegate
|
||||||
|
selector:@selector(imeStatusChangeNotified:)
|
||||||
|
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||||
|
object: nil];
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,6 +1149,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
|||||||
if (_glfw.ns.disabledCursorWindow == window)
|
if (_glfw.ns.disabledCursorWindow == window)
|
||||||
_glfw.ns.disabledCursorWindow = NULL;
|
_glfw.ns.disabledCursorWindow = NULL;
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver: window->ns.delegate];
|
||||||
|
|
||||||
[window->ns.object orderOut:nil];
|
[window->ns.object orderOut:nil];
|
||||||
|
|
||||||
if (window->monitor)
|
if (window->monitor)
|
||||||
@ -1735,6 +1784,67 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
NSTextInputContext* context = [NSTextInputContext currentInputContext];
|
||||||
|
[context discardMarkedText];
|
||||||
|
[window->ns.view unmarkText];
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||||
|
{
|
||||||
|
// NOTE: macOS 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, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW native API //////
|
////// GLFW native API //////
|
||||||
|
88
src/input.c
88
src/input.c
@ -79,6 +79,24 @@ 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->preeditText,
|
||||||
|
window->preeditBlockCount,
|
||||||
|
window->preeditBlocks,
|
||||||
|
focusedBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwInputIMEStatus(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
if (window->callbacks.imestatus)
|
||||||
|
window->callbacks.imestatus((GLFWwindow*) window);
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
||||||
{
|
{
|
||||||
if (window->callbacks.scroll)
|
if (window->callbacks.scroll)
|
||||||
@ -162,6 +180,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
|
|||||||
return window->stickyKeys;
|
return window->stickyKeys;
|
||||||
case GLFW_STICKY_MOUSE_BUTTONS:
|
case GLFW_STICKY_MOUSE_BUTTONS:
|
||||||
return window->stickyMouseButtons;
|
return window->stickyMouseButtons;
|
||||||
|
case GLFW_IME:
|
||||||
|
return _glfwPlatformGetIMEStatus(window);
|
||||||
default:
|
default:
|
||||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
||||||
return 0;
|
return 0;
|
||||||
@ -245,6 +265,14 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
|||||||
window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE;
|
window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
return;
|
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);
|
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode);
|
||||||
@ -469,6 +497,50 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
|||||||
_glfwPlatformSetCursor(window, cursor);
|
_glfwPlatformSetCursor(window, cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWAPI void glfwGetPreeditCaretPos(GLFWwindow* handle, int* xpos, int* ypos, int* height)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
assert(window != NULL);
|
||||||
|
|
||||||
|
if (xpos)
|
||||||
|
*xpos = 0;
|
||||||
|
if (ypos)
|
||||||
|
*ypos = 0;
|
||||||
|
if (height)
|
||||||
|
*height = 0;
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
if (xpos)
|
||||||
|
*xpos = window->preeditCaretPosX;
|
||||||
|
if (ypos)
|
||||||
|
*ypos = window->preeditCaretPosY;
|
||||||
|
if (height)
|
||||||
|
*height = window->preeditCaretHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWAPI void glfwSetPreeditCaretPos(GLFWwindow* handle, int xpos, int ypos, int height)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
assert(window != NULL);
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
window->preeditCaretPosX = xpos;
|
||||||
|
window->preeditCaretPosY = ypos;
|
||||||
|
window->preeditCaretHeight = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWAPI void glfwResetPreeditText(GLFWwindow* handle)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
assert(window != NULL);
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
_glfwPlatformResetPreeditText(window);
|
||||||
|
}
|
||||||
|
|
||||||
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
@ -499,6 +571,22 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmods
|
|||||||
return cbfun;
|
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,
|
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle,
|
||||||
GLFWmousebuttonfun cbfun)
|
GLFWmousebuttonfun cbfun)
|
||||||
{
|
{
|
||||||
|
@ -370,9 +370,17 @@ struct _GLFWwindow
|
|||||||
int cursorMode;
|
int cursorMode;
|
||||||
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||||
char keys[GLFW_KEY_LAST + 1];
|
char keys[GLFW_KEY_LAST + 1];
|
||||||
|
|
||||||
// Virtual cursor position when cursor is disabled
|
// Virtual cursor position when cursor is disabled
|
||||||
double virtualCursorPosX, virtualCursorPosY;
|
double virtualCursorPosX, virtualCursorPosY;
|
||||||
|
|
||||||
|
// IME preedit data
|
||||||
|
unsigned int* preeditText;
|
||||||
|
int* preeditBlocks;
|
||||||
|
int preeditBlockCount;
|
||||||
|
int preeditCaretPosX, preeditCaretPosY;
|
||||||
|
int preeditCaretHeight;
|
||||||
|
|
||||||
_GLFWcontext context;
|
_GLFWcontext context;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -391,6 +399,8 @@ struct _GLFWwindow
|
|||||||
GLFWkeyfun key;
|
GLFWkeyfun key;
|
||||||
GLFWcharfun character;
|
GLFWcharfun character;
|
||||||
GLFWcharmodsfun charmods;
|
GLFWcharmodsfun charmods;
|
||||||
|
GLFWpreeditfun preedit;
|
||||||
|
GLFWimestatusfun imestatus;
|
||||||
GLFWdropfun drop;
|
GLFWdropfun drop;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
|
|
||||||
@ -818,6 +828,46 @@ int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhy
|
|||||||
*/
|
*/
|
||||||
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
|
VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface);
|
||||||
|
|
||||||
|
/*! @copydoc glfwResetPreeditText
|
||||||
|
* @ingroup platform
|
||||||
|
*/
|
||||||
|
void _glfwPlatformResetPreeditText(_GLFWwindow* window);
|
||||||
|
|
||||||
|
/*! @brief Sets whether the IME is enabled.
|
||||||
|
*
|
||||||
|
* This function sets whether the IME is enabled.
|
||||||
|
*
|
||||||
|
* @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
|
// Event API functions
|
||||||
@ -904,6 +954,19 @@ void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int m
|
|||||||
*/
|
*/
|
||||||
void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain);
|
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.
|
/*! @brief Notifies shared code of a scroll event.
|
||||||
* @param[in] window The window that received the event.
|
* @param[in] window The window that received the event.
|
||||||
* @param[in] xoffset The scroll offset along the x-axis.
|
* @param[in] xoffset The scroll offset along the x-axis.
|
||||||
|
@ -890,6 +890,25 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return err;
|
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 //////
|
////// GLFW native API //////
|
||||||
|
@ -301,3 +301,16 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return VK_ERROR_INITIALIZATION_FAILED;
|
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 <string.h>
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <imm.h>
|
||||||
|
|
||||||
#define _GLFW_KEY_INVALID -2
|
#define _GLFW_KEY_INVALID -2
|
||||||
|
|
||||||
@ -422,6 +423,17 @@ static void releaseMonitor(_GLFWwindow* window)
|
|||||||
_glfwRestoreVideoModeWin32(window->monitor);
|
_glfwRestoreVideoModeWin32(window->monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set cursor position to decide candidate window
|
||||||
|
//
|
||||||
|
static void updateCaretPosition(_GLFWwindow* window, HIMC imc)
|
||||||
|
{
|
||||||
|
const int x = window->preeditCaretPosX;
|
||||||
|
const int y = window->preeditCaretPosY;
|
||||||
|
const int h = window->preeditCaretHeight;
|
||||||
|
CANDIDATEFORM excludeRect = { 0, CFS_EXCLUDE, { x, y }, { x, y, x, y + h } };
|
||||||
|
ImmSetCandidateWindow(imc, &excludeRect);
|
||||||
|
}
|
||||||
|
|
||||||
// Window callback function (handles window messages)
|
// Window callback function (handles window messages)
|
||||||
//
|
//
|
||||||
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||||
@ -536,7 +548,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
_glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
|
_glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain);
|
||||||
return 0;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
@ -571,6 +583,81 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case WM_IME_COMPOSITION:
|
||||||
|
{
|
||||||
|
HIMC imc;
|
||||||
|
LONG textSize, attrSize, clauseSize;
|
||||||
|
int i, focusedBlock, length;
|
||||||
|
LPWSTR buffer;
|
||||||
|
LPSTR attributes;
|
||||||
|
DWORD* clauses;
|
||||||
|
|
||||||
|
if (lParam & GCS_RESULTSTR)
|
||||||
|
{
|
||||||
|
window->preeditBlockCount = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(lParam & GCS_COMPSTR))
|
||||||
|
break;
|
||||||
|
|
||||||
|
imc = ImmGetContext(hWnd);
|
||||||
|
textSize = ImmGetCompositionStringW(imc, GCS_COMPSTR, NULL, 0);
|
||||||
|
attrSize = ImmGetCompositionStringW(imc, GCS_COMPATTR, NULL, 0);
|
||||||
|
clauseSize = ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, NULL, 0);
|
||||||
|
|
||||||
|
if (textSize <= 0)
|
||||||
|
{
|
||||||
|
ImmReleaseContext(hWnd, imc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = textSize / sizeof(WCHAR);
|
||||||
|
buffer = calloc(length + 1, sizeof(WCHAR));
|
||||||
|
attributes = calloc(attrSize, 1);
|
||||||
|
clauses = calloc(clauseSize, 1);
|
||||||
|
|
||||||
|
ImmGetCompositionStringW(imc, GCS_COMPSTR, buffer, textSize);
|
||||||
|
ImmGetCompositionStringW(imc, GCS_COMPATTR, attributes, attrSize);
|
||||||
|
ImmGetCompositionStringW(imc, GCS_COMPCLAUSE, clauses, clauseSize);
|
||||||
|
|
||||||
|
free(window->preeditText);
|
||||||
|
window->preeditText = calloc(length + 1, sizeof(unsigned int));
|
||||||
|
memcpy(window->preeditText, buffer, sizeof(unsigned int) * length);
|
||||||
|
|
||||||
|
focusedBlock = 0;
|
||||||
|
|
||||||
|
window->preeditBlockCount = clauseSize / sizeof(DWORD) - 1;
|
||||||
|
free(window->preeditBlocks);
|
||||||
|
window->preeditBlocks = calloc(window->preeditBlockCount, sizeof(int));
|
||||||
|
|
||||||
|
for (i = 0; i < window->preeditBlockCount; i++)
|
||||||
|
{
|
||||||
|
window->preeditBlocks[i] = clauses[i + 1] - clauses[i];
|
||||||
|
if (attributes[clauses[i]] != ATTR_CONVERTED)
|
||||||
|
focusedBlock = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
free(attributes);
|
||||||
|
free(clauses);
|
||||||
|
|
||||||
|
_glfwInputPreedit(window, focusedBlock);
|
||||||
|
updateCaretPosition(window, imc);
|
||||||
|
|
||||||
|
ImmReleaseContext(hWnd, imc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_IME_NOTIFY:
|
||||||
|
{
|
||||||
|
if (wParam == IMN_SETOPENSTATUS)
|
||||||
|
_glfwInputIMEStatus(window);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case WM_LBUTTONDOWN:
|
case WM_LBUTTONDOWN:
|
||||||
case WM_RBUTTONDOWN:
|
case WM_RBUTTONDOWN:
|
||||||
case WM_MBUTTONDOWN:
|
case WM_MBUTTONDOWN:
|
||||||
@ -1705,6 +1792,28 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
HIMC imc = ImmGetContext(window->win32.handle);
|
||||||
|
ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||||
|
ImmReleaseContext(window->win32.handle, imc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||||
|
{
|
||||||
|
HIMC imc = ImmGetContext(window->win32.handle);
|
||||||
|
ImmSetOpenStatus(imc, active);
|
||||||
|
ImmReleaseContext(window->win32.handle, imc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
HIMC imc = ImmGetContext(window->win32.handle);
|
||||||
|
BOOL result = ImmGetOpenStatus(imc);
|
||||||
|
ImmReleaseContext(window->win32.handle, imc);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW native API //////
|
////// GLFW native API //////
|
||||||
|
@ -230,6 +230,10 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
|||||||
if (wndconfig.focused)
|
if (wndconfig.focused)
|
||||||
_glfwPlatformFocusWindow(window);
|
_glfwPlatformFocusWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window->preeditCaretPosX = 0;
|
||||||
|
window->preeditCaretPosY = height;
|
||||||
|
window->preeditCaretHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (GLFWwindow*) window;
|
return (GLFWwindow*) window;
|
||||||
@ -399,6 +403,9 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
|
|||||||
|
|
||||||
_glfwPlatformDestroyWindow(window);
|
_glfwPlatformDestroyWindow(window);
|
||||||
|
|
||||||
|
free(window->preeditText);
|
||||||
|
free(window->preeditBlocks);
|
||||||
|
|
||||||
// Unlink window from global linked list
|
// Unlink window from global linked list
|
||||||
{
|
{
|
||||||
_GLFWwindow** prev = &_glfw.windowListHead;
|
_GLFWwindow** prev = &_glfw.windowListHead;
|
||||||
|
@ -1027,6 +1027,28 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return err;
|
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 //////
|
////// GLFW native API //////
|
||||||
|
@ -128,6 +128,16 @@ typedef struct _GLFWwindowX11
|
|||||||
unsigned int lastKeyCode;
|
unsigned int lastKeyCode;
|
||||||
Time lastKeyTime;
|
Time lastKeyTime;
|
||||||
|
|
||||||
|
// Preedit callbacks
|
||||||
|
XIMCallback preeditStartCallback;
|
||||||
|
XIMCallback preeditDoneCallback;
|
||||||
|
XIMCallback preeditDrawCallback;
|
||||||
|
XIMCallback preeditCaretCallback;
|
||||||
|
XIMCallback statusStartCallback;
|
||||||
|
XIMCallback statusDoneCallback;
|
||||||
|
XIMCallback statusDrawCallback;
|
||||||
|
|
||||||
|
int imeFocus;
|
||||||
} _GLFWwindowX11;
|
} _GLFWwindowX11;
|
||||||
|
|
||||||
// X11-specific global data
|
// X11-specific global data
|
||||||
|
265
src/x11_window.c
265
src/x11_window.c
@ -48,6 +48,9 @@
|
|||||||
#define Button6 6
|
#define Button6 6
|
||||||
#define Button7 7
|
#define Button7 7
|
||||||
|
|
||||||
|
#if defined(X_HAVE_UTF8_STRING)
|
||||||
|
static unsigned int decodeUTF8(const char** s);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Wait for data to arrive using select
|
// Wait for data to arrive using select
|
||||||
// This avoids blocking other threads via the per-display Xlib lock that also
|
// This avoids blocking other threads via the per-display Xlib lock that also
|
||||||
@ -437,6 +440,218 @@ static char** parseUriList(char* text, int* count)
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update caret position to decide candidate window
|
||||||
|
//
|
||||||
|
static void updateCaretPosition(_GLFWwindow* window, XIC xic)
|
||||||
|
{
|
||||||
|
XVaNestedList attributes;
|
||||||
|
XPoint spot;
|
||||||
|
|
||||||
|
spot.x = window->preeditCursorPosX;
|
||||||
|
spot.y = window->preeditCursorPosY + window->preeditCursorHeight;
|
||||||
|
attributes = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
|
||||||
|
XSetICValues(xic, XNPreeditAttributes, attributes, NULL);
|
||||||
|
XFree(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IME start callback (do nothing)
|
||||||
|
//
|
||||||
|
static void preeditStartCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// IME done callback (do nothing)
|
||||||
|
//
|
||||||
|
static void preeditDoneCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// IME draw callback
|
||||||
|
//
|
||||||
|
static void preeditDrawCallback(XIC xic, XPointer clientData, XIMPreeditDrawCallbackStruct* callData)
|
||||||
|
{
|
||||||
|
int i, j, length, 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)
|
||||||
|
{
|
||||||
|
// Composition string is empty
|
||||||
|
window->preeditBlockCount = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = callData->text;
|
||||||
|
length = callData->chg_length;
|
||||||
|
if (text->encoding_is_wchar)
|
||||||
|
{
|
||||||
|
// wchar is not supported
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(window->preeditText);
|
||||||
|
window->preeditText = calloc(length + 1, sizeof(unsigned int));
|
||||||
|
|
||||||
|
if (!window->preeditBlocks)
|
||||||
|
window->preeditBlocks = calloc(4, sizeof(int));
|
||||||
|
|
||||||
|
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->preeditBlockCount = 1;
|
||||||
|
window->preeditBlocks[0] = length;
|
||||||
|
window->preeditBlocks[1] = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
}
|
||||||
|
else if (rstart == 0)
|
||||||
|
{
|
||||||
|
if (rend == length -1)
|
||||||
|
{
|
||||||
|
window->preeditBlockCount = 1;
|
||||||
|
window->preeditBlocks[0] = length;
|
||||||
|
window->preeditBlocks[1] = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window->preeditBlockCount = 2;
|
||||||
|
window->preeditBlocks[0] = rend + 1;
|
||||||
|
window->preeditBlocks[1] = length - rend - 1;
|
||||||
|
window->preeditBlocks[2] = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rend == length -1)
|
||||||
|
{
|
||||||
|
window->preeditBlockCount = 2;
|
||||||
|
window->preeditBlocks[0] = rstart;
|
||||||
|
window->preeditBlocks[1] = length - rstart;
|
||||||
|
window->preeditBlocks[2] = 0;
|
||||||
|
_glfwInputPreedit(window, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window->preeditBlockCount = 3;
|
||||||
|
window->preeditBlocks[0] = rstart;
|
||||||
|
window->preeditBlocks[1] = rend - rstart + 1;
|
||||||
|
window->preeditBlocks[2] = length - rend - 1;
|
||||||
|
window->preeditBlocks[3] = 0;
|
||||||
|
_glfwInputPreedit(window, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((caretX != window->preeditCaretPosX) ||
|
||||||
|
(caretY != window->preeditCaretPosY) ||
|
||||||
|
(caretHeight != window->preeditCaretHeight))
|
||||||
|
{
|
||||||
|
updateCaretPosition(window, xic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IME Caret callback (do nothing)
|
||||||
|
//
|
||||||
|
static void preeditCaretCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void statusStartCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) clientData;
|
||||||
|
window->x11.imeFocus = GLFW_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void statusDoneCallback(XIC xic, XPointer clientData, XPointer callData)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) clientData;
|
||||||
|
window->x11.imeFocus = GLFW_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void statusDrawCallback(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) preeditStartCallback;
|
||||||
|
window->x11.preeditDoneCallback.client_data = (XPointer)window;
|
||||||
|
window->x11.preeditDoneCallback.callback = (XIMProc) preeditDoneCallback;
|
||||||
|
window->x11.preeditDrawCallback.client_data = (XPointer)window;
|
||||||
|
window->x11.preeditDrawCallback.callback = (XIMProc) preeditDrawCallback;
|
||||||
|
window->x11.preeditCaretCallback.client_data = (XPointer)window;
|
||||||
|
window->x11.preeditCaretCallback.callback = (XIMProc) preeditCaretCallback;
|
||||||
|
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) statusStartCallback;
|
||||||
|
window->x11.statusDoneCallback.client_data = (XPointer)window;
|
||||||
|
window->x11.statusDoneCallback.callback = (XIMProc) statusDoneCallback;
|
||||||
|
window->x11.statusDrawCallback.client_data = (XPointer)window;
|
||||||
|
window->x11.statusDrawCallback.callback = (XIMProc) statusDrawCallback;
|
||||||
|
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
|
// Centers the cursor over the window client area
|
||||||
//
|
//
|
||||||
static void centerCursor(_GLFWwindow* window)
|
static void centerCursor(_GLFWwindow* window)
|
||||||
@ -646,14 +861,23 @@ static GLFWbool createNativeWindow(_GLFWwindow* window,
|
|||||||
|
|
||||||
if (_glfw.x11.im)
|
if (_glfw.x11.im)
|
||||||
{
|
{
|
||||||
|
XVaNestedList preeditList = _createXIMPreeditCallbacks(window);
|
||||||
|
XVaNestedList statusList = _createXIMStatusCallbacks(window);
|
||||||
window->x11.ic = XCreateIC(_glfw.x11.im,
|
window->x11.ic = XCreateIC(_glfw.x11.im,
|
||||||
XNInputStyle,
|
XNInputStyle,
|
||||||
XIMPreeditNothing | XIMStatusNothing,
|
XIMPreeditCallbacks | XIMStatusCallbacks,
|
||||||
XNClientWindow,
|
XNClientWindow,
|
||||||
window->x11.handle,
|
window->x11.handle,
|
||||||
XNFocusWindow,
|
XNFocusWindow,
|
||||||
window->x11.handle,
|
window->x11.handle,
|
||||||
|
XNPreeditAttributes,
|
||||||
|
preeditList,
|
||||||
|
XNStatusAttributes,
|
||||||
|
statusList,
|
||||||
NULL);
|
NULL);
|
||||||
|
XFree(preeditList);
|
||||||
|
XFree(statusList);
|
||||||
|
window->x11.imeFocus = GLFW_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
_glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
|
||||||
@ -2480,6 +2704,45 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformResetPreeditText(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
// Restore conversion state after resetting ic later
|
||||||
|
XIMPreeditState state = XIMPreeditUnKnown;
|
||||||
|
XVaNestedList attributes;
|
||||||
|
char* result;
|
||||||
|
|
||||||
|
if (*window->preeditText == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
attributes = XVaCreateNestedList(0, XNPreeditState, &state, NULL);
|
||||||
|
XGetICValues(window->x11.ic, XNPreeditAttributes, attributes, NULL);
|
||||||
|
XFree(attributes);
|
||||||
|
|
||||||
|
result = XmbResetIC(window->x11.ic);
|
||||||
|
|
||||||
|
attributes = XVaCreateNestedList(0, XNPreeditState, state, NULL);
|
||||||
|
XSetICValues(window->x11.ic, XNPreeditAttributes, attributes, NULL);
|
||||||
|
XFree(attributes);
|
||||||
|
|
||||||
|
window->preeditBlockCount = 0;
|
||||||
|
_glfwInputPreedit(window, 0);
|
||||||
|
|
||||||
|
XFree(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active)
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
XSetICFocus(window->x11.ic);
|
||||||
|
else
|
||||||
|
XUnsetICFocus(window->x11.ic);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _glfwPlatformGetIMEStatus(_GLFWwindow* window)
|
||||||
|
{
|
||||||
|
return window->x11.imeFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW native API //////
|
////// GLFW native API //////
|
||||||
|
@ -420,6 +420,57 @@ static void char_mods_callback(GLFWwindow* window, unsigned int codepoint, int m
|
|||||||
get_mods_name(mods));
|
get_mods_name(mods));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void preedit_callback(GLFWwindow* window,
|
||||||
|
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 (*string && blockLength)
|
||||||
|
{
|
||||||
|
for (i = 0; string[i]; 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);
|
||||||
|
glfwSetPreeditCaretPos(window, width / 2, height / 2, 20);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("(empty)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
static void drop_callback(GLFWwindow* window, int count, const char** paths)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -596,6 +647,8 @@ int main(int argc, char** argv)
|
|||||||
glfwSetKeyCallback(slots[i].window, key_callback);
|
glfwSetKeyCallback(slots[i].window, key_callback);
|
||||||
glfwSetCharCallback(slots[i].window, char_callback);
|
glfwSetCharCallback(slots[i].window, char_callback);
|
||||||
glfwSetCharModsCallback(slots[i].window, char_mods_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);
|
glfwSetDropCallback(slots[i].window, drop_callback);
|
||||||
|
|
||||||
glfwMakeContextCurrent(slots[i].window);
|
glfwMakeContextCurrent(slots[i].window);
|
||||||
|
Loading…
Reference in New Issue
Block a user