vietnam sublime text users too.
any news?
Input method support
just tried today, still not work (2.2216)
please check #2 hint for how to fix it. Most gtk im module implementation donât send keyboard event if widget donât have focus.
Since user âcsslayerâ gives so many hints how to solve this problem, Iâm wondering why this couldnât be fixed yet.
Itâs really an important issue for most CJK users.
Still not solved.
build 2217 with fcitxâŚ
it seems that it can not call the fcitx by the shortcut with Ctrl-Space. but change the fcitxâs shortcut from C-S to another. Problem remainsâŚ
input methods(seems all 3rd party or system intergrated ) canât follow cursor position in microsoft windows 7 and xp.
Jon, please consider fix this problem, every cjk user depend on input methods
Still not works in CJK with ibus
I hope this can be solved as soon as possible.
By the way, there is a package named âInputHelperâ which can temporarily solve this issue. (Though itâs not quite convenient)
Iâm a sublime text2 user from China, it works fine in my windows, but in my fedora i canât input any Chinese words , itâs important for CJK user, please fix it.
This is a dirty fix but at least works. cursor position update also supported.
Use LD_PRELOAD to reimplement gtk_im_context_set_client_window and set im focus in.
use âgdk_region_get_clipboxâ to catch the caret position. (Itâs really difficult to find which function can catch the positionâŚ)
Here I made a assumption that the caret width is always 2, since it is 2.
the height is the âfont glyph heightâ.
1, save below code to sublime_imfix.c
/*
sublime-imfix.c
Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
By Cjacker Huang <jianzhong.huang at i-soft.com.cn>
gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
LD_PRELOAD=./libsublime-imfix.so sublime_text
*/
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
typedef GdkSegment GdkRegionBox;
struct _GdkRegion
{
long size;
long numRects;
GdkRegionBox *rects;
GdkRegionBox extents;
};
GtkIMContext *local_context;
void
gdk_region_get_clipbox (const GdkRegion *region,
GdkRectangle *rectangle)
{
g_return_if_fail (region != NULL);
g_return_if_fail (rectangle != NULL);
rectangle->x = region->extents.x1;
rectangle->y = region->extents.y1;
rectangle->width = region->extents.x2 - region->extents.x1;
rectangle->height = region->extents.y2 - region->extents.y1;
GdkRectangle rect;
rect.x = rectangle->x;
rect.y = rectangle->y;
rect.width = 0;
rect.height = rectangle->height;
//The caret width is 2;
//Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
if(rectangle->width == 2 && GTK_IS_IM_CONTEXT(local_context)) {
gtk_im_context_set_cursor_location(local_context, rectangle);
}
}
//this is needed, for example, if you input something in file dialog and return back the edit area
//context will lost, so here we set it again.
static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context)
{
XEvent *xev = (XEvent *)xevent;
if(xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
GdkWindow * win = g_object_get_data(G_OBJECT(im_context),"window");
if(GDK_IS_WINDOW(win))
gtk_im_context_set_client_window(im_context, win);
}
return GDK_FILTER_CONTINUE;
}
void gtk_im_context_set_client_window (GtkIMContext *context,
GdkWindow *window)
{
GtkIMContextClass *klass;
g_return_if_fail (GTK_IS_IM_CONTEXT (context));
klass = GTK_IM_CONTEXT_GET_CLASS (context);
if (klass->set_client_window)
klass->set_client_window (context, window);
if(!GDK_IS_WINDOW (window))
return;
g_object_set_data(G_OBJECT(context),"window",window);
int width = gdk_window_get_width(window);
int height = gdk_window_get_height(window);
if(width != 0 && height !=0) {
gtk_im_context_focus_in(context);
local_context = context;
}
gdk_window_add_filter (window, event_filter, context);
}
2, compile a shared library.
gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
3, LD_PRELOAD it
LD_PRELOAD=./libsublime-imfix.so sublime_text
[quote=âcjackerâ]This is a dirty fix but at least works. cursor location update still not works, since I really can not find a proper way to get the current caret loation relative to
GdkWidow, it seems sublime use a customized GtkWidget? I am not sure.
âŚ
[/quote]
Iâve tested this in Archlinux with sublime version 2.0.1-2, It works quite well.
Thanks for your excellent job!!
Iâve tested this in Archlinux with sublime version 2.0.1-2, It works quite well.
Thanks for your excellent job!![/quote]
original post had been updated to support cursor location update, please try it.
Followed all of cjackerâs steps but they didnât seem to work for me. No errors or anything, I just still donât get anything from the IME. Even if I turn it on manurally, when I type the characters are still English, not the Japanese characters from the IME.
[quote=âcjackerâ]This is a dirty fix but at least works. cursor position update also supported.
Use LD_PRELOAD to reimplement gtk_im_context_set_client_window and set im focus in.
use âgdk_region_get_clipboxâ to catch the caret position. (Itâs really difficult to find which function can catch the positionâŚ)
âŚ
[/quote]
Iâve tried your method and got the following result:
$ gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
$ LD_PRELOAD=./libsublime-imfix.so sublime-text
ERROR: ld.so: object './libsublime-imfix.so' from LD_PRELOAD cannot be preloaded: ignored.
$ uname -ai
Linux tonytonyjan-Latitude-6430U 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:31:23 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
Is there any message I can give you?
[quote=âcjackerâ]This is a dirty fix but at least works. cursor position update also supported.
Use LD_PRELOAD to reimplement gtk_im_context_set_client_window and set im focus in.
use âgdk_region_get_clipboxâ to catch the caret position. (Itâs really difficult to find which function can catch the positionâŚ)
[/code][/quote]
I tried this on Ubuntu 12.04, it works but only work for FCITX not ibus.
I sure get cursor location update, but there still is some problem (actually a big one) - backspace will erase the text before cursor, not character in IME.
So, I guess I just went back to ibus since it makes me feel better Anyway, great work.
Another question: is it possible to bring this cursor location update feature to Windows version of sublime?
IME works better on Windows version of sublime, the only problem is cursor location update. If you can fix that, it will be really great.
Thanks dude, that works perfect!
For Sublime Text 2, there was a nice plugin for supporting input method.
github.com/chikatoike/IMESuppor ⌠DME_en.org
Now that Sublime Text 3 has threaded plugin system, the plugin no longer works properly, at least in my environment.
Because the plugin hooks the input, there are some side effects like BracketHighlighter becomes unworkable.
Please support the input method correctly, especially with Sublime Text 3 for Windows.
I wasnât able to use my compose key at all on Arch Linux running KDE with xim in ST2, I tried scim but it didnât supposed custom compose keys from ~/.XCompose, but I was able to fix this for Linux by changing to scim-uim
To enable this for all GTK apps, put this into your ~/.xprofile after installing scim-uim, and then restart X:
GTK_IM_MODULE=uim-scim
Note that there are other ways to do this that make this work for all users on the computer and whatnot.
To run it just for ST2, or test it out without restarting, run this in your preferred console, set it as a bash alias, etc:
export GTK_IM_MODULE=uim-scim subl
Compose, and ~/.XCompose should now work.
Sublime text used to be my favorite. Now due to this problem Iâve had to switch editors. So upsettting.
Here is the update solution to combine whitequarkâs fix for XIM immodule.
My original codes only works with fcitx immodule.
Whitequarkâs codes only workds with xim immodule and without cursor position update support.
Here is the better one to support GTK_IM_MODULE=fcitx or GTK_IM_MODULE=xim and both support cursor position update.
For how to interpose(I am still too lasy to explain it:-D), maybe you can refer to whitequarkâs blog.
/*
sublime-imfix.c
Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
By Cjacker Huang <jianzhong.huang at i-soft.com.cn>
By whitequark@whitequark.org
How to compile:
gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
How to use:
LD_PRELOAD=./libsublime-imfix.so sublime_text
Changes:
2014 06-09
1, Fix cursor position update for sublime text 3.
2, Combine the codes from whitequark(fix for xim immodule) and add cursor update support for XIM immodule.
*/
/*for RTLD_NEXT*/
#define _GNU_SOURCE
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef VERBOSE
#define DEBUG(fmt, ...) do { \
FILE* err = fopen("/tmp/libsublime-immethod-fix.log", "a"); \
if (err) { \
fprintf(err, fmt, __VA_ARGS__); \
fclose(err); \
} \
} while(0)
#else
#define DEBUG(fmt, ...)
#endif
typedef GdkSegment GdkRegionBox;
struct _GdkRegion
{
long size;
long numRects;
GdkRegionBox *rects;
GdkRegionBox extents;
};
GtkIMContext *local_context;
//this func is interposed to support cursor position update.
void
gdk_region_get_clipbox (const GdkRegion *region,
GdkRectangle *rectangle)
{
g_return_if_fail (region != NULL);
g_return_if_fail (rectangle != NULL);
rectangle->x = region->extents.x1;
rectangle->y = region->extents.y1;
rectangle->width = region->extents.x2 - region->extents.x1;
rectangle->height = region->extents.y2 - region->extents.y1;
GdkRectangle rect;
rect.x = rectangle->x;
rect.y = rectangle->y;
rect.width = 0;
rect.height = rectangle->height;
//The caret width is 2 in sublime text 2
//And is 1 in sublime text 3.
//Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
if((rectangle->width == 2 || rectangle->width == 1) && GTK_IS_IM_CONTEXT(local_context)) {
gtk_im_context_set_cursor_location(local_context, rectangle);
}
}
//this is needed, for example, if you input something in file dialog and return back the edit area
//context will lost, so here we set it again.
static GdkFilterReturn event_filter (GdkXEvent *xevent, GdkEvent *event, gpointer im_context)
{
XEvent *xev = (XEvent *)xevent;
if(xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
GdkWindow * win = g_object_get_data(G_OBJECT(im_context),"window");
if(GDK_IS_WINDOW(win))
gtk_im_context_set_client_window(im_context, win);
}
return GDK_FILTER_CONTINUE;
}
void gtk_im_context_set_client_window (GtkIMContext *context,
GdkWindow *window)
{
GtkIMContextClass *klass;
g_return_if_fail (GTK_IS_IM_CONTEXT (context));
klass = GTK_IM_CONTEXT_GET_CLASS (context);
if (klass->set_client_window)
klass->set_client_window (context, window);
//below is our interposed codes to save the context to local_context.
if(!GDK_IS_WINDOW (window))
return;
g_object_set_data(G_OBJECT(context),"window",window);
int width = gdk_window_get_width(window);
int height = gdk_window_get_height(window);
if(width != 0 && height !=0) {
gtk_im_context_focus_in(context);
local_context = context;
}
//only add this event_filter when using 'fcitx' immodule.
//for xim immodule, this function is as same as original from gtk2.
const gchar * immodule = g_getenv("GTK_IM_MODULE");
if(immodule && !strcmp(immodule, "fcitx")) {
gdk_window_add_filter (window, event_filter, context);
}
}
/*below codes is from whitequark, fix for xim immodule*/
/* See gtkimcontextxim.c */
GType gtk_type_im_context_xim = 0;
#define GTK_TYPE_IM_CONTEXT_XIM (gtk_type_im_context_xim)
#define GTK_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIM))
#define GTK_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
#define GTK_IS_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_IM_CONTEXT_XIM))
#define GTK_IS_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_XIM))
#define GTK_IM_CONTEXT_XIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
typedef struct _GtkIMContextXIM GtkIMContextXIM;
typedef struct _GtkIMContextXIMClass GtkIMContextXIMClass;
struct _GtkIMContextXIMClass
{
GtkIMContextClass parent_class;
};
typedef struct _StatusWindow StatusWindow;
typedef struct _GtkXIMInfo GtkXIMInfo;
struct _GtkIMContextXIM
{
GtkIMContext object;
GtkXIMInfo *im_info;
gchar *locale;
gchar *mb_charset;
GdkWindow *client_window;
GtkWidget *client_widget;
/* The status window for this input context; we claim the
* * status window when we are focused and have created an XIC
* */
StatusWindow *status_window;
gint preedit_size;
gint preedit_length;
gunichar *preedit_chars;
XIMFeedback *feedbacks;
gint preedit_cursor;
XIMCallback preedit_start_callback;
XIMCallback preedit_done_callback;
XIMCallback preedit_draw_callback;
XIMCallback preedit_caret_callback;
XIMCallback status_start_callback;
XIMCallback status_done_callback;
XIMCallback status_draw_callback;
XIMCallback string_conversion_callback;
XIC ic;
guint filter_key_release : 1;
guint use_preedit : 1;
guint finalizing : 1;
guint in_toplevel : 1;
guint has_focus : 1;
};
static GClassInitFunc orig_gtk_im_context_xim_class_init;
static GType (*orig_g_type_module_register_type)(GTypeModule *,
GType, const gchar *,
const GTypeInfo *, GTypeFlags);
static gboolean (*orig_gtk_im_context_xim_filter_keypress)(GtkIMContext *context,
GdkEventKey *event);
static gboolean
hook_gtk_im_context_xim_filter_keypress(GtkIMContext *context, GdkEventKey *event) {
GtkIMContextXIM *im_context_xim = GTK_IM_CONTEXT_XIM(context);
if (!im_context_xim->client_window) {
DEBUG("im_context_xim == %p\n", im_context_xim);
DEBUG("event->window == %p\n", event->window);
gtk_im_context_set_client_window(context, event->window);
}
return orig_gtk_im_context_xim_filter_keypress(context, event);
}
static void
hook_gtk_im_context_xim_class_init (GtkIMContextXIMClass *class) {
orig_gtk_im_context_xim_class_init(class, NULL); /* wat? */
GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
assert(!orig_gtk_im_context_xim_filter_keypress);
orig_gtk_im_context_xim_filter_keypress = im_context_class->filter_keypress;
im_context_class->filter_keypress = hook_gtk_im_context_xim_filter_keypress;
DEBUG("orig_gtk_im_context_xim_filter_keypress: %p\n",
orig_gtk_im_context_xim_filter_keypress);
}
GType
g_type_module_register_type (GTypeModule *module,
GType parent_type,
const gchar *type_name,
const GTypeInfo *type_info,
GTypeFlags flags) {
if (!orig_g_type_module_register_type) {
orig_g_type_module_register_type = dlsym(RTLD_NEXT, "g_type_module_register_type");
assert(orig_g_type_module_register_type);
}
if (type_name && !strcmp(type_name, "GtkIMContextXIM")) {
assert(!orig_gtk_im_context_xim_class_init);
orig_gtk_im_context_xim_class_init = type_info->class_init;
assert(sizeof(GtkIMContextXIM) == type_info->instance_size);
const GTypeInfo hook_im_context_xim_info =
{
type_info->class_size,
type_info->base_init,
type_info->base_finalize,
(GClassInitFunc) hook_gtk_im_context_xim_class_init,
type_info->class_finalize,
type_info->class_data,
type_info->instance_size,
type_info->n_preallocs,
type_info->instance_init,
};
DEBUG("orig_gtk_im_context_xim_class_init: %p\n", orig_gtk_im_context_xim_class_init);
gtk_type_im_context_xim =
orig_g_type_module_register_type(module, parent_type, type_name,
&hook_im_context_xim_info, flags);
return gtk_type_im_context_xim;
}
return orig_g_type_module_register_type(module, parent_type, type_name, type_info, flags);
}