mirror of
https://github.com/glfw/glfw.git
synced 2025-10-04 13:46:37 +00:00
Merge d36a164423
into 3461d1c2a5
This commit is contained in:
commit
7f6227d85c
2
.gitignore
vendored
2
.gitignore
vendored
@ -82,4 +82,4 @@ tests/timeout
|
|||||||
tests/title
|
tests/title
|
||||||
tests/vulkan
|
tests/vulkan
|
||||||
tests/windows
|
tests/windows
|
||||||
|
tests/ime
|
||||||
|
@ -205,6 +205,7 @@ endif()
|
|||||||
if (_GLFW_WIN32)
|
if (_GLFW_WIN32)
|
||||||
|
|
||||||
list(APPEND glfw_PKG_LIBS "-lgdi32")
|
list(APPEND glfw_PKG_LIBS "-lgdi32")
|
||||||
|
list(APPEND glfw_LIBRARIES "imm32")
|
||||||
|
|
||||||
if (GLFW_USE_HYBRID_HPG)
|
if (GLFW_USE_HYBRID_HPG)
|
||||||
set(_GLFW_USE_HYBRID_HPG 1)
|
set(_GLFW_USE_HYBRID_HPG 1)
|
||||||
@ -298,12 +299,13 @@ if (_GLFW_COCOA)
|
|||||||
|
|
||||||
list(APPEND glfw_LIBRARIES
|
list(APPEND glfw_LIBRARIES
|
||||||
"-framework Cocoa"
|
"-framework Cocoa"
|
||||||
|
"-framework Carbon"
|
||||||
"-framework IOKit"
|
"-framework IOKit"
|
||||||
"-framework CoreFoundation"
|
"-framework CoreFoundation"
|
||||||
"-framework CoreVideo")
|
"-framework CoreVideo")
|
||||||
|
|
||||||
set(glfw_PKG_DEPS "")
|
set(glfw_PKG_DEPS "")
|
||||||
set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo")
|
set(glfw_PKG_LIBS "-framework Cocoa -framework Carbon -framework IOKit -framework CoreFoundation -framework CoreVideo")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
@ -214,6 +214,96 @@ void character_callback(GLFWwindow* window, unsigned int codepoint)
|
|||||||
}
|
}
|
||||||
@endcode
|
@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
|
@subsection input_key_name Key names
|
||||||
|
|
||||||
|
@ -1018,6 +1018,7 @@ extern "C" {
|
|||||||
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
|
#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003
|
||||||
#define GLFW_LOCK_KEY_MODS 0x00033004
|
#define GLFW_LOCK_KEY_MODS 0x00033004
|
||||||
#define GLFW_RAW_MOUSE_MOTION 0x00033005
|
#define GLFW_RAW_MOUSE_MOTION 0x00033005
|
||||||
|
#define GLFW_IME 0x00033006
|
||||||
|
|
||||||
#define GLFW_CURSOR_NORMAL 0x00034001
|
#define GLFW_CURSOR_NORMAL 0x00034001
|
||||||
#define GLFW_CURSOR_HIDDEN 0x00034002
|
#define GLFW_CURSOR_HIDDEN 0x00034002
|
||||||
@ -1473,6 +1474,37 @@ 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] 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.
|
/*! @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.
|
||||||
@ -3864,13 +3896,13 @@ GLFWAPI void glfwPostEmptyEvent(void);
|
|||||||
*
|
*
|
||||||
* 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 @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
|
* The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
|
||||||
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
|
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS,
|
||||||
* @ref GLFW_RAW_MOUSE_MOTION.
|
* @ref GLFW_RAW_MOUSE_MOTION or @ref 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`,
|
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
|
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS`
|
||||||
* `GLFW_RAW_MOUSE_MOTION`.
|
* `GLFW_RAW_MOUSE_MOTION` 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.
|
||||||
@ -3889,8 +3921,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
|||||||
*
|
*
|
||||||
* 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 @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
|
* must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS,
|
||||||
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or
|
* @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS,
|
||||||
* @ref GLFW_RAW_MOUSE_MOTION.
|
* @ref GLFW_RAW_MOUSE_MOTION or @ref 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:
|
||||||
@ -3928,10 +3960,13 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode);
|
|||||||
* attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref
|
* attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref
|
||||||
* glfwRawMouseMotionSupported to check for support.
|
* glfwRawMouseMotionSupported to check for support.
|
||||||
*
|
*
|
||||||
|
* 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`,
|
* @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`,
|
||||||
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or
|
* `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS`,
|
||||||
* `GLFW_RAW_MOUSE_MOTION`.
|
* `GLFW_RAW_MOUSE_MOTION` 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
|
||||||
@ -4328,6 +4363,67 @@ 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 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.
|
/*! @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
|
||||||
@ -4442,6 +4538,58 @@ 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.
|
||||||
|
*
|
||||||
|
* @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.
|
/*! @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
|
||||||
|
@ -327,8 +327,11 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
_glfwUpdateDisplayLinkDisplayNSGL(window);
|
_glfwUpdateDisplayLinkDisplayNSGL(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
- (void)imeStatusChangeNotified:(NSNotification *)notification {
|
||||||
|
_glfwInputIMEStatus(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
// Content view class for the GLFW window
|
// Content view class for the GLFW window
|
||||||
@ -687,6 +690,56 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
|
|||||||
markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||||
else
|
else
|
||||||
markedText = [[NSMutableAttributedString alloc] initWithString:string];
|
markedText = [[NSMutableAttributedString alloc] 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
|
- (void)unmarkText
|
||||||
@ -934,6 +987,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
|||||||
acquireMonitor(window);
|
acquireMonitor(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver: window->ns.delegate
|
||||||
|
selector:@selector(imeStatusChangeNotified:)
|
||||||
|
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||||
|
object: nil];
|
||||||
return GLFW_TRUE;
|
return GLFW_TRUE;
|
||||||
|
|
||||||
} // autoreleasepool
|
} // autoreleasepool
|
||||||
@ -946,6 +1004,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)
|
||||||
@ -1754,6 +1814,49 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
} // autoreleasepool
|
} // autoreleasepool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 //////
|
////// GLFW native API //////
|
||||||
|
62
src/input.c
62
src/input.c
@ -305,6 +305,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Notifies shared code of a scroll event
|
// Notifies shared code of a scroll event
|
||||||
//
|
//
|
||||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset)
|
||||||
@ -487,7 +501,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
|
|||||||
return window->lockKeyMods;
|
return window->lockKeyMods;
|
||||||
case GLFW_RAW_MOUSE_MOTION:
|
case GLFW_RAW_MOUSE_MOTION:
|
||||||
return window->rawMouseMotion;
|
return window->rawMouseMotion;
|
||||||
}
|
case GLFW_IME:
|
||||||
|
return _glfwPlatformGetIMEStatus(window); }
|
||||||
|
|
||||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||||
return 0;
|
return 0;
|
||||||
@ -582,7 +597,10 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
|||||||
window->rawMouseMotion = value;
|
window->rawMouseMotion = value;
|
||||||
_glfwPlatformSetRawMouseMotion(window, value);
|
_glfwPlatformSetRawMouseMotion(window, value);
|
||||||
}
|
}
|
||||||
else
|
else if (mode == GLFW_IME)
|
||||||
|
{
|
||||||
|
_glfwPlatformSetIMEStatus(window, value ? GLFW_TRUE : GLFW_FALSE);
|
||||||
|
} else
|
||||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -824,6 +842,30 @@ GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle)
|
|||||||
_glfwPlatformSetCursor(window, cursor);
|
_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)
|
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
@ -854,6 +896,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)
|
||||||
{
|
{
|
||||||
|
@ -392,6 +392,15 @@ struct _GLFWwindow
|
|||||||
double virtualCursorPosX, virtualCursorPosY;
|
double virtualCursorPosX, virtualCursorPosY;
|
||||||
GLFWbool rawMouseMotion;
|
GLFWbool rawMouseMotion;
|
||||||
|
|
||||||
|
// Preedit texts
|
||||||
|
unsigned int* preeditText;
|
||||||
|
int ntext;
|
||||||
|
int ctext;
|
||||||
|
int* preeditAttributeBlocks;
|
||||||
|
int nblocks;
|
||||||
|
int cblocks;
|
||||||
|
int preeditCursorPosX, preeditCursorPosY, preeditCursorHeight;
|
||||||
|
|
||||||
_GLFWcontext context;
|
_GLFWcontext context;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -411,6 +420,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;
|
||||||
|
|
||||||
@ -716,6 +727,8 @@ void _glfwInputKey(_GLFWwindow* window,
|
|||||||
int key, int scancode, int action, int mods);
|
int key, int scancode, int action, int mods);
|
||||||
void _glfwInputChar(_GLFWwindow* window,
|
void _glfwInputChar(_GLFWwindow* window,
|
||||||
unsigned int codepoint, int mods, GLFWbool plain);
|
unsigned int codepoint, int mods, GLFWbool plain);
|
||||||
|
void _glfwInputPreedit(_GLFWwindow* window, int focusedBlock);
|
||||||
|
void _glfwInputIMEStatus(_GLFWwindow* window);
|
||||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset);
|
||||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||||
@ -736,6 +749,9 @@ void _glfwInputError(int code, const char* format, ...)
|
|||||||
void _glfwInputError(int code, const char* format, ...);
|
void _glfwInputError(int code, const char* format, ...);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void _glfwPlatformResetPreeditText(_GLFWwindow* window);
|
||||||
|
void _glfwPlatformSetIMEStatus(_GLFWwindow* window, int active);
|
||||||
|
int _glfwPlatformGetIMEStatus(_GLFWwindow* window);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW internal API //////
|
////// GLFW internal API //////
|
||||||
|
@ -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
|
||||||
|
|
||||||
@ -574,6 +575,15 @@ static void releaseMonitor(_GLFWwindow* window)
|
|||||||
_glfwRestoreVideoModeWin32(window->monitor);
|
_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)
|
// Window callback function (handles window messages)
|
||||||
//
|
//
|
||||||
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
||||||
@ -733,7 +743,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:
|
||||||
@ -768,7 +778,93 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
|
|
||||||
break;
|
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_LBUTTONDOWN:
|
||||||
case WM_RBUTTONDOWN:
|
case WM_RBUTTONDOWN:
|
||||||
case WM_MBUTTONDOWN:
|
case WM_MBUTTONDOWN:
|
||||||
@ -2229,6 +2325,30 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
|||||||
return err;
|
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 //////
|
////// GLFW native API //////
|
||||||
|
12
src/window.c
12
src/window.c
@ -230,6 +230,9 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
|||||||
{
|
{
|
||||||
if (wndconfig.centerCursor)
|
if (wndconfig.centerCursor)
|
||||||
_glfwCenterCursorInContentArea(window);
|
_glfwCenterCursorInContentArea(window);
|
||||||
|
window->preeditCursorPosX = 0;
|
||||||
|
window->preeditCursorPosY = height;
|
||||||
|
window->preeditCursorHeight = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -239,6 +242,9 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
|||||||
if (wndconfig.focused)
|
if (wndconfig.focused)
|
||||||
_glfwPlatformFocusWindow(window);
|
_glfwPlatformFocusWindow(window);
|
||||||
}
|
}
|
||||||
|
window->preeditCursorPosX = 0;
|
||||||
|
window->preeditCursorPosY = height;
|
||||||
|
window->preeditCursorHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (GLFWwindow*) window;
|
return (GLFWwindow*) window;
|
||||||
@ -465,7 +471,11 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* handle)
|
|||||||
|
|
||||||
*prev = window->next;
|
*prev = window->next;
|
||||||
}
|
}
|
||||||
|
// Clear memory for preedit text
|
||||||
|
if (window->preeditText)
|
||||||
|
free(window->preeditText);
|
||||||
|
if (window->preeditAttributeBlocks)
|
||||||
|
free(window->preeditAttributeBlocks);
|
||||||
free(window);
|
free(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +201,16 @@ typedef struct _GLFWwindowX11
|
|||||||
// The time of the last KeyPress event
|
// The time of the last KeyPress event
|
||||||
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
|
||||||
|
244
src/x11_window.c
244
src/x11_window.c
@ -50,6 +50,9 @@
|
|||||||
|
|
||||||
#define _GLFW_XDND_VERSION 5
|
#define _GLFW_XDND_VERSION 5
|
||||||
|
|
||||||
|
#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
|
||||||
@ -587,6 +590,196 @@ static void enableCursor(_GLFWwindow* window)
|
|||||||
updateCursorImage(window);
|
updateCursorImage(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the X11 window (and its colormap)
|
// Create the X11 window (and its colormap)
|
||||||
//
|
//
|
||||||
static GLFWbool createNativeWindow(_GLFWwindow* window,
|
static GLFWbool createNativeWindow(_GLFWwindow* window,
|
||||||
@ -774,14 +967,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);
|
||||||
@ -3046,6 +3248,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 //////
|
////// GLFW native API //////
|
||||||
|
@ -26,6 +26,7 @@ add_executable(iconify iconify.c ${GETOPT} ${GLAD})
|
|||||||
add_executable(monitors monitors.c ${GETOPT} ${GLAD})
|
add_executable(monitors monitors.c ${GETOPT} ${GLAD})
|
||||||
add_executable(reopen reopen.c ${GLAD})
|
add_executable(reopen reopen.c ${GLAD})
|
||||||
add_executable(cursor cursor.c ${GLAD})
|
add_executable(cursor cursor.c ${GLAD})
|
||||||
|
add_executable(ime ime.c ${GETOPT} ${GLAD})
|
||||||
|
|
||||||
add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD})
|
add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD})
|
||||||
add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD})
|
add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD})
|
||||||
@ -49,7 +50,7 @@ endif()
|
|||||||
set(WINDOWS_BINARIES empty gamma icon inputlag joysticks opacity tearing
|
set(WINDOWS_BINARIES empty gamma icon inputlag joysticks opacity tearing
|
||||||
threads timeout title windows)
|
threads timeout title windows)
|
||||||
set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen
|
set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen
|
||||||
cursor)
|
cursor ime)
|
||||||
|
|
||||||
if (VULKAN_FOUND)
|
if (VULKAN_FOUND)
|
||||||
add_executable(vulkan WIN32 vulkan.c ${ICON})
|
add_executable(vulkan WIN32 vulkan.c ${ICON})
|
||||||
|
@ -429,6 +429,45 @@ static void char_callback(GLFWwindow* window, unsigned int codepoint)
|
|||||||
get_character_string(codepoint));
|
get_character_string(codepoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
static void drop_callback(GLFWwindow* window, int count, const char** paths)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -601,6 +640,8 @@ int main(int argc, char** argv)
|
|||||||
glfwSetScrollCallback(slots[i].window, scroll_callback);
|
glfwSetScrollCallback(slots[i].window, scroll_callback);
|
||||||
glfwSetKeyCallback(slots[i].window, key_callback);
|
glfwSetKeyCallback(slots[i].window, key_callback);
|
||||||
glfwSetCharCallback(slots[i].window, char_callback);
|
glfwSetCharCallback(slots[i].window, char_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);
|
||||||
|
508
tests/ime.c
Normal file
508
tests/ime.c
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
//========================================================================
|
||||||
|
// Event linter (event spewer)
|
||||||
|
// Copyright (c) Camilla Berglund <elmindreda@elmindreda.org>
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied
|
||||||
|
// warranty. In no event will the authors be held liable for any damages
|
||||||
|
// arising from the use of this software.
|
||||||
|
//
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it
|
||||||
|
// freely, subject to the following restrictions:
|
||||||
|
//
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
// claim that you wrote the original software. If you use this software
|
||||||
|
// in a product, an acknowledgment in the product documentation would
|
||||||
|
// be appreciated but is not required.
|
||||||
|
//
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not
|
||||||
|
// be misrepresented as being the original software.
|
||||||
|
//
|
||||||
|
// 3. This notice may not be removed or altered from any source
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
//========================================================================
|
||||||
|
//
|
||||||
|
// This test hooks every available callback and outputs their arguments
|
||||||
|
//
|
||||||
|
// Log messages go to stdout, error messages to stderr
|
||||||
|
//
|
||||||
|
// Every event also gets a (sequential) number to aid discussion of logs
|
||||||
|
//
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#include "getopt.h"
|
||||||
|
|
||||||
|
// Event index
|
||||||
|
static unsigned int counter = 0;
|
||||||
|
|
||||||
|
// Mouse position
|
||||||
|
static int posX = 0, posY = 0;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GLFWwindow* window;
|
||||||
|
int number;
|
||||||
|
int closeable;
|
||||||
|
} Slot;
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
printf("Usage: ime [-f] [-h] [-n WINDOWS]\n");
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" -f use full screen\n");
|
||||||
|
printf(" -h show this help\n");
|
||||||
|
printf(" -n the number of windows to create\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* get_key_name(int key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
// Printable keys
|
||||||
|
case GLFW_KEY_A: return "A";
|
||||||
|
case GLFW_KEY_B: return "B";
|
||||||
|
case GLFW_KEY_C: return "C";
|
||||||
|
case GLFW_KEY_D: return "D";
|
||||||
|
case GLFW_KEY_E: return "E";
|
||||||
|
case GLFW_KEY_F: return "F";
|
||||||
|
case GLFW_KEY_G: return "G";
|
||||||
|
case GLFW_KEY_H: return "H";
|
||||||
|
case GLFW_KEY_I: return "I";
|
||||||
|
case GLFW_KEY_J: return "J";
|
||||||
|
case GLFW_KEY_K: return "K";
|
||||||
|
case GLFW_KEY_L: return "L";
|
||||||
|
case GLFW_KEY_M: return "M";
|
||||||
|
case GLFW_KEY_N: return "N";
|
||||||
|
case GLFW_KEY_O: return "O";
|
||||||
|
case GLFW_KEY_P: return "P";
|
||||||
|
case GLFW_KEY_Q: return "Q";
|
||||||
|
case GLFW_KEY_R: return "R";
|
||||||
|
case GLFW_KEY_S: return "S";
|
||||||
|
case GLFW_KEY_T: return "T";
|
||||||
|
case GLFW_KEY_U: return "U";
|
||||||
|
case GLFW_KEY_V: return "V";
|
||||||
|
case GLFW_KEY_W: return "W";
|
||||||
|
case GLFW_KEY_X: return "X";
|
||||||
|
case GLFW_KEY_Y: return "Y";
|
||||||
|
case GLFW_KEY_Z: return "Z";
|
||||||
|
case GLFW_KEY_1: return "1";
|
||||||
|
case GLFW_KEY_2: return "2";
|
||||||
|
case GLFW_KEY_3: return "3";
|
||||||
|
case GLFW_KEY_4: return "4";
|
||||||
|
case GLFW_KEY_5: return "5";
|
||||||
|
case GLFW_KEY_6: return "6";
|
||||||
|
case GLFW_KEY_7: return "7";
|
||||||
|
case GLFW_KEY_8: return "8";
|
||||||
|
case GLFW_KEY_9: return "9";
|
||||||
|
case GLFW_KEY_0: return "0";
|
||||||
|
case GLFW_KEY_SPACE: return "SPACE";
|
||||||
|
case GLFW_KEY_MINUS: return "MINUS";
|
||||||
|
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||||
|
case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
|
||||||
|
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
|
||||||
|
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||||
|
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||||
|
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||||
|
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
|
||||||
|
case GLFW_KEY_COMMA: return "COMMA";
|
||||||
|
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||||
|
case GLFW_KEY_SLASH: return "SLASH";
|
||||||
|
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||||
|
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||||
|
case GLFW_KEY_F1: return "F1";
|
||||||
|
case GLFW_KEY_F2: return "F2";
|
||||||
|
case GLFW_KEY_F3: return "F3";
|
||||||
|
case GLFW_KEY_F4: return "F4";
|
||||||
|
case GLFW_KEY_F5: return "F5";
|
||||||
|
case GLFW_KEY_F6: return "F6";
|
||||||
|
case GLFW_KEY_F7: return "F7";
|
||||||
|
case GLFW_KEY_F8: return "F8";
|
||||||
|
case GLFW_KEY_F9: return "F9";
|
||||||
|
case GLFW_KEY_F10: return "F10";
|
||||||
|
case GLFW_KEY_F11: return "F11";
|
||||||
|
case GLFW_KEY_F12: return "F12";
|
||||||
|
case GLFW_KEY_F13: return "F13";
|
||||||
|
case GLFW_KEY_F14: return "F14";
|
||||||
|
case GLFW_KEY_F15: return "F15";
|
||||||
|
case GLFW_KEY_F16: return "F16";
|
||||||
|
case GLFW_KEY_F17: return "F17";
|
||||||
|
case GLFW_KEY_F18: return "F18";
|
||||||
|
case GLFW_KEY_F19: return "F19";
|
||||||
|
case GLFW_KEY_F20: return "F20";
|
||||||
|
case GLFW_KEY_F21: return "F21";
|
||||||
|
case GLFW_KEY_F22: return "F22";
|
||||||
|
case GLFW_KEY_F23: return "F23";
|
||||||
|
case GLFW_KEY_F24: return "F24";
|
||||||
|
case GLFW_KEY_F25: return "F25";
|
||||||
|
case GLFW_KEY_UP: return "UP";
|
||||||
|
case GLFW_KEY_DOWN: return "DOWN";
|
||||||
|
case GLFW_KEY_LEFT: return "LEFT";
|
||||||
|
case GLFW_KEY_RIGHT: return "RIGHT";
|
||||||
|
case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
|
||||||
|
case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
|
||||||
|
case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
|
||||||
|
case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
|
||||||
|
case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
|
||||||
|
case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
|
||||||
|
case GLFW_KEY_TAB: return "TAB";
|
||||||
|
case GLFW_KEY_ENTER: return "ENTER";
|
||||||
|
case GLFW_KEY_BACKSPACE: return "BACKSPACE";
|
||||||
|
case GLFW_KEY_INSERT: return "INSERT";
|
||||||
|
case GLFW_KEY_DELETE: return "DELETE";
|
||||||
|
case GLFW_KEY_PAGE_UP: return "PAGE UP";
|
||||||
|
case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
|
||||||
|
case GLFW_KEY_HOME: return "HOME";
|
||||||
|
case GLFW_KEY_END: return "END";
|
||||||
|
case GLFW_KEY_KP_0: return "KEYPAD 0";
|
||||||
|
case GLFW_KEY_KP_1: return "KEYPAD 1";
|
||||||
|
case GLFW_KEY_KP_2: return "KEYPAD 2";
|
||||||
|
case GLFW_KEY_KP_3: return "KEYPAD 3";
|
||||||
|
case GLFW_KEY_KP_4: return "KEYPAD 4";
|
||||||
|
case GLFW_KEY_KP_5: return "KEYPAD 5";
|
||||||
|
case GLFW_KEY_KP_6: return "KEYPAD 6";
|
||||||
|
case GLFW_KEY_KP_7: return "KEYPAD 7";
|
||||||
|
case GLFW_KEY_KP_8: return "KEYPAD 8";
|
||||||
|
case GLFW_KEY_KP_9: return "KEYPAD 9";
|
||||||
|
case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
|
||||||
|
case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY";
|
||||||
|
case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
|
||||||
|
case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
|
||||||
|
case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
|
||||||
|
case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
|
||||||
|
case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
|
||||||
|
case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
|
||||||
|
case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
|
||||||
|
case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
|
||||||
|
case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
|
||||||
|
case GLFW_KEY_PAUSE: return "PAUSE";
|
||||||
|
case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
|
||||||
|
case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
|
||||||
|
case GLFW_KEY_MENU: return "MENU";
|
||||||
|
|
||||||
|
default: return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* get_action_name(int action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GLFW_PRESS:
|
||||||
|
return "pressed";
|
||||||
|
case GLFW_RELEASE:
|
||||||
|
return "released";
|
||||||
|
case GLFW_REPEAT:
|
||||||
|
return "repeated";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "caused unknown action";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* get_mods_name(int mods)
|
||||||
|
{
|
||||||
|
static char name[512];
|
||||||
|
|
||||||
|
if (mods == 0)
|
||||||
|
return " no mods";
|
||||||
|
|
||||||
|
name[0] = '\0';
|
||||||
|
|
||||||
|
if (mods & GLFW_MOD_SHIFT)
|
||||||
|
strcat(name, " shift");
|
||||||
|
if (mods & GLFW_MOD_CONTROL)
|
||||||
|
strcat(name, " control");
|
||||||
|
if (mods & GLFW_MOD_ALT)
|
||||||
|
strcat(name, " alt");
|
||||||
|
if (mods & GLFW_MOD_SUPER)
|
||||||
|
strcat(name, " super");
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* get_character_string(int codepoint)
|
||||||
|
{
|
||||||
|
// This assumes UTF-8, which is stupid
|
||||||
|
static char result[6 + 1];
|
||||||
|
|
||||||
|
int length = wctomb(result, codepoint);
|
||||||
|
if (length == -1)
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
result[length] = '\0';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error_callback(int error, const char* description)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: %s\n", description);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
|
||||||
|
{
|
||||||
|
Slot* slot = glfwGetWindowUserPointer(window);
|
||||||
|
int currentIMEstatus;
|
||||||
|
int x, y;
|
||||||
|
if (action == GLFW_PRESS) {
|
||||||
|
if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||||
|
currentIMEstatus = glfwGetInputMode(window, GLFW_IME);
|
||||||
|
glfwSetInputMode(window, GLFW_IME, 1-currentIMEstatus);
|
||||||
|
glfwResetPreeditText(window);
|
||||||
|
printf("%08x to %i at %0.3f: Reset preedit text and IME status -> %s\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), currentIMEstatus ? "OFF" : "ON");
|
||||||
|
} else if (button == GLFW_MOUSE_BUTTON_RIGHT) {
|
||||||
|
glfwGetPreeditCursorPos(window, &x, &y, NULL);
|
||||||
|
glfwSetPreeditCursorPos(window, posX, posY, 20);
|
||||||
|
printf("%08x to %i at %0.3f: Move preedit text cursor position (%d, %d) -> (%d, %d)\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), x, y, posX, posY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cursor_position_callback(GLFWwindow* window, double x, double y)
|
||||||
|
{
|
||||||
|
posX = x;
|
||||||
|
posY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||||
|
{
|
||||||
|
Slot* slot = glfwGetWindowUserPointer(window);
|
||||||
|
const char* name = glfwGetKeyName(key, scancode);
|
||||||
|
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), key, scancode,
|
||||||
|
get_key_name(key),
|
||||||
|
name,
|
||||||
|
get_mods_name(mods),
|
||||||
|
get_action_name(action));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), key, scancode,
|
||||||
|
get_key_name(key),
|
||||||
|
get_mods_name(mods),
|
||||||
|
get_action_name(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action != GLFW_PRESS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case GLFW_KEY_C:
|
||||||
|
{
|
||||||
|
slot->closeable = !slot->closeable;
|
||||||
|
|
||||||
|
printf("(( closing %s ))\n", slot->closeable ? "enabled" : "disabled");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char_callback(GLFWwindow* window, unsigned int codepoint)
|
||||||
|
{
|
||||||
|
Slot* slot = glfwGetWindowUserPointer(window);
|
||||||
|
printf("%08x to %i at %0.3f: Character 0x%08x (%s) input\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), codepoint,
|
||||||
|
get_character_string(codepoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void char_mods_callback(GLFWwindow* window, unsigned int codepoint, int mods)
|
||||||
|
{
|
||||||
|
Slot* slot = glfwGetWindowUserPointer(window);
|
||||||
|
printf("%08x to %i at %0.3f: Character 0x%08x (%s) with modifiers (with%s) input\n",
|
||||||
|
counter++, slot->number, glfwGetTime(), codepoint,
|
||||||
|
get_character_string(codepoint),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ime_callback(GLFWwindow* window) {
|
||||||
|
Slot* slot = glfwGetWindowUserPointer(window);
|
||||||
|
printf("%08x to %i at %0.3f: IME switched\n",
|
||||||
|
counter++, slot->number, glfwGetTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
Slot* slots;
|
||||||
|
GLFWmonitor* monitor = NULL;
|
||||||
|
int ch, i, width, height, count = 1;
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
|
||||||
|
glfwSetErrorCallback(error_callback);
|
||||||
|
|
||||||
|
if (!glfwInit())
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
printf("Library initialized\n");
|
||||||
|
|
||||||
|
while ((ch = getopt(argc, argv, "hfn:")) != -1)
|
||||||
|
{
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
monitor = glfwGetPrimaryMonitor();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
count = (int) strtol(optarg, NULL, 10);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor)
|
||||||
|
{
|
||||||
|
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
|
||||||
|
glfwWindowHint(GLFW_RED_BITS, mode->redBits);
|
||||||
|
glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
|
||||||
|
glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
|
||||||
|
|
||||||
|
width = mode->width;
|
||||||
|
height = mode->height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = 640;
|
||||||
|
height = 480;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Invalid user\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
slots = calloc(count, sizeof(Slot));
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
char title[128];
|
||||||
|
|
||||||
|
slots[i].closeable = GLFW_TRUE;
|
||||||
|
slots[i].number = i + 1;
|
||||||
|
|
||||||
|
sprintf(title, "Event Linter (Window %i)", slots[i].number);
|
||||||
|
|
||||||
|
if (monitor)
|
||||||
|
{
|
||||||
|
printf("Creating full screen window %i (%ix%i on %s)\n",
|
||||||
|
slots[i].number,
|
||||||
|
width, height,
|
||||||
|
glfwGetMonitorName(monitor));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Creating windowed mode window %i (%ix%i)\n",
|
||||||
|
slots[i].number,
|
||||||
|
width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
slots[i].window = glfwCreateWindow(width, height, title, monitor, NULL);
|
||||||
|
if (!slots[i].window)
|
||||||
|
{
|
||||||
|
free(slots);
|
||||||
|
glfwTerminate();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwSetWindowUserPointer(slots[i].window, slots + i);
|
||||||
|
|
||||||
|
glfwSetMouseButtonCallback(slots[i].window, mouse_button_callback);
|
||||||
|
glfwSetCursorPosCallback(slots[i].window, cursor_position_callback);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(slots[i].window);
|
||||||
|
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||||
|
glfwSwapInterval(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Main loop starting\n");
|
||||||
|
printf("Left Mouse Button: toggle IME\n");
|
||||||
|
printf("Right Mouse Button: set preedit cursor position\n\n");
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (glfwWindowShouldClose(slots[i].window))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
glfwWaitEvents();
|
||||||
|
|
||||||
|
// Workaround for an issue with msvcrt and mintty
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(slots);
|
||||||
|
glfwTerminate();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user