Guest User

Untitled

a guest
Feb 24th, 2018
307
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.82 KB | None | 0 0
  1. /**
  2. * sublime-imfix.c
  3. * Use LD_PRELOAD to interpose some function to fix sublime input method support for linux.
  4. * By Cjacker Huang <jianzhong.huang at i-soft.com.cn>
  5. * By whitequark@whitequark.org
  6. *
  7. * How to compile:
  8. * gcc -shared -o libsublime-imfix.so sublime_imfix.c `pkg-config --libs --cflags gtk+-2.0` -fPIC
  9. * How to use:
  10. * LD_PRELOAD=./libsublime-imfix.so sublime_text
  11. *
  12. * Changes:
  13. * 2014 06-09
  14. * 1. Fix cursor position update for sublime text 3.
  15. * 2. Combine the codes from whitequark(fix for xim immodule) and add cursor update support for XIM immodule.
  16. */
  17.  
  18. /* for RTLD_NEXT */
  19. #define _GNU_SOURCE
  20.  
  21. #include <assert.h>
  22. #include <dlfcn.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26.  
  27. #include <gtk/gtk.h>
  28. #include <gdk/gdkx.h>
  29. #include <X11/Xlib.h>
  30. #include <X11/Xutil.h>
  31.  
  32. #ifdef VERBOSE
  33. #define DEBUG(fmt, ...) do { \
  34. FILE* err = fopen("/tmp/libsublime-immethod-fix.log", "a"); \
  35. if (err) { \
  36. fprintf(err, fmt, __VA_ARGS__); \
  37. fclose(err); \
  38. } \
  39. } while(0)
  40. #else
  41. #define DEBUG(fmt, ...)
  42. #endif
  43.  
  44. typedef GdkSegment GdkRegionBox;
  45.  
  46. struct _GdkRegion {
  47. long size;
  48. long numRects;
  49. GdkRegionBox *rects;
  50. GdkRegionBox extents;
  51. };
  52.  
  53. GtkIMContext *local_context;
  54.  
  55. // This function is added to prevent some bugs caused by subprocess inheriting `LD_PRELOAD` settings
  56. // For example, without it, `Preferences - Browse Packages` function won't work.
  57. void __attribute__((constructor)) on_load(void) {
  58. // Clear `LD_PRELOAD` environment variable
  59. putenv("LD_PRELOAD=");
  60. }
  61.  
  62. // this func is interposed to support cursor position update.
  63. void
  64. gdk_region_get_clipbox(const GdkRegion *region,
  65. GdkRectangle *rectangle) {
  66. g_return_if_fail(region != NULL);
  67. g_return_if_fail(rectangle != NULL);
  68.  
  69. rectangle->x = region->extents.x1;
  70. rectangle->y = region->extents.y1;
  71. rectangle->width = region->extents.x2 - region->extents.x1;
  72. rectangle->height = region->extents.y2 - region->extents.y1;
  73. GdkRectangle rect;
  74. rect.x = rectangle->x;
  75. rect.y = rectangle->y;
  76. rect.width = 0;
  77. rect.height = rectangle->height;
  78. // The caret width is 2 in sublime text 2
  79. // And is 1 in sublime text 3.
  80. // Maybe sometimes we will make a mistake, but for most of the time, it should be the caret.
  81. if ((rectangle->width == 2 || rectangle->width == 1) && GTK_IS_IM_CONTEXT(local_context)) {
  82. gtk_im_context_set_cursor_location(local_context, rectangle);
  83. }
  84. }
  85.  
  86. // this is needed, for example, if you input something in file dialog and return back the edit area
  87. // context will lost, so here we set it again.
  88. static GdkFilterReturn event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer im_context) {
  89. XEvent *xev = (XEvent *)xevent;
  90. if (xev->type == KeyRelease && GTK_IS_IM_CONTEXT(im_context)) {
  91. GdkWindow *win = g_object_get_data(G_OBJECT(im_context), "window");
  92. if (GDK_IS_WINDOW(win)) {
  93. gtk_im_context_set_client_window(im_context, win);
  94. }
  95. }
  96. return GDK_FILTER_CONTINUE;
  97. }
  98.  
  99. void gtk_im_context_set_client_window(GtkIMContext *context,
  100. GdkWindow *window) {
  101. GtkIMContextClass *klass;
  102. g_return_if_fail(GTK_IS_IM_CONTEXT(context));
  103. klass = GTK_IM_CONTEXT_GET_CLASS(context);
  104. if (klass->set_client_window) {
  105. klass->set_client_window(context, window);
  106. }
  107.  
  108. //below is our interposed codes to save the context to local_context.
  109. if (!GDK_IS_WINDOW(window)) {
  110. return;
  111. }
  112. g_object_set_data(G_OBJECT(context), "window", window);
  113. int width = gdk_window_get_width(window);
  114. int height = gdk_window_get_height(window);
  115. if (width != 0 && height != 0) {
  116. gtk_im_context_focus_in(context);
  117. local_context = context;
  118. }
  119. //only add this event_filter when using 'fcitx' immodule.
  120. //for xim immodule, this function is as same as original from gtk2.
  121. const gchar *immodule = g_getenv("GTK_IM_MODULE");
  122. if (immodule && !strcmp(immodule, "fcitx")) {
  123. gdk_window_add_filter(window, event_filter, context);
  124. }
  125. }
  126.  
  127.  
  128. /* The following codes are from whitequark, fix for xim immodule */
  129.  
  130. /* See gtkimcontextxim.c */
  131. GType gtk_type_im_context_xim = 0;
  132.  
  133. #define GTK_TYPE_IM_CONTEXT_XIM (gtk_type_im_context_xim)
  134. #define GTK_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIM))
  135. #define GTK_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
  136. #define GTK_IS_IM_CONTEXT_XIM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_IM_CONTEXT_XIM))
  137. #define GTK_IS_IM_CONTEXT_XIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_XIM))
  138. #define GTK_IM_CONTEXT_XIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_XIM, GtkIMContextXIMClass))
  139.  
  140. typedef struct _GtkIMContextXIM GtkIMContextXIM;
  141. typedef struct _GtkIMContextXIMClass GtkIMContextXIMClass;
  142.  
  143. struct _GtkIMContextXIMClass {
  144. GtkIMContextClass parent_class;
  145. };
  146.  
  147. typedef struct _StatusWindow StatusWindow;
  148. typedef struct _GtkXIMInfo GtkXIMInfo;
  149.  
  150. struct _GtkIMContextXIM {
  151. GtkIMContext object;
  152.  
  153. GtkXIMInfo *im_info;
  154.  
  155. gchar *locale;
  156. gchar *mb_charset;
  157.  
  158. GdkWindow *client_window;
  159. GtkWidget *client_widget;
  160.  
  161. /* The status window for this input context; we claim the
  162. * status window when we are focused and have created an XIC
  163. */
  164. StatusWindow *status_window;
  165.  
  166. gint preedit_size;
  167. gint preedit_length;
  168. gunichar *preedit_chars;
  169. XIMFeedback *feedbacks;
  170.  
  171. gint preedit_cursor;
  172.  
  173. XIMCallback preedit_start_callback;
  174. XIMCallback preedit_done_callback;
  175. XIMCallback preedit_draw_callback;
  176. XIMCallback preedit_caret_callback;
  177.  
  178. XIMCallback status_start_callback;
  179. XIMCallback status_done_callback;
  180. XIMCallback status_draw_callback;
  181.  
  182. XIMCallback string_conversion_callback;
  183.  
  184. XIC ic;
  185.  
  186. guint filter_key_release : 1;
  187. guint use_preedit : 1;
  188. guint finalizing : 1;
  189. guint in_toplevel : 1;
  190. guint has_focus : 1;
  191. };
  192.  
  193. static GClassInitFunc orig_gtk_im_context_xim_class_init;
  194. static GType(*orig_g_type_module_register_type)(GTypeModule *,
  195. GType, const gchar *,
  196. const GTypeInfo *, GTypeFlags);
  197. static gboolean(*orig_gtk_im_context_xim_filter_keypress)(GtkIMContext *context,
  198. GdkEventKey *event);
  199.  
  200. static gboolean
  201. hook_gtk_im_context_xim_filter_keypress(GtkIMContext *context, GdkEventKey *event) {
  202. GtkIMContextXIM *im_context_xim = GTK_IM_CONTEXT_XIM(context);
  203. if (!im_context_xim->client_window) {
  204. DEBUG("im_context_xim == %p\n", im_context_xim);
  205. DEBUG("event->window == %p\n", event->window);
  206.  
  207. gtk_im_context_set_client_window(context, event->window);
  208. }
  209.  
  210. return orig_gtk_im_context_xim_filter_keypress(context, event);
  211. }
  212.  
  213. static void
  214. hook_gtk_im_context_xim_class_init(GtkIMContextXIMClass *class) {
  215. orig_gtk_im_context_xim_class_init(class, NULL); /* wat? */
  216.  
  217. GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS(class);
  218.  
  219. assert(!orig_gtk_im_context_xim_filter_keypress);
  220. orig_gtk_im_context_xim_filter_keypress = im_context_class->filter_keypress;
  221. im_context_class->filter_keypress = hook_gtk_im_context_xim_filter_keypress;
  222. DEBUG("orig_gtk_im_context_xim_filter_keypress: %p\n",
  223. orig_gtk_im_context_xim_filter_keypress);
  224. }
  225.  
  226. GType
  227. g_type_module_register_type(GTypeModule *module,
  228. GType parent_type,
  229. const gchar *type_name,
  230. const GTypeInfo *type_info,
  231. GTypeFlags flags) {
  232. if (!orig_g_type_module_register_type) {
  233. orig_g_type_module_register_type = dlsym(RTLD_NEXT, "g_type_module_register_type");
  234. assert(orig_g_type_module_register_type);
  235. }
  236.  
  237. if (type_name && !strcmp(type_name, "GtkIMContextXIM")) {
  238. assert(!orig_gtk_im_context_xim_class_init);
  239. orig_gtk_im_context_xim_class_init = type_info->class_init;
  240.  
  241. assert(sizeof(GtkIMContextXIM) == type_info->instance_size);
  242.  
  243. const GTypeInfo hook_im_context_xim_info = {
  244. type_info->class_size,
  245. type_info->base_init,
  246. type_info->base_finalize,
  247. (GClassInitFunc) hook_gtk_im_context_xim_class_init,
  248. type_info->class_finalize,
  249. type_info->class_data,
  250. type_info->instance_size,
  251. type_info->n_preallocs,
  252. type_info->instance_init,
  253. };
  254.  
  255. DEBUG("orig_gtk_im_context_xim_class_init: %p\n", orig_gtk_im_context_xim_class_init);
  256.  
  257. gtk_type_im_context_xim =
  258. orig_g_type_module_register_type(module, parent_type, type_name,
  259. &hook_im_context_xim_info, flags);
  260.  
  261. return gtk_type_im_context_xim;
  262. }
  263.  
  264. return orig_g_type_module_register_type(module, parent_type, type_name, type_info, flags);
  265. }
Add Comment
Please, Sign In to add comment